zoukankan      html  css  js  c++  java
  • 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连接池 ”这个名词,接下来也尝试一下。用那个来做应该会更好。这个小程序的确简陋了,期待各位园友拍砖和吐槽。谢谢

  • 相关阅读:
    Final-阶段站立会议5
    Debug阶段成员贡献分
    每周例行报告——第九周
    beta发布简评
    简易四则运算生成程序——批量出题
    每周例行报告——第八周
    每周例行报告——第七周
    每周例行报告——第六周
    课堂作业:alpha发布点评
    “四则运算生成程序——GUI支持和部分功能改进”链接
  • 原文地址:https://www.cnblogs.com/HopeGi/p/3012112.html
Copyright © 2011-2022 走看看