好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Socket 一对多通信

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编程

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于Socket 一对多通信的详细内容...

  阅读:56次