我们在讲解Socket编程前,先看几个和Socket编程紧密相关的概念:


1. TCP/IP层次模型

当然这里我们只讨论重要的四层

01,应用层(Application):应用层是个很广泛的概念,有一些基本相同的系统级TCP/IP应用以及应用协议,也有许多的企业应用和互联网应用。http协议在应用层运行。

02,传输层(Tanspot):传输层包括UDP和TCP,UDP几乎不对报文进行检查,而TCP提供传输保证。

03,网络层(Netwok):网络层协议由一系列协议组成,包括ICMP、IGMP、RIP、OSPF、IP(v4,v6)等。

04,链路层(Link):又称为物理数据网络接口层,负责报文传输。

然后我们来看下tcp层次模型图

从上图中可以看出,应用程序在应用层运行,在传输层,在数据前加上了TCP头,在网络层加上的IP头,在数据链路层加上了帧。


2. 端口

端口号范围:0-65535,总共能表示65536个数。

按端口号可分为3大类

01,公认端口(WellKnownPorts):从0到1023,它们紧密绑定(binding)于一些服务。通常这些端口的通讯明确表明了某种服务的协议。例如:80端口实际上总是HTTP通讯。

02,注册端口(RegisteredPorts):从1024到49151。它们松散地绑定于一些服务。也就是说有许多服务绑定于这些端口,这些端口同样用于许多其它目的。例如:许多系统处理动态端口从1024左右开始。

03,动态和/或私有端口(Dynamicand/orPrivatePorts):从49152到65535。理论上,不应为服务分配这些端口。实际上,机器通常从1024起分配动态端口。


3. TCP和UDP报文

下面一起来看下TCP和UDP的报文图

从图中我们可以看出TCP和UDP中都有校验和,但是在UDP报文中,一般不使用校验和,这样可以加快数据传输的速度,但是数据的准确性可能会受到影响。换句话说,Tcp协议都有校验和,为了保证传输数据的准确性。


4. Socket

Socket包括ip地址和端口号两部分,程序通过Socket来通信,Socket相当于操作系统的一个组件。Socket作为进程之间通信机制,通常也称作”套接字”,用于描述IP地址和端口号,是一个通信链的句柄。说白了,就是两个程序通信用的。

生活案例对比:

Socket之间的通信可以类比生活中打电话的案例。任何用户在通话之前,首先要占有一部电话机,相当于申请一个Socket,同时要知道对方的号码,相当于对方有一个固定的Socket,然后向对方拨号呼叫,相当于发出连接请求。假如对方在场并空闲,拿起 电话话筒,双方就可以进行通话了。双方的通话过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。通话结束后,一方挂起电话机,相当于关闭socket,撤销连接。

注意:Socket不仅可以在两台电脑之间通信,还可以在同一台电脑上的两个程序间通信。


5. 端口进阶(深入)

通过IP地址确定了网络中的一台电脑后,该电脑上可能提供很多提供服务的应用,每一个应用都对应一个端口。

在Internet上有很多这样的主机,这些主机一般运行了多个服务软件 ,同时提供几种服务,每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务(应用程序)

例如:http 使用80端口,   ftp使用21端口     smtp使用25端口


6. Socket分类

Socket主要有两种类型:

01,流式Socket:              是一种面向连接的Socket,针对于面向连接的TCP服务应用,安全,但是效率低

02,数据报式Socket:      是一种无连接的Socket,对应于无连接的UDP服务应用,不安全,但效率高


7. Socket一般应用模式(服务器端和客户端)

服务器端的Socket(至少需要两个)

01. 一个负责接收客户端连接请求(但不负责与客户端通信)

02. 每成功接收到客户端的连接便在服务器端产生一个对应的复杂通信的Socket

021. 在接收到客户端连接时创建

022. 为每个连接成功的客户端请求在服务器端都创建一个对应的Socket(负责和客户端通信)

客户端的Socket

01. 必须指定要连接的服务器地址和端口

02. 通过创建一个Socket对象来初始化一个到服务器端的TCP连接

通过上图,我们可以看出,首先服务器会创建一个负责监听的socket,然后客户端通过socket连接到服务器指定端口,最后服务器端负责监听的socket,监听到客户端有连接过来了,就创建一个负责和客户端通信的socket。


8. Socket通信过程(服务器端和客户端)

下面我们来看下Socket更具体的通信过程:

Socket的通讯过程

服务器端:

01,申请一个socket

02,绑定到一个IP地址和一个端口上

03,开启侦听,等待接收连接

客户端:

01,申请一个socket

02,连接服务器(指明IP地址和端口号)

服务器端接收到连接请求后,产生一个新的socket(端口大于1024)与客户端建立连接并进行通信,原监听socket继续监听。

注意:负责通信的Socket不能无限创建,创建的数量和操作系统有关。


9. Socket的构造函数

Public Socket(AddressFamily addressFamily,SocketType  socketType,ProtocolType  protocolTYpe)

AddressFamily:指定Socket用来解析地址的寻址方案。例如:InterNetWork指示当Socket使用一个IP版本4地址连接

SocketType:定义要打开的Socket的类型

Socket类使用ProtocolType枚举向Windows  Sockets  API通知所请求的协议

C#中又多了一层的封装:TCPClient和TCPListener两个类又做了一层封装。

注意:

1,端口号必须在 1 和 65535之间,最好在1024以后。

2,要连接的远程主机必须正在监听指定端口,也就是说你无法随意连接远程主机。如:

IPAddress addr = IPAddress.Parse("127.0.0.1");

IPEndPoint endp = new IPEndPoint(addr, 9000);

服务端先绑定:serverWelcomeSocket.Bind(endp)

客户端再连接:clientSocket.Connect(endp)

3,一个Socket一次只能连接一台主机

4,Socket关闭后无法再次使用

5,每个Socket对象只能与一台远程主机连接。如果你想连接到多台远程主机,你必须创建多个Socket对象。


8. Socket常用类和方法

相关类:

IPAddress:包含了一个IP地址

IPEndPoint:包含了一对IP地址和端口号

方法:

Socket():创建一个Socket

Bind():绑定一个本地的IP和端口号(IPEndPoint)

Listen():让Socket侦听传入的连接,并指定侦听队列容量

Connect():初始化与另一个Socket的连接

Accept():接收连接并返回一个新的Socket

Send():输出数据到Socket

Receive():从Socket中读取数据

Close():关闭Socket,销毁连接

9. 编程实现

用户界面:

代码实现:

服务器

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms; using System.Net;
using System.Net.Sockets;
using System.Threading; namespace WindowsFormsApplication5
{
public partial class Form1 : Form
{
//记录通信用的Socket
Dictionary<string, Socket> dic = new Dictionary<string, Socket>(); public Form1()
{
InitializeComponent();
} private void btnListen_Click(object sender, EventArgs e)
{
//IP地址,如果是自动获取的为
//IPAddress ip = IPAddress.Any;
IPAddress ip = IPAddress.Parse(txtIP.Text);
//建立一个通信节点:IP地址+port端口号
IPEndPoint point = new IPEndPoint(ip, int.Parse(txtPort.Text));
//创建监听用的Socket
/*
* AddressFamily.InterNetWork:使用IPv4地址
* SocketType.Stream:支持可靠、双向、基于连接的字节流,而不重复数据。此类型的
* Socket与单个对方主机进行通信,并且在通信开始之前需要远程
* 主机连接。Stream使用传输控制协议(TCP)ProtocolType和
* InterNetworkAddressFamily。
* ProtocolType.Tcp:使用传输控制协议
*
*/
//使用IPv4地址、流式Socket方式、TCP协议传输数据
Socket ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//创建好Socket后,必须告诉Socket绑定的IP地址和端口号
//让Socket监听主机point try
{
//监听Socket要绑定在哪个端点
ServerSocket.Bind(point);
//监听队列的长度为10
ServerSocket.Listen();
ShowMsg("服务器开始监听"); //启动一个线程接收client的链接信息
Thread thread = new Thread(AcceptInfo);
thread.IsBackground = true;
thread.Start(ServerSocket);
}
catch(Exception ex)
{
ShowMsg(ex.Message);
} }//btnListen_Click() //在聊天记录框中显示信息
void ShowMsg(string Msg)
{
txtLog.AppendText(Msg + "\r\n");
}
/// <summary>
/// 接收client的连接请求,存储连接记录
/// </summary>
/// <param name="o">负责监听的ServerSocket</param>
void AcceptInfo(object o)
{
//把主调函数的ServerSocket接收一下
Socket socket = o as Socket; while (true)
{
//通信用Socket,这个线程负责一直循环,接收client的请求
try
{
//创建通用的Socket
Socket tSocket = socket.Accept(); string point = tSocket.RemoteEndPoint.ToString(); Console.WriteLine(point);
ShowMsg(point + "连接成功");
cboIpPort.Items.Add(point);
dic.Add(point, tSocket); //创建一个线程,接收数据信息
Thread th = new Thread(ReceiveMsg);
th.IsBackground = true;
th.Start(tSocket);
}//try
catch(Exception ex)
{
ShowMsg(ex.Message);
break;
} }//while
}//AcceptInfo() /// <summary>
/// 接收client发送的数据
/// </summary>
/// <param name="o">负责连接的普通Socket</param>
void ReceiveMsg(object o)
{
Socket clientSocket = o as Socket; while(true)
{
try
{
//定义byte数组存放从客户端接收过来的数据
byte[] buffer = new byte[];
//将接收过来的数据放到buffer中,并返回实际接收的数据的长度
int n = clientSocket.Receive(buffer); //将字节数组转换成字符串
string words = Encoding.UTF8.GetString(buffer, , n);
ShowMsg(clientSocket.RemoteEndPoint.ToString() + ":" + words);
}
catch(Exception ex)
{
ShowMsg(ex.Message);
break;
} }//while }//ReceiveMsg /// <summary>
/// 发送数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click(object sender, EventArgs e)
{
try
{
ShowMsg(txtMsg.Text);
string ip = cboIpPort.Text;
byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);
dic[ip].Send(buffer);//clientSocket.Send(buffer);
txtMsg.Text = "";
}
catch(Exception ex)
{
ShowMsg(ex.Message);
}
}//btnSend_Click() private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
} }//class
}

客户端

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms; using System.Net;
using System.Net.Sockets;
using System.Threading; namespace WindowsFormsApplication6
{
public partial class Form1 : Form
{
//建立通信用的Socket
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); public Form1()
{
InitializeComponent();
} private void btnConnect_Click(object sender, EventArgs e)
{
//连接到目的IP, IPAddress ip = IPAddress.Any;用于自动获取IP地址
IPAddress ip = IPAddress.Parse(txtIP.Text); //连接到目标ip地址的哪个应用(端口号!)
IPEndPoint point = new IPEndPoint(ip, int.Parse(txtPort.Text)); try
{
//连接到服务器
clientSocket.Connect(point);
ShowMsg("连接成功");
ShowMsg("服务器" + clientSocket.RemoteEndPoint.ToString());
ShowMsg("客户端" + clientSocket.LocalEndPoint.ToString()); //连接成功后,就可以接收服务器的发送信息了
Thread th = new Thread(ReceiveMsg);
th.IsBackground = true;
th.Start();
}
catch (Exception ex)
{
ShowMsg(ex.Message);
} }//btnConnect_Click() /// <summary>
/// 接收服务器信息
/// </summary>
void ReceiveMsg()
{
while (true)
{
try
{
byte[] buffer = new byte[];
int n = clientSocket.Receive(buffer); //Encoding这个类里有byte数组和字符串转换的函数
string str = Encoding.UTF8.GetString(buffer, , n);
ShowMsg(clientSocket.RemoteEndPoint.ToString() + ": " + str);
}
catch (Exception ex)
{
ShowMsg(ex.Message);
}
}//while
}//ReceiveMsg() /// <summary>
/// 显示信息
/// </summary>
/// <param name="Msg"></param>
void ShowMsg(string Msg)
{
txtLog.AppendText(Msg + "\r\n");
} private void btnSend_Click(object sender, EventArgs e)
{
if(clientSocket != null)
{
try
{
//把发送窗口的数据显示到记录窗口
ShowMsg(txtMsg.Text); //字符数组
byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);
clientSocket.Send(buffer);
txtMsg.Text = "";
}
catch(Exception ex)
{
ShowMsg(ex.Message);
}
}
}//btnSend_Click() private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
}
}

注意:Control.CheckForIllegalCrossThreadCalls = false;这句话要在formLoad事件中执行,不然会出现运行错误:线程间操作无效: 从不是创建控件“”的线程访问它。

因为线程操作了一个UI控件,但是这个UI控件不是该线程创建的,就会出错。

还有三个知识点:

Dictionary<TKey, TValue> 类表示键和值的集合。
命名空间:System.Collections.Generic
Dictionary<string, string>是一个泛型 他本身有集合的功能有时候可以把它看成数组 他的结构是这样的:Dictionary<[key], [value]> 他的特点是存入对象是需要与[key]值一一对应的存入该泛型 通过某一个一定的[key]去找到对应的值 举个例子: //实例化对象 Dictionary<int, string> dic = new Dictionary<int, string>(); //对象打点添加 dic.Add(, "one"); dic.Add(, "two"); dic.Add(, "one"); //提取元素的方法 string a = dic[]; string b = dic[]; string c = dic[]; //1、2、3是键,分别对应“one”“two”“one” //上面代码中分别把值赋给了a,b,c //注意,键相当于找到对应值的唯一标识,所以不能重复 //但是值可以重复

Socket的一个属性RemoteEndPoint和LocalEndPoint:源代码中直接使用的是RemoteEndPoint.ToString()返回的格式是IP:Port。服务器字典里的key就是IP:Port形式的字符串。

Socket.RemoteEndPoint 属性,他是Socket类的一个属性。
定义为:public EndPoint RemoteEndPoint { get; } 如果您使用的是面向连接的协议,则 RemoteEndPoint 属性将获取包含 Socket 连接到的远程 IP 地址和端口号的 EndPoint。
而如果当前使用的是无连接的协议,则 RemoteEndPoint 包含将要和 Socket 通信的默认远程 IP 地址和端口号。
您必须将此 EndPoint 强制转换为 IPEndPoint 才能检索信息。 然后就可以调用 IPEndPoint.Address 方法来检索远程 IPAddress,调用 IPEndPoint.Port 方法来检索远程端口号。 s.Connect (lep);
// Using the RemoteEndPoint property.
Console.WriteLine ("I am connected to " + IPAddress.Parse (((IPEndPoint)s.RemoteEndPoint).Address.ToString ()) + "on port number " + ((IPEndPoint)s.RemoteEndPoint).Port.ToString ());
// Using the LocalEndPoint property.
Console.WriteLine ("My local IpAddress is :" + IPAddress.Parse (((IPEndPoint)s.LocalEndPoint).Address.ToString ()) + "I am connected on port number " + ((IPEndPoint)s.LocalEndPoint).Port.ToString ());

字符数组和字符串之间的转换:

Encoding.UTF8.GetString()
Encoding.UTF8.GetBytes()

[非技术参考]C# Socket网络编程的更多相关文章

  1. Linux Socket 网络编程

    Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后 ...

  2. Python之路【第七篇】python基础 之socket网络编程

    本篇文章大部分借鉴 http://www.cnblogs.com/nulige/p/6235531.html python socket  网络编程 一.服务端和客户端 BS架构 (腾讯通软件:ser ...

  3. windows下的socket网络编程

    windows下的socket网络编程 windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了, ...

  4. windows下的socket网络编程(入门级)

    windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先 ...

  5. SOCKET网络编程细节问题3

    SOCKET网络编程快速上手(二)——细节问题(3) 3.SIGPIPE问题 人怕牺牲,我们写的程序也一样,人有死不瞑目,程序又何尝不是?程序跑着跑着,突然就崩掉了.好一点的牺牲前告诉你些打印,差点的 ...

  6. Python面向对象进阶和socket网络编程-day08

    写在前面 上课第八天,打卡: 为什么坚持?想一想当初: 一.面向对象进阶 - 1.反射补充 - 通过字符串去操作一个对象的属性,称之为反射: - 示例1: class Chinese: def __i ...

  7. Socket网络编程--小小网盘程序(5)

    各位好呀!这一小节应该就是这个小小网盘程序的最后一小节了,这一节将实现最后的三个功能,即列出用户在服务器中的文件列表,还有删除用户在服务器中的文件,最后的可以共享文件给好友. 列出用户在服务器中的文件 ...

  8. Java Web 基础(一) 基于TCP的Socket网络编程

    一.Socket简单介绍 Socket通信作为Java网络通讯的基础内容,集中了异常.I/O流模式等众多知识点.学习Socket通信,既能够了解真正的网络通讯原理,也能够增强对I/O流模式的理解. 1 ...

  9. 5.3linux下C语言socket网络编程简例

    原创文章,转载请注明转载字样和出处,谢谢! 这里给出在Linux下的简单socket网络编程的实例,使用tcp协议进行通信,服务端进行监听,在收到客户端的连接后,发送数据给客户端:客户端在接受到数据后 ...

随机推荐

  1. SQL Server:SQL Like 通配符特殊用法:Escape 【转】

    SQL中escape的主要用途 1.使用   ESCAPE   关键字定义转义符.在模式中,当转义符置于通配符之前时,该通配符就解释为普通字符.例如,要搜索在任意位置包含字符串   5%   的字符串 ...

  2. 佩特来项目经验小集合(2)___组合查询存储过程,报错 &quot;varchar JBID=&#39;&#39; 转换成数据类型 int 时失败&quot;

       今天写一个组合查询的存储过程遇到这样一个问题:在将 varchar 值 'SELECT * FROM View_DLS_WXJD_Customer WHERE 1=1 and JBID ='' ...

  3. Linux下实现视频读取(二)---camera參数设定

    Camera的可设置项极多,V4L2 支持了不少.但Sam之前对这些设置的使用方法和涵义都是在看videodev2.h中边看边理解.感觉很生涩. 直到写这篇blog时,才发现v4l2有专门的SPEC来 ...

  4. Linux下装Eclipse C/C++,以及环境配置

    由于前些日子朋友搞个智能家居开发,用C语言写的.叫我装个CentOS(Linux中的一种)来进行开发,所以这几天都在摸索怎么装,当然,朋友也有给予一丁点帮助(可恶的色长.你叫我装东西,也不帮帮我),由 ...

  5. 设置IE兼容模式

    文件兼容性用于定义让IE如何编译你的网页.此文件解释文件兼容性,如何指定你网站的文件兼容性模式以及如何判断一个网页该使用的文件模式. 前言 为了帮助确保你的网页在所有未来的IE版本都有一致的外观,IE ...

  6. TCP/IP详解之:Ping程序、Traceroute程序

    Ping程序: ping程序是通过发送一份ICMP回显请求报文(即ICMP报文的一种,其类型为8,代码为0)给主机,并等待返回ICMP回显应答 来测试另一台主机是否可达. ping程序不用经过传输层, ...

  7. OpenCV系列--摄像头控制的简单代码

    操作系统:windows xp 开发工具:VS2008 opencv版本:2.1.0 依赖库:OpenCV2.1\lib\highgui.lib #include "cv.h" # ...

  8. 实现php获取mp3文件元信息如播放时间歌曲作者等

    最近收集到一个php获取mp3文件元信息的类,感觉比较方便.现在分享给大家! 下面是使用方式和测试方式: <?php include_once 'mp3file.class.php'; func ...

  9. JavaScript中创建命名空间

    引用:http://ourjs.com/detail/538d8d024929582e6200000c   在JavaScript中全局变量经常会引起命名冲突,甚至有时侯重写变量也不是按照你想像中的顺 ...

  10. Linux SSH 互信

    第一步: 创建用于身份认证的两个密钥文件 ssh-keygen #注明.想省事的话打完这个命令后一直回车就行了. 第二步: 把公钥上传到目标主机上去 ssh-copy-id -i id_rsa.pub ...