• 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服务器端的一些操作,更多的优化例如超时断开的处理,数据包中黏包的处理等会在后续篇幅中再介绍

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

  • 相关阅读:
    查看单据项目文本对应的参数信息
    下载EPM包详细运行日志
    ABAP 字符串换行符处理
    PyCharm编辑HTML文件时输入{%不能自动补全
    Ubuntu 18.04安装MongoDB 4.0
    ubuntu18.04(bionic) 配置阿里数据源
    【Python】迭代器
    【python】多任务(2. 进程)
    【python】多任务(1. 线程)
    【python】文件下载---基础版
  • 原文地址:https://www.cnblogs.com/ywkcode/p/15208441.html
Copyright © 2020-2023  润新知