• 闲来无事,写个基于TCP协议的Socket通讯Demo


      .Net Socket通讯可以使用Socket类,也可以使用 TcpClientTcpListenerUdpClient类。我这里使用的是Socket类,Tcp协议。

      程序很简单,一个命令行的服务端,一个命令行的客户端。服务端启动后根据输入的端口号绑定本机端口并启动侦听,客户端启动后根据输入的客户端数量、服务IP、服务端口号连接服务端。客户端连接成功后启动新线程随机发送消息到服务端并等待接收服务端返回的消息,服务端和客户端成功创建连接后启动新线程接收客户端消息并返回客户端一个消息,如此循环下去……

      上图:

    图1. 客户端运行界面

    图2. 服务端运行界面

     

    图3. 项目结构

      

      

    服务端关键代码

      启动侦听:

    1 var endPoint = new IPEndPoint(0, port);
    2 var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    3 socket.Bind(endPoint);
    4 socket.Listen(int.MaxValue);

      启动新线程接收客户端连接:

    1 // 启动新线程负责接受客户端连接
    2 var socketThread = new Thread(OnSocketAccept) {IsBackground = true};
    3 socketThread.Start(socket);
    4 Show("服务准备就绪");

      接收客户端连接细节:

     1 /// <summary>
     2 /// 建立Socket连接
     3 /// </summary>
     4 /// <param name="obj"></param>
     5 private static void OnSocketAccept(object obj)
     6 {
     7     try
     8     {
     9         var socket = obj as Socket;
    10         while (true)
    11         {
    12             try
    13             {
    14                 Socket clientSocket = socket.Accept();
    15                 ...
    16                 // 尝试获取锁,超时则关闭当前连接并继续下次循环
    17                 if (!Monitor.TryEnter(ClientDictLock, LockTimeOut))
    18                 {
    19                     CloseSocket(socket, key);
    20                     continue;   
    21                 }
    22                 try
    23                 {
    24                     // 当然连接已存在则先关闭再缓存新连接
    25                     if (ClientDict.ContainsKey(key))
    26                     {
    27                         CloseSocket(ClientDict[key], key);
    28                         ClientDict[key] = clientSocket;
    29                     }
    30                     else
    31                     {
    32                         ClientDict.Add(key, clientSocket);
    33                     }
    34                 }
    35                 finally
    36                 {
    37                     Monitor.Exit(ClientDictLock);
    38                 }
    39                 // 启动线程池线程执行接收和发送操作
    40                 ThreadPool.QueueUserWorkItem(OnSendOrReceive, clientSocket);
    41             }
    42             catch (ThreadAbortException)
    43             {
    44                 throw;
    45             }
    46             catch (Exception exception)
    47             {
    48                 ...
    49             }
    50         }
    51     }
    52     catch (ThreadAbortException)
    53     {
    54         ...
    55     }
    56     catch (Exception exception)
    57     {
    58         ...
    59     }
    60 }

      发送/接收数据:

     1 /// <summary>
     2 /// 接收/发送数据
     3 /// </summary>
     4 /// <param name="obj"></param>
     5 private static void OnSendOrReceive(object obj)
     6 {
     7     try
     8     {
     9         var socket = obj as Socket;
    10         ...
    11         while (true)
    12         {
    13             try
    14             {
    15                 ...
    16                 // 接收数据
    17                 var receiveLength = socket.Receive(receiveBuffer);
    18                 ...
    19                 // 发送数据
    20                 var sendLength = socket.Send(sendBuffer);
    21                 ...
    22             }
    23             catch (ThreadAbortException)
    24             {
    25                 throw;
    26             }
    27             catch (SocketException exception)
    28             {
    29                 CloseSocket(socket, key);
    30                 break;
    31             }
    32             catch (Exception exception)
    33             {
    34                 ...
    35             }
    36         }
    37     }
    38     catch (ThreadAbortException)
    39     {
    40     }
    41     catch (Exception exception)
    42     {
    43         ...
    44     }
    45 }

      关闭连接:

     1 /// <summary>
     2 /// 关闭连接
     3 /// </summary>
     4 /// <param name="socket"></param>
     5 /// <param name="key"></param>
     6 private static void CloseSocket(Socket socket, IPEndPoint key)
     7 {
     8     socket.Shutdown(SocketShutdown.Both);
     9     socket.Disconnect(true);
    10     socket.Close();
    11     socket.Dispose();
    12     ...
    13 }

    客户端关键代码

      连接服务端:

    // 根据客户端数量建立Socket连接
    for (int i = 1; i <= clientNum; i++)
    {
        ConnetServer(address, port, i);
    }
    
    /// <summary>
    /// 建立Socket连接
    /// </summary>
    /// <param name="address"></param>
    /// <param name="port"></param>
    /// <param name="id"></param>
    static void ConnetServer(IPAddress address, int port, int id)
    {
        try
        {
            var clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            // 连接服务端
            clientSocket.Connect(address, port);
            ...        
            // 启动线程池线程开始发送数据和接收数据
            ThreadPool.QueueUserWorkItem(OnSendOrReceive, clientSocket);
        }
        catch (Exception exception)
        {
            ...
        }
    }

      发送/接收数据、关闭连接代码同服务端一样。

      优先使用线程池线程,服务端接受客户端连接使用独立线程(socketThread)是考虑到可能需要手工停止该线程。

      使用 Monitor.TryEnter(ClientDictLock, LockTimeOut) 和 Monitor.Exit(ClientDictLock); 是考虑到 lock 可能形成死锁,使用lock需要注意。

      本人才疏学浅,欢迎大家批评指正!

    源代码下载

      

  • 相关阅读:
    华为云亮相QCon2020深圳站,带你体会大厂的云原生玩法与秘诀
    没有它你的DevOps是玩不转的,你信不?
    开发实践丨用小熊派STM32开发板模拟自动售货机
    【基于C#的ASP.NET】错误篇1——用户系统+管理员系统CS1061: “ASP.manage_aspx”不包含“SqlDataSource1_Selecting”的定义
    阿里云高级技术专家白常明:边缘云的技术挑战和应用创新
    T级内存,创建效率提升10倍以上,阿里云 KVM异构虚拟机启动时间优化实践
    终极清单来了!阿里云双11爆款揭晓
    2020阿里云双110.73折起,爆款提前抢大促全攻略
    直面最大挑战双11 阿里数据中台为商家带来确定性保障
    OpenYurt 深度解读:如何构建 Kubernetes 原生云边高效协同网络?
  • 原文地址:https://www.cnblogs.com/dipwater/p/3573263.html
Copyright © 2020-2023  润新知