C# Socket初探

在Socket之前,首先得了解TCP/IP协议,该协议用来定义主机如何连入因特网和数据在它们之间传输的标准。

OSI七层网络模型如下图所示:

 其中,TCP和UDP在传输层,完成了程序到程序,端口到端口的通信。UDP协议简单高效,只管发送,不管是否收到,TCP协议可以理解为带确认模式的UDP协议(三次握手)。

Socket,翻译为套接字,是TCPIP的使用接口,基于Socket我们才能调用TCP协议。

Socket通信基本流程图入下图所示:

言归正传,以下通过代码实例进行简单演示,客户端我们通过SocketTool.exe进行模拟,主要演示服务器端代码

(1)Socket服务端连接

 1 int port = 60001;
 2 string host = "127.0.0.1";//服务器端ip地址
 3 
 4 IPAddress ip = IPAddress.Parse(host);
 5 IPEndPoint ipe = new IPEndPoint(ip, port);
 6 
 7 Socket sSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 8 sSocket.Bind(ipe);
 9 sSocket.Listen(0);
10 
11 Socket serverSocket = sSocket.Accept();
12 Console.WriteLine("连接已建立....");
13 try
14 { 
15    while (true)
16    {
17 
18     //receive message
19     string recStr = "";
20     byte[] recBytes = new byte[4096];
21     int bytes = serverSocket.Receive(recBytes, recBytes.Length, 0);
22    
23     recStr += Encoding.ASCII.GetString(recBytes, 0, bytes);
24     string RetMsg = $"客户端:{serverSocket.RemoteEndPoint.ToString()},消息:{recStr},时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}";
25     Console.WriteLine($"获得客户端消息:{RetMsg}");
26 
27     //回发消息
28     if (!string.IsNullOrEmpty(recStr))
29     {
30         byte[] sendByte = Encoding.ASCII.GetBytes("success!");
31         serverSocket.Send(sendByte, sendByte.Length, 0);
32     } 
33    }
34 }
35 catch (Exception ex)
36 {
37     Console.WriteLine($"Socket Error:" + ex.Message);
38 }
39 finally 
40 {
41     sSocket.Close();
42 }

其中,分别定义用于监听的Socket和连接的Socket,通过方法Receive监听客户端传来的消息。

问题:当前模式无法支持连接多个客户端,Socket等待连接的过程会造成阻塞。解决:通过多线程进行优化

(2)多线程下的Socket服务端

int port = 60001;
string host = "127.0.0.1";//服务器端ip地址
IPAddress ip = IPAddress.Parse(host);
IPEndPoint ipe = new IPEndPoint(ip, port);

serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(ipe);
serverSocket.Listen(10);
Console.WriteLine("等待客户端连接...");

 try
 {
    Thread socketThread = new Thread(ListenClientSocket);
    socketThread.Start();
}
catch (Exception ex)
{
      Console.WriteLine($"Socket Error:" + ex.Message);
}
finally 
{
      serverSocket.Close();
 }
 1 static void ListenClientSocket()
 2 {
 3     while (true)
 4     {
 5         Socket clientSocket = serverSocket.Accept();
 6         Console.WriteLine("连接已建立....");
 7         #region 消息回发
 8         byte[] sendByte = Encoding.ASCII.GetBytes("success!");
 9         clientSocket.Send(sendByte, sendByte.Length, 0);
10         #endregion
11 
12         #region 连接客户端,解析数据,多线程 
13         Thread receivethread = new Thread(ReceiveSocket); //委托方法
14         receivethread.Start(clientSocket);
15         #endregion
16 
17     }
18 }
 1 static void ReceiveSocket(object clientsocket)
 2 { 
 3     Socket myclientSocket= (Socket)clientsocket;
 4     while (true)
 5     {
 6         string recStr = "";
 7         byte[] recBytes = new byte[4096];
 8         int bytes = myclientSocket.Receive(recBytes, recBytes.Length, 0);
 9 
10         recStr += Encoding.ASCII.GetString(recBytes, 0, bytes);
11         string RetMsg = $"客户端:{myclientSocket.RemoteEndPoint.ToString()},消息:{recStr},时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}";
12         Console.WriteLine($"获得客户端消息:{RetMsg}");
13     }
14 }

监听和连接的Socket我们通过创建线程Thread解决等待时候的阻塞。

问题:多线程再客户端数量较多的情况下,每次创建新线程都会消耗一定的内存。解决:通过线程池进行优化

(3)线程池下的Socket

线程池中的线程执行完指定的方法后并不会自动消除,而是以挂起状态返回线程池,如果应用程序再次想线程池发出请求,以挂起状态的线程就会被激活并执行任务,而不会创建新线程,从而节约开销
1 ThreadPool.QueueUserWorkItem(new WaitCallback(方法名));
2 ThreadPool.QueueUserWorkItem(new WaitCallback(方法名), 参数);

通过创建1000个线程和从线程池中取1000个线程各自消耗的时间进行对比:

 不难发现,线程池的方案性能更优异。但也不是没有缺点,线程池取的线程都是后台线程,级别都是Normal。

更新后的代码如下:

主体方法中:

ThreadPool.QueueUserWorkItem(state=> ListenClientSocket());
//Thread socketThread = new Thread(ListenClientSocket);
//socketThread.Start();

监听方法中:

1  #region 连接客户端,解析数据,多线程
2  ThreadPool.QueueUserWorkItem(state => ReceiveSocket(clientSocket));
3  //Thread receivethread = new Thread(ReceiveSocket); //委托方法
4  //receivethread.Start(clientSocket);
5  #endregion

上述基本介绍了Socket服务器端的一些操作,更多的优化例如超时断开的处理,数据包中黏包的处理等会在后续篇幅中再介绍

以上,仅用于学习和总结!

原文地址:https://www.cnblogs.com/ywkcode/p/15208441.html