C#特性知识图谱六、Socket

第六章 Socket网络编程

1、基础知识

基于TCP/IP谢意开发网络应用程序,应用程序之间主要通过Socket交互数据。
image
微软在设计Windows是引入了UNIX中Socket的概念和响应的设计理念,并针对Windows的特性略作调整,形成了Windows平台的Socket,简称为WinSock,并为开发者提供了一整套的API,称为Windows WinSock Win32 APIWinSock主要有2个版本如下所示:
image
WinSock的操作系统底层基于tcpip.sys(负责管理网络连接和缓冲管理)和Afd.sys(Ancillary Function Driver for WinSock,用于支持基于window socket的应用程序,比如Ftp、Telnet等)实现。
image

2、.Net Framework中的Socket

Socket类是对WinSock的封装,拥有不少方法直接对应于WinSock中的C/C++函数,比如PollSelectIOControl等,Socket类定义有27个属性,近100个方法,其中Handle属性代表位于操作系统核心的Socket核心对象,Socket类提供SetSocketOption方法来设置各种选项属性。

3、.Net Socket的编程模式

Windows平台网络编程技术
image

  • 基于阻塞模式的Socket编程
    为单线程或多线程的,每个线程处理一个客户端链接。
  • “非阻塞”模式的Socket编程
    为早期UNIX为提示网络应用程序性能二采用的编程模式,建议不要在.Net网络程序中使用。
  • 使用IAsyncResult的异步编程模式
    Socket类提供了一堆的“BeginXXX/EndXXX”方法实现异步Socket编程,使用线程池中的线程完成工作,性能较好。
  • 使用EAP的一部编程模式
    Socket类提供了以“Async”结尾的方法,在底层使用Windows操作系统的Completion Port(完成端口)和Overlapped I/O Mechanism(重叠输入/输出机制),以提高性能。

4、Socket编程教学

4.1、Server端Socket

Server端Socket在制定的端口上监听,等待客户端的连接请求,并且想客户端发送或接收数据。

4.1.1 创建Socket对象

image

4.1.2 绑定

在Socket应用程序中,绑定的作用是让某个Socket对象关联上特定的网络接口Network Interface(IPEndPoint)。
image
如果不需要制定特定的网络接口,也不再议使用的端口,那么可以创建一个使用IPAddress.Any,端口为0的IPEndPoint,在这种情况下,操作系统将使用[1024,5000]之间的端口进行处理。

4.1.3 监听(Listen)

通知操作系统将跟我绑定的网络接口和端口号一致的数据包发送给我来处理。Socket对象的Listen方法在绑定的IPEndPoint上启动监听。

server.Listen(10);

image

4.1.4 响应连接请求

如果有客户端的连接请求,服务端的Socket的Accept()方法获取于此请求对应的Socket,准备实现数据交互。

Socket client=server.Accept();

Accept()方法返回的Socket不能用于接收新的连接,但可以用于接收和发送数据,同时,其RemoteEndPoint方法可以获取远程客户端的IP地址和端口号。

4.1.5 接收处理数据

  • 将接收到的数据保存到实现准备好的数据缓冲区:
byte[] data=new byte[BufferSize];
int recv=client.Receive(data);
  • 处理接收到的数据:
Console.WriteLine(Encoding.UTF8.GetString(data,0,recv));

4.1.6 关闭Socket

  • 关闭方式
  1. 调用Shutdown方法通知TCP/IP协议栈发送所有未发送的数据,或停止接收数据。
  2. 调用Close方法关闭套接字。
    image
  • 使用using
    Socket本身实现了IDisposable接口,所以可使用using关键字实现自动释放。
    image

4.2、Client端Socket

Client端Socket尝试连接Server端的Socket,在连接成功后与Server端应用程序相互收发数据。

4.2.1 客户端创建Socket对象

image

4.2.2 发送数据

server.Send(Encoding.UTF8.GetBytes(Message));

4.3 异常处理

当有错误出现时会抛出一个SocketException,每个SocketException都有一个对应的错误号,是有底层的WinSock规定的。
image

4.4 总结

image

4.4.1 数据缓冲区问题

TCP协议为了保证数据能正确地送达目的地,要数据接收双方都比须缓冲收到(或发送)的数据。
image

  • 数据缓冲区简介
  1. 应用程序TCP/IP数据缓冲区
    由应用程序设置的数据缓冲区,多使用字节数组,保证它能放下最长的那一条消息。
  2. TCP/IP系统缓冲区
    由操作系统为TCP/IP所提供的专用数据缓冲区,默认情况下Windows操作系统为TCP所提供的数据缓冲区大小为8K。我们可以通过Socket对象的一下属性对其进行调整:
public int ReceiveBufferSize {get;set;}
public int SendBufferSize {get;set;}
  • 客户端数据发送:
    image

  • 服务端接收数据:
    image

  • 数据传送要求:

  1. 双方SendReceive必须配对。
  2. 双方设置的数据缓冲区大小应该能放下一个完整的消息。
  3. 数据发送方和接收方的数据处理速度应该匹配。

以阻塞方式调用SocketSend方法只是保证这些数据已经被成功地送到了操作系统为TCP/IP设置的缓冲区中,但是否马上就被操作系统送到网络上,并且马上对方就能收到这些数据还是个未知数。

4.4.2 黏包与丢包

SocketSendReceive方法都有可以一次发送多个缓冲区数据的方法:

public int Send(IList<ArraySegment<byte>> buffers);
public int Receive(IList<ArraySegment<byte>> buffers);

其中ArraySegment代表一个一维数组的片段。接收方会尝试接收由缓冲区数据大小之和所确定的最大字节的数据,将其顺序填入缓冲区数组中。

4.4.3 解决缓冲区问题的方法

  1. 只传播固定大小的消息

  2. 采用一问一答的方式

    • 客户端发送数据完毕后,发送一条发送完成信号信息。

    • 服务端接收完数据之后,发送一条接收完成信号消息。

    • 客户端没有收到接收完成信号消息就等待且不会发送新的消息。

  3. 在消息开头附上一个消息的尺寸信息

    在消息头部开辟一小段空间用于存储消息长度值。

原文地址:https://www.cnblogs.com/tjubuntu/p/15464563.html