一个连接由它的两个端点标识,这样的端点称为套接
套接字是支持TCP/IP协议的网络通信的基本操作单元。
可以将套接字看作不同主机间的进程进行双向通信的端点。
上图连接1的一对套接字为: (192.168.2.23,5000)和(192.168.2.122,8888)
上图连接2的一对套接字为: (192.168.2.23,5001)和(192.168.2.122,8888)
对于UDP协议尽管两个进程之间没有建立连接,但是也同样存在发送端点,和接收端点,也同样使用套接字的概念。
套接字的类型有:
流式套接字:提供了面向连接的、可靠的、数据无错并且无重复的数据发送服务,而且接收数据的顺序和发送数据的顺序是相同的。
数据报套接字:提供了面向无连接的服务,它以独立的数据包形式发送数据(数据包长度不能大于32KB),不提供正确性检查,也不保证各数据包的发送顺序和接收顺序相同 ,因
此,可能出现数据的重发、丢失等现象。
原始套接字:用于直接访问协议的较低层。常用于检验新的协议实现或访问现有服务中配置的新设备,一般不提倡直接使用原始套接字。
Socket类包含在System.Net.Sockets命名空间中。
一个Socket实例包含了一个本地或者一个远程端点的套接字信息。 使用Socket类编程,由于很多细节都需要自己考虑,相对来说复杂一些,易出错。
一般对套接字编程比较熟悉的人,或者使用非标准协议(自定义的新协议)进行编程的时候,才使用Socket类。
Socket类的构造函数为:
public Socket(
AddressFamily addressFamily, //网络类型
SocketType socketType, //套接字类型
ProtocolType protocolType); //使用的协议
参数含义:
(1)addressFamily addressFamily表示网络类型,该参数使用AddressFamily枚举指定Socket使用的寻址方案 例如AddressFamily.InterNetwork表示IP版本4的地址。
(2)socketType socketTyp指定Socket的类型,该参数使用SocketType枚举指定使用哪种套接字。
例如:
SocketType.Stream表明连接是基于流套接字的
SocketType.Dgram表示连接是基于数据报套接字
SocketType.Raw表示连接基于原始套接字;
(3)protocolType protocolType指定Socket使用的协议,该参数使用ProtocolType枚举指定使用哪种协议。
例如:
ProtocolType.Tcp表明连接协议是TCP
ProtocolType.Udp表明连接协议是UDP
SocketType |
ProtocolType |
说明 |
Dgram |
Udp |
无连接通信 |
Stream |
Tcp |
面向连接的通信 |
Raw |
Icmp |
Internet控制报文协议 |
Raw |
Raw |
简单IP包通信 |
IP连接领域有两种通信类型:
面向连接的(connection-oriented)
无连接的(connectionless)。
根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:
服务器监听 客户端请求 连接确认
服务器监听:是指服务器套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器套接字
的地址和端口号,然后再向服务器套接字提出连接请求。
连接确认:是指当服务器套接字监听到客户端套接字的连接请求时,它就响应客户端套接字的请求,把服务器套接字的信息发给客户端,一旦客户端确认了此信息,连接即
可建立。而服务器套接字继续监听其他客户端套接字的连接请求。
同步TCP编写服务器端程序的一般步骤为:
(1) 创建一个包含采用的网络类型、数据传输类型和协议类型的本地套接字对象,并将其与服务器的IP地址和端口号绑定。这个过程可以通过Socket类。
(2) 在指定的端口进行监听,以便接受客户端连接请求。
(3) 一旦接受了客户端的连接请求,就根据客户端发送的连接信息创建与该客户端对应的Socket对象。
(4) 根据创建的Socket对象,分别与每个连接的客户进行数据传输。
(5) 根据传送信息情况确定是否关闭与对方的连接。
使用同步TCP编写客户端程序的一般步骤为:
(1) 创建一个包含传输过程中采用的网络类型、数据传输类型和协议类型的Socket对象。
(2) 与远程服务器建立连接。
(3) 与服务器进行数据传输。
(4) 完成工作后,向服务器发送关闭信息,并关闭与服务器的连接。
1.(服务器)
// 1、创建IPEndPoint实例,用于Socket侦听时绑定 IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 6001); // 2、创建套接字实例 Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //3、将所创建的套接字与IPEndPoint绑定 serverSocket.Bind(ipep); //4、设置套接字为收听模式 serverSocket.Listen(10); while (true) { try { // 5、在套接字上接收接入的连接 clientSocket = serverSocket.Accept(); clientThread = new Thread(new ThreadStart(ReceiveData)); clientThread.Start(); } catch (Exception ex) { MessageBox.Show("listening Error: " + ex.Message); } }
2.(客户端)
byte[] data = new byte[1024]; //1、创建IPEndPoint实例和套接字 IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6001); clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //2、将套接字连接到远程服务器 try { clientSocket.Connect(ipep); } catch (SocketException ex) { MessageBox.Show("connect error: " + ex.Message); return; } //3、接收信息 while (true) { //接收服务器信息 int bufLen = 0; try { bufLen = clientSocket.Available; clientSocket.Receive(data, 0, bufLen, SocketFlags.None); if (bufLen == 0) { continue; } } catch (Exception ex) { MessageBox.Show("Receive Error:" + ex.Message); return; } string clientcommand = System.Text.Encoding.ASCII.GetString(data).Substring(0, bufLen); lstClient.Items.Add(clientcommand); //clientSocket.Shutdown(SocketShutdown.Both); //clientSocket.Close(); }
UDP使用无连接的套接字,无连接的套接字不需要在网络设备之间发送连接信息。
注意: 必须使用Bind方法将套接字绑定到一个本地地址和端口之后才能使用ReceiveFrom方法接收数据,如果只发送而不接收,则不需要使用Bind方法。