目錄
之前有好幾篇博客在講TCP/UDP通信方面的內容,也有做過一些Demo(包括整理出來的、可供學習使用的簡單通信框架)。具體可以參見以下博客:
http://www.cnblogs.com/xiaozhi_5638/p/4244797.html(清晰易懂TCP通信原理解析)
http://www.cnblogs.com/xiaozhi_5638/p/3290283.html(基於泵的TCP通信過程建立)
http://www.cnblogs.com/xiaozhi_5638/p/3169641.html(基於泵的UDP通信過程建立)
http://www.cnblogs.com/xiaozhi_5638/p/4528551.html(泵結構在系統中的作用)
可以看出,很多博客一直在強調“泵”(循環結構)在各個場合中的作用,若有不清楚泵結構的朋友,可以參見這篇博客“代碼中的泵”。今天這次博客的重點並不是講泵結構在通信過程中的應用,而是講如何在同一進程中輕松快捷地創建多個TCP服務端(綁定多個Port)、多個TCP客戶端(隨意連接指定服務端)、多個UDP客戶端(綁定多個Port),同時能夠進行統一管理、訪問。
在TCP通信開發過程中,經常會出現一個程序需要監聽多個Port,或者一個程序需要連接多個TCP服務器(本人實際項目中遇到過),那么如何統一、方便管理創建的多個Socket呢?
如上圖左邊所示,一個程序需要同時訪問兩個Server,那么它至少要同時保持兩個Socket連接。同理,一個服務程序很可能同時監聽多個Port,需要管理多個偵聽Socket。在UDP通信系統中(上圖右邊),一個程序也可能需要監聽多個Port,同時接收多個Port上的數據。那么本文提供了一套可以輕松管理這些多個Socket的方案。
TCP服務端
一個TCP服務端主要包含兩個結構:一個是Socket偵聽循環(Socket偵聽泵),一個便是數據接收循環(數據接收泵)。前者主要負責處理Socket連入請求,后者負責接收對應客戶端發來的數據。下圖顯示的是一個TCP服務端包含的主要構造:
TCP客戶端
一個TCP客戶端主要包含一個結構:數據接收循環(數據接收泵)。客戶端Socket連入服務器成功后,便需要開啟數據接收循環,用於接收服務端發來的數據。下圖顯示的是一個TCP客戶端包含的主要構造:
UDP客戶端
一個UDP客戶端主要包含一個結構:數據接收循環(數據接收泵)。客戶端綁定本地Port成功后,便需要開啟數據接收循環,用於接收來自綁定端口的數據。下圖顯示的是一個UDP客戶端包含的主要構造:
注:
雖然UDP通信中的每個終端都是平等的(不存在主動連入和被動連入),但還是習慣上稱每個終端為Client。雖然它包含的主要結構和TCP客戶端類似(都只有一個數據接收循環),但是在TCP中,客戶端Socket需要提前Connect到服務器,而通常情況下,UDP需要提前綁定本地端口。
這里說到的Socket,在TCP服務端中指的是偵聽Socket,在TCP客戶端中指的是與服務端建立連接的Socket,而在UDP客戶端中指的是綁定本地端口的Socket。這些Socket都可以存在多個,TCP服務端中可以有多個Socket偵聽不同的Port,TCP客戶端可以有多個Socket與不同的服務端建立連接,而UDP客戶端中則可以有多個Socket綁定不同的端口。那么如何管理多個Socket呢?
答案其實很簡單,可以將它們放進一個容器,統一通過容器管理、訪問。那么怎樣區分不同的Socket呢?我們可以給每個Socket加一個唯一標示(ID)。之后每次訪問,均通過ID區分,只要給定ID,其余的操作均相同。這樣一來,TCP服務端、TCP客戶端以及UDP客戶端的構造可以變成:
多個TCPServer:
多個TCPClient
多個UDPClient
每次訪問,比如TCP服務端開啟偵聽、注冊事件以及主動調用發送數據的API時,均可以通過容器代理進行操作,只需要給出指定的ID進行區分即可。
TCP服務端
框架公開了TCPServerManager、TCPEndPoint兩個類型,前者主要負責代理操作的功能,所有對服務端的操作均通過它來完成;后者對應每個連接進來的客戶端。
1)創建服務器
之后便可以使用manager操作創建好的服務器。注意如果這里已經存在ID為RegisterServer的服務器的話,manager就指代已經創建好的服務器。我們還可以創建第二服務器:
以上,只要給定的ID不同即可。
2)啟動服務器
3)注冊事件
以上分別注冊客戶端連接、斷開以及發送消息的事件,之后便可以在事件處理方法中處理消息。
4)消息處理
當客戶端發送消息時,系統會激發TCPMessageReceived事件,我們在事件處理程序中可以解析、處理消息:
在消息處理這塊,框架簡單地定義了一個“協議”:消息類型+消息正文。如果你覺得不夠,完全可以在args.Data中再定義自己的協議。客戶端上線、下線處理方式類似。這里不再贅述。
TCP客戶端
框架公開了TCPClientManager類型,它主要負責代理操作的功能,所有對客戶端的操作均通過它來完成。
1)創建客戶端
以上創建了一個TCP客戶端。如果給定的ID已存在,那么manager指代已經存在的客戶端。同理,我們可以創建第二個客戶端:
以上,只要給定的ID不同即可。
2)連接服務器
3)注冊事件
以上注冊消息事件,當收到服務端消息時會激發TCPMessageReceived事件。
4)消息處理
TCP客戶端消息處理這塊參見TCP服務端。原理類似。
UDP客戶端
框架公開了UDPClientManager類型,主要負責代理操作的功能,對UDP客戶端的所有操作均由它來完成。
1)創建客戶端
以上創建了一個UDP客戶端。如果給定的ID已存在,那么manager指代的是已經存在的UDP客戶端。同理,我們可以創建第二個UDP客戶端:
以上,只要給定的ID不存在即可。
2)監聽端口
以上創建了兩個不同的UDP客戶端,分別監聽不同的Port,接收不同的數據。
3)注冊事件
以上注冊了消息事件,當端口上有數據收到時會激發UDPMessageReceived事件。
4)消息處理
當UDP客戶但收到消息時,系統會激發UDPMessageReceived事件,我們可以在事件處理程序中解析、處理消息:
注意UDP這塊處理消息有一點與TCP不同,args參數中是以RemoteIP和RemotePort來表明消息發送方的信息,而TCP處理消息時,args參數中以TCPEndPoint來代表消息發送方的信息,后者包含的信息更全(不只有RemoteIP和RemotePort,具體參見源碼)。
在消息處理這塊,框架簡單地定義了一個“協議”:消息類型+消息正文。如果你覺得不夠,完全可以在args.Data中再定義自己的協議。
.NET4.0 VS2010
對外公開的有TCPServerManager、TCPClientManager、TCPEndPoint、UDPClientManager以及Msg枚舉類型。
- TCP部分已解決沾包問題,附帶心跳檢測功能(有待完善);
- 框架默認采用了一個簡單的協議,用以區分發送消息的類型。我們在使用過程中,完全可以自定義應用層協議(再封裝一層),並在收到數據后解析;
- TCP服務端的在線列表需要自己人工維護(上下線);收到的消息數據都是byte[]類型,需要自己解析;而且不能注冊自己想要的消息(必須全部接收)。由於我司主要開發局域網內網系統,對連接數量要求不是很高,我司實際使用中的通信框架經實體機房測試,連接數在400+,系統運行穩定。TCP部分發送3G大文件,不會出現內存異常等問題。
- 這次也是為了驗證多個Socket管理的方案,所以才出了這個框架,由於時間倉促,所以僅供學習使用。若有人要應用到實際項目中去,可能需要修改完善一些地方,因為我並沒有完全測試。
源碼中帶有兩個Demo。
github:https://github.com/sherlockchou86/TJSYXYCommunication
rar文件直接下載:http://files.cnblogs.com/files/xiaozhi_5638/TJSYXY.Communication.rar