在開始講之前,我想先跟大家描述一下,這個所謂的通信程序具體是一個什么樣的東西。該通信程序類似一個弱版本的qq,登錄時需要進行注冊,登錄成功后,可以實現即時的通信,群聊,私聊,同時還可傳文件。先上個圖
服務端: 客戶端登錄: 客戶端主界面:
所謂的即時的通信程序,也就是利用TCP和UDP的傳輸協議,進行信息、文件的傳輸。那什么是TCP,什么是UDP呢?
TCP是Transmission Control Protocol(傳輸控制協議)的簡稱,是TCP/IP體系中面向連接的運輸層協議,在網絡中提供全雙工的和可靠的服務。TCP協議的主要特點是:基於連接的協議,數據傳輸比較穩定,且可以保證數據按順序的准時達。
UDP 是User Datagram Protocol(用戶數據報協議)的簡稱,是 OSI 參考模型中一種無連接的傳輸層協議,提供面向事務的簡單不可靠信息傳送服務。UDP協議的主要特點是:基於無連接協議,數據傳送迅速,反應快,同時缺點也很明顯,就是數據傳輸不穩定,不能保證數據到達的順序,可能會有數據的缺失。
我們所熟悉的QQ就是一種基於TCP和UDP兩種協議結合的一種即時通信程序,在我們進行會話、聊天的時候,采用UDP協議,通過服務器中轉方式。大家都知道,UDP 協議是不可靠協議,它只管發送,不管對方是否收到的,但它的傳輸很高效。但是,作為聊天軟件,怎么可以采用這樣的不可靠方式來傳輸消息呢?於是,騰訊采用了上層協議來保證可靠傳輸:如果客戶端使用UDP協議發出消息后,服務器收到該包,需要使用UDP協議發回一個應答包。如此來保證消息可以無遺漏傳輸。之所以會發生在客戶端明明看到“消息發送失敗”但對方又收到了這個消息的情況,就是因為客戶端發出的消息服務器已經收到並轉發成功,但客戶端由於網絡原因沒有收到服務器的應答包引起的。在我們進行文件傳輸的時候使用的是基於TCP的數據傳輸協議,因為要保證傳輸數據的順序到達,同時還要保證不能有數據的丟失。
在我們的.NET平台下如果想要實現即時的通信就需要借助於Socket網絡編程,Socket(網絡套接字)是網絡通信的基本構件,它是可以被命名和尋址的通信端口,使用中,每個Socket 都有對應的類型和一個與之相關的進程。在開始介紹Socket創建即時通信之前我們需要先了解下使用TCP進行即時通信的操作流程:
下邊我會結合這個流程圖,一步一步跟大家講通信的具體流程。
1、創建服務端套接字,監聽本機的IP和一個特定的端口,把本機作為服務端。(下面的只是一個簡單的演示實例)
int port = 11615;//監聽端口,最好設置的大一些,避免和一些特定的端口沖突 IPAddress ipAdd=IPAddress.Parse(“192.168.245.1”);//監聽IP,我們通過Dns.GetByHostName().Arrylist[],動態獲得本機的IP,這里僅是示例。 TcpListener listener=new TcpListener(ipAdd, port);//創建監聽對象
2、監聽服務器端口,等待客戶端連接請求
listener.Start();
3、創建客戶端的套接字,並向遠程服務端發送連接請求
TcpClient tcpClient=new TcpClient(); int port = 11615; IPAddress ipAdd=IPAddress.Parse(“192.168.245.1”); tcpClient.Connect(ipAdd, port);
4、服務端確認與客戶端的連接,並創建與該客戶端對應的Socket對象
Socket clientSocket= listener.AcceptSocket();
5、客戶端獲取與服務器通信的流通道
NetworkStream stream = tcpClient.GetStream();
6、客戶端通過流通道與服務器端進行數據傳輸
6.1、客戶端向服務器端發送數
string hello=“Hello World!”; byte [] buffer=Encoding.Default.GetBytes(hello); stream.Write(buffer,0,buffer.Length);
6.2、客戶端接收從服務器端發來的數據
byte [] buffer=new byte[1024]; int length= stream.Read(buff,0,buff.Length); string msg = Encoding.Default.GetString(buffer,0,length);
7、服務端接受客戶端請求
7.1、服務端向客戶端發送數據
string hello=“Hello World!”; byte [] buffer=Encoding.Default.GetBytes(hello); stream.Write(buffer,0,buffer.Length);
7.2、服務端接收從客戶器端發來的數據
byte [] buffer=new byte[1024]; int length= stream.Read(buff,0,buff.Length); string msg = Encoding.Default.GetString(buffer,0,length);
8、服務端斷開與客戶端的連接
clientSocket.Close();
9、客戶端斷開與服務端的連接
tcpClient.Close();
10、服務端關閉服務套接字
tcpClient.Close();
大體的通信流程就是這樣子,但是到具體的實際應用中還有不同 ,因為我們要涉及到多個客戶端之間的交互,而服務端只是相當於一個中間的媒介,接收客戶端傳遞的消息,並將消息轉發給另一個客戶端,如果有多個客戶端,同時進行通信,而這個時候就需要用到多線程進行通信的管理和控制。
具體的流程如下:
首先,我們在需要服務端創建一個TCPLister對象,啟用一個線程,專門負責監聽客戶端的請求。
其次,只要有一個客戶端進行請求 ,則TCPLister對象會馬上創建一個專門負責通信的Socket與客戶端請求的套接字,
同時再啟用一個新的線程專門負責與客戶端進行通信。
在客戶端,我們只需要創建一個TCPClient對象,啟用一個新的線程來專門的負責信息的接受即可。
在具體的通過過程中,我們還需要定制一些列的消息通信協議,在消息內部用“|”管道符將其內容進行分割,通過對協議的解析我們可以對不同的消息做不同的處理。在本應用才程序中,具體的通信協議如下:
在服務端接收協議如下:
命令格式 接收到客戶端發送的通信流的字節數組中的首字節Msg[0] |
說明 1、如果Msg[0]則說明發送的是文件,則遍歷在線用戶列表,像用戶在線用戶逐一發送該字節數組 2、如果Msg[0]!=0,則說明發送過來的信息字符串,則直接進行Encoding將字節數組轉換為字符串,並對字符串進行如下的分析,根據不同的分析結果執行不同的命令。 |
ON|發送者的用戶名| |
1、該命令在客戶端與服務器端建立連接后由客戶端自動發送 2、服務器端收到該命令后,將“用戶名”添加到在線用戶列表並向所有在線用戶發送消息“***上線啦!" |
Off|發送者的用戶名| |
1、該命令是在用戶上線、或者離開時執行的,用於向客戶端發送在線人員名單,客戶端接受到該信息后就會對在線列表進行更新。 |
MSG|接收者姓名|發送者姓名|發送內容| MSGALL|發送者的用戶名|發送內容| |
1、該命令是用戶點擊發送消息按鈕時,發送的信息,前提是用戶必須選定要發送的對象 2、服務端收到該消息后,對消息進行解析,從中取得接受者用戶名,並從在線列表中取得相應的通訊Socket,將“發送者用戶名說“發送內容“這樣格式的消息發送回客戶端 3、如果是MSGALL,則為群發消息,服務器端收到該命令后,將遍歷在線用戶列表的用戶,並逐一向在線用戶發送”發送者的用戶名說‘發送內容’”這樣的信息發送給所有的在線用戶 |
在客戶端接收協議如下:
命令格式 |
說明 |
接受服務端發送的通信流字節數組中的首字節Msg[0] |
如果Msg[0]==0則說明服務端發送的是文件,直接新建文件流從接收到的字節數組中的第二個字節開始讀取,讀取長度為字節數組長度減一個字節,並在本地保存文件。 如果Msg[0]!=0則說明服務端發送的是消息字符串,然后再根據下面的命令格式進行解析,進而做出不同的操作 |
OnLine|剛登錄的用戶名| |
客戶端收到該命令后,在聊天串口中看到“****”上線了的通知! |
Off|用戶1、用戶2、用戶3、| |
該命令是在服務器端收到客戶端發來的Off和On命令,回發給客戶端的命令,然后客戶端解析命令,更新在線人員列表。 |
以上這些就是該即時通信程序的通信格式,在下一節中,我會帶領大家一步一步的構建該即時通信程序的服務端,在構建的過程中會逐步向大家演示TCPLister的應用,以及相關的涉及的多線程的知識。
好了,這一節就到這里了,希望能給大家帶來些許幫助,同時也希望大家多多指點。