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