zoukankan      html  css  js  c++  java
  • Socket 一对多通信

    Socket 一对多通信

    这篇文章橙色的文字都是废话,不耐烦的园友可以跳过那些文字。包括这句话。

      最初接触Socket编程的是在学校的java课上,可那时候没心学java,老师讲的Socket也没怎么理会,上机操作时,上网拷了一段C#的客户端和服务端代码,分别与java写的服务端和客户端进行通信。至于整个通信流程是怎样的没理会,直到写上一篇博文时才清楚。

      还记得那时候上课老师问过如果一个服务端要跟两个客户端通信,那怎么办?接着他复制粘贴了一下创建Socket,绑定,监听那几行代码。

    1 ServerSocket ss1 = new ServerSocket(8081);
    2 Socket s1 = ss1.accept();
    3 ServerSocket ss2 = new ServerSocket(8082);
    4 Socket s2 = ss2.accept();

      其实这样是多开了端口,的确是一个服务端对两个客户端通信了,但真正的一对多通信肯定不是这样吧,否则作为一台服务器,面对那么大的并发量,要开多少个端口才完事。如果我没理解错的话,java的accept也是同步的,这样就意味着一个客户端1跟这个服务端连接了之后,需要再来一个客户端2 (或者在那个客户端1) 与另一个端口连接了,才能正常通信。这样个人觉得不科学。

      上网谷歌了一下,找到了一篇博文,是java的Socket的一对多通信,里面的一句话我一看眼发亮了:多执行一次Accept()。想C#与java类似的,于是尝试了一下,果然行。唉!看来学Socket编程的,都是参考java那部分的文章。

      结合了对多线程的皮毛理解,想了一个办法实现一个服务端的与多个客户端交互,而且是用同一端口。大致是这样,在服务端的主线程执行一个循环去Accept客户端的Connet。每Accept一次,就开一个线程去负责与这个客户端通信。下面就上代码

    首先是一些需要用到的变量

    1         static int socketCount;  //已经开的线程
    2         static object threadFlag; //多线程的锁旗标
    3         const int MAX_SOCKET_COUNT = 3;  //最大接受客户端数量

    接着是Main方法里面的代码

    复制代码
    1             threadFlag = new object();
    2             socketCount = 0;
    3 
    4             Socket serverScoket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
    5             IPEndPoint serverPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8081);
    6 
    7             serverScoket.Bind(serverPoint);
    8             serverScoket.Listen(10);
    复制代码

    这里跟往常的Socket通信一样。建立终结点和Socket,绑定和监听。下面的就涉及到多线程了。

    复制代码
     1             Thread t = null;
     2             Socket clientSocket = null;
     3             try
     4             {
     5                 while (true)
     6                 {
     7 
     8                     //判断当前已开的线程数是否超出最大值,超出了则要等待
     9                     while (socketCount >= MAX_SOCKET_COUNT) Thread.Sleep(1000);
    10                     clientSocket = serverScoket.Accept();
    11                     //累计变量自增
    12                     socketCount++;
    13                     IPEndPoint clientPoint = clientSocket.RemoteEndPoint as IPEndPoint;
    14                     Console.WriteLine("client {0}:{1} connect",clientPoint.Address,clientPoint.Port);
    15                     t = new Thread(new ParameterizedThreadStart(ThreadConnect));
    16                     t.Start(clientSocket);
    17                 }
    18             }
    19             finally
    20             {
    21                 serverScoket.Close();
    22                 serverScoket.Dispose();
    23                 Environment.Exit(0);
    24 
    25             }
    复制代码

      用了一个While循环去不断地有限制地接受客户端。每当接受了一个客户端,就开一个线程去处理它的通信。同时已开启线程数量累加一个。如果已开启的线程数大于最大值的话是停止接受的。防止开的线程过多压坏了服务端。

      在接受时只是用了同步接受,没采用异步,因为感觉没必要,反正接受包在了一个死循环里面,还没接收到就让主线程一直卡在那句里面。等待接受了,才开始下一次接受等待。不过这里用的一个死循环觉得挺不妥的。没办法跳出循环,关闭Socket,释放资源等一系列操作不知道在哪里执行好。

    最后上一段线程执行方法代码

    复制代码
     1         static void ThreadConnect(object clientObj)
     2         {
     3             Socket clientSocket = clientObj as Socket;
     4             IPEndPoint clientPoint = clientSocket.RemoteEndPoint as IPEndPoint;
     5             if (clientSocket == null)
     6             {
     7                 lock (threadFlag)
     8                 {
     9                     socketCount--;
    10                 }
    11                 return; 
    12             }
    13             clientSocket.Send(Encoding.ASCII.GetBytes("Hello world"),SocketFlags.None);
    14             byte[] datas;
    15             int rec;
    16             while (true)
    17             {
    18                 datas = new byte[1024];
    19                 rec = clientSocket.Receive(datas);
    20                 //当客户端发来的消息长度为0时,表明结束通信
    21                 if (rec == 0) break;
    22 
    23                 string msg= "Msg has been receive length is "+rec;
    24                 clientSocket.Send(Encoding.ASCII.GetBytes(msg),SocketFlags.None);
    25             }
    26             Console.WriteLine("client {0}:{1} disconnect", clientPoint.Address, clientPoint.Port);
    27             lock (threadFlag)
    28             {
    29                 //减少当前已开的线程数
    30                 socketCount--;
    31                 clientSocket.Close();
    32                 clientSocket.Dispose();
    33             }
    34         }
    复制代码

      整个消息接收和发送过程都放在了死循环里面,由客户端发来的信息的长度来判定客户端是否终止通信,从而断定是否跳出循环。不过消息的收发可以用异步的,只不过这里没用上而已,感觉这里应该用。在最后的地方还要关闭Socket,释放资源。同时也要减少已开的线程数,毕竟这个线程已经完了。

      在写完这篇文章时突然想到 “Sokect连接池 ”这个名词,接下来也尝试一下。用那个来做应该会更好。这个小程序的确简陋了,期待各位园友拍砖和吐槽。谢谢

     
     
    分类: C#Socket编程
  • 相关阅读:
    NTP on FreeBSD 12.1
    Set proxy server on FreeBSD 12.1
    win32 disk imager使用后u盘容量恢复
    How to install Google Chrome Browser on Kali Linux
    Set NTP Service and timezone on Kali Linux
    Set static IP address and DNS on FreeBSD
    github博客标题显示不了可能是标题包含 特殊符号比如 : (冒号)
    server certificate verification failed. CAfile: none CRLfile: none
    删除文件和目录(彻底的)
    如何在Curl中使用Socks5代理
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3012296.html
Copyright © 2011-2022 走看看