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連接池 ”這個名詞,接下來也嘗試一下。用那個來做應該會更好。這個小程序的確簡陋了,期待各位園友拍磚和吐槽。謝謝

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM