陳艷軍 溫占考 周勝群
摘要:該文首先分析了TCP/IP協(xié)議的基本概念和Socket編程的基本原理,重點(diǎn)在于用C#實(shí)現(xiàn)了基于TCP/IP網(wǎng)絡(luò)通信的長/短連接切換的程序模型,核心在于改進(jìn)傳統(tǒng)Socket連接中服務(wù)器的性能,根據(jù)需要進(jìn)行長短連接的切換,有效的提高了連接效率及網(wǎng)絡(luò)通信性能。該文對(duì)理解Socket通信原理和實(shí)際開發(fā)高效Socket應(yīng)用程序有較大的參考價(jià)值。
關(guān)鍵詞:TCP/IP;Socket長連接;Socket短連接
中圖分類號(hào):TP393 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):1009-3044(2014)08-1692-03
1 TCP/IP與Socket關(guān)系
TCP/IP協(xié)議(Transmission Control Protocol/Internet Protocol)定義了網(wǎng)絡(luò)設(shè)備如何接入Internet以及數(shù)據(jù)如何在它們之間傳輸?shù)臉?biāo)準(zhǔn)。協(xié)議采用了四層的層級(jí)結(jié)構(gòu),每一層都呼叫它的下一層所提供的協(xié)議來完成自己的需求。如今TCP/IP協(xié)議已經(jīng)成為計(jì)算機(jī)網(wǎng)絡(luò)協(xié)議事實(shí)上的標(biāo)準(zhǔn)。如圖1表明了這些協(xié)議的關(guān)系。
在計(jì)算機(jī)網(wǎng)絡(luò)體系的TCP/IP四層結(jié)構(gòu)中,終端的應(yīng)用進(jìn)程要相互通信,網(wǎng)絡(luò)層為兩端提供了邏輯通信,運(yùn)輸層為兩端應(yīng)用進(jìn)程提供了端到端的邏輯通信。而TCP/IP協(xié)議簇的一個(gè)接口,提供了可靠的運(yùn)輸層協(xié)議。這就表明應(yīng)用程序在使用數(shù)據(jù)傳輸之前,必須建立TCP連接。在傳輸數(shù)據(jù)完畢后,必須釋放已經(jīng)建立的TCP連接。其中,每一條TCP連接只能是點(diǎn)對(duì)點(diǎn)的,TCP連接的端點(diǎn)不是主機(jī),不是主機(jī)的IP地址,不是應(yīng)用進(jìn)程,也不是傳輸層的協(xié)議端口號(hào),而是套接字(Socket)。綁定端口號(hào)及IP地址即構(gòu)成套接字。圖2說明了應(yīng)用程序、套接字、協(xié)議之間的關(guān)系。
2 Socket的長/短連接
Socket通信在客戶端與服務(wù)器端的連接上分為長連接和短連接。具體的說,長連接指在客戶機(jī)和服務(wù)器連接后,可以連續(xù)發(fā)送數(shù)據(jù)包,當(dāng)沒有數(shù)據(jù)包發(fā)送時(shí),該連接一直保持,但這種建立起來的連接是不穩(wěn)定的。一般在較長時(shí)間沒有數(shù)據(jù)傳輸?shù)那闆r下,雙方需要發(fā)送“心跳包”來維持該連接有狀態(tài)。長連接一般的步驟是:連接→數(shù)據(jù)傳輸→保持連接(心跳)→數(shù)據(jù)傳輸→保持連接(心跳)→……→關(guān)閉連接;
短連接是指客戶端和服務(wù)端在需要通信時(shí)就建立一個(gè)Socket連接,在數(shù)據(jù)傳輸完成后關(guān)閉此連接。比如銀行一般都采用短連接。短連接一般的步驟是:連接→數(shù)據(jù)傳輸→關(guān)閉連接;
下面分析一下使用長連接和短連接的典型環(huán)境。當(dāng)數(shù)據(jù)在兩端傳輸比較頻繁,而且連接數(shù)不太多的情況下一般使用長連接。因?yàn)槊總€(gè)TCP的連接都需要三次握手,如果使用短連接不斷的建立連接在關(guān)閉。這樣就耗費(fèi)了大量的時(shí)間,而且降低了網(wǎng)絡(luò)通信的效率。所以在建立連接后不斷開,通信時(shí)直接發(fā)送數(shù)據(jù)包就行。例如:數(shù)據(jù)庫的連接一般使用長連接,如果用短連接頻繁的通信會(huì)造成Socket錯(cuò)誤。又如HTTP服務(wù),只是連接、請(qǐng)求、關(guān)閉。這個(gè)過程時(shí)間較短,服務(wù)器在響應(yīng)后即可關(guān)閉連接。
其實(shí)長連接是相對(duì)于通常的短連接而說的,也就是長時(shí)間保持客戶端與服務(wù)端的連接狀態(tài)。
3 Socket網(wǎng)絡(luò)通信長/短連接切換的C/S模型
Socket網(wǎng)絡(luò)通信是C/S(Client/Server)模型在互聯(lián)網(wǎng)上最常用的方式之一。服務(wù)器(Server)始終運(yùn)行,所以用死循環(huán)來完成,其一直監(jiān)聽客戶機(jī)(Client)的連接。當(dāng)客戶端發(fā)出連接時(shí),服務(wù)器即可獲得一個(gè)用于通信的套接字,客戶機(jī)使用該套接字與服務(wù)器進(jìn)行通信。本文采用長/短連接切換的C/S模型,其在服務(wù)器端設(shè)置了兩層循環(huán),第一層循環(huán)為死循環(huán),重復(fù)監(jiān)聽客戶端的Socket連接請(qǐng)求。第二層循環(huán)進(jìn)行長/短連接的切換。本模型中添加了一個(gè)標(biāo)志變量,在標(biāo)志變量表示為長連接時(shí),第二層循環(huán)始終保持,當(dāng)標(biāo)志變量為短連接時(shí),退出第二層循環(huán)。圖3說明了本模型中客戶機(jī)與服務(wù)器進(jìn)行長/短連接切換的通信步驟。
4 Socket通信中客戶端的實(shí)現(xiàn)
客戶端向服務(wù)器發(fā)起連接請(qǐng)求后,就被動(dòng)的等待服務(wù)器的響應(yīng)。下面給出主要的重點(diǎn)代碼,典型的TCP客戶端也都包括了以下內(nèi)容:
1)創(chuàng)建一個(gè)Socket實(shí)例。用于向指定的遠(yuǎn)程主機(jī)和端口建立一個(gè)TCP連接。
Socket c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//創(chuàng)建一個(gè)Socket
IPEndPoint iep =new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);
2)通過發(fā)送ShortComT/ ShortComF字符串來確定當(dāng)下是采用長連接還是短連接。字符串”ShortComT”為短連接,字符串”ShortComF”為長連接。
//跟服務(wù)器Socket連接,建立長連接
public static Socket CreatLongLink(){
c.Connect(ipe);//連接到服務(wù)器
byte[] sendbytes = Encoding.Default.GetBytes("ShortComF");
c.Send(sendbytes, sendbytes.Length, 0);
return c;
}//關(guān)閉長連接,服務(wù)器切換至短連接
public static void CloseLongLink(Socket c){
//發(fā)送一個(gè)短連接命令
Byte[] sendbytes = Encoding.Default.GetBytes("ShortComT");
c.Send(sendbytes, sendbytes.Length, 0);
c.Close();}
5 Socket服務(wù)器端的性能改進(jìn)
服務(wù)器端的設(shè)計(jì)采用長/短連接可切換的方式,這樣可以根據(jù)系統(tǒng)的實(shí)際需求,來采用具體的連接方式,在實(shí)際項(xiàng)目中,有時(shí)候數(shù)據(jù)的傳輸比較少,有時(shí)候傳輸又比較頻繁。為了提高服務(wù)器的性能,節(jié)約網(wǎng)絡(luò)資源,設(shè)計(jì)了一種長/短連接可切換的模型。要注意異常處理代碼的編寫。
private Thread _listenerThread = null; //定義線程,用于開啟服務(wù)器Socket監(jiān)聽
private Socket _serverSocket = null; //定義服務(wù)器Socket對(duì)象
private bool shortCom = true; //定義長/短連接標(biāo)志,Socket通信默認(rèn)為短連接
//包含應(yīng)用程序連接到主機(jī)上的服務(wù)所需的主機(jī)和本地或遠(yuǎn)程端口信息。通過組合服務(wù)的主機(jī) IP 地址和端口號(hào)
IPEndPoint iep =new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);
//線程運(yùn)行定義的方法,主要是開啟服務(wù)器Socket監(jiān)聽
_listenerThread = new Thread(new ThreadStart(SocketServerRun));
_listenerThread.Start();
//定義方法,開啟服務(wù)器Socket監(jiān)聽,通過對(duì)客戶端發(fā)送的連接標(biāo)志進(jìn)行判斷來切換長/短連接
private void SocketServerRun(){
//創(chuàng)建基于TCP協(xié)議的服務(wù)器端Socket對(duì)象
_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_serverSocket.Bind(iep);//綁定端口及IP
_serverSocket.Listen(0);//開始監(jiān)聽
try{do{//本層循環(huán)為了不斷地監(jiān)聽客戶端的Socket連接請(qǐng)求
Socket _clientSocket= _serverSocket.Accept();//阻塞直到有客戶端Sokcet請(qǐng)求
do{//本層循環(huán)用于長連接,由長/短連接標(biāo)志變量控制
byte[] recvCMDBytes = new byte[1024];
string recvCMDStr += Encoding.Default.GetString(recvCMDBytes, 0, _clientSocket.Receive(recvCMDBytes, recvCMDBytes.Length, 0)); //從客戶端接受信息
if (recvCMDStr.Length > 0){
switch (recvCMDStr){
case "ShortComT": //短連接命令
shortCom = true;
break;
case "ShortComF"://長連接
shortCom = false;
break;
case ……: //其他
…… //處理事務(wù)
break;
}}} while (!shortCom);
//斷開客戶端的連接
_clientSocket.Close();
} while (true);
}catch (Exception ex){ //異常出現(xiàn),重啟服務(wù)器的Socket監(jiān)聽
shortCom = true; //默認(rèn)線程短連接
_serverSocket.Close();//Socket關(guān)閉
_listenerThread = new Thread(new ThreadStart(SocketServerRun));// 重啟服務(wù)器的Socket監(jiān)聽
_listenerThread.Start();
}}
6 總結(jié)
本文介紹了TCP/IP網(wǎng)絡(luò)編程的原理及Socket的相關(guān)知識(shí),提出了服務(wù)器端的Socket連接方式的優(yōu)化方案,讀者可以針對(duì)實(shí)際項(xiàng)目的需求,合理地選擇Socket服務(wù)器端的連接類型,同時(shí)本文還提供了較實(shí)用的參考代碼。使用長/短連接切換技術(shù)實(shí)現(xiàn)了特定場合C/S架構(gòu)下Socket編程的需求。對(duì)相關(guān)開發(fā)人員有一定的啟發(fā)意義。
參考文獻(xiàn):
[1] 謝希仁.計(jì)算機(jī)網(wǎng)絡(luò)[M].北京:電子工業(yè)出版社,2013(6).
[2] 陳浩,張偉.基于java socket的TCP/IP網(wǎng)絡(luò)編程 [J].計(jì)算機(jī)光盤軟件與應(yīng)用,2013(1).
[3] http://blog.csdn.net/shanliangliuxing/article/details/7743917.
[4] 羅亞非.基于TCP的Socket多線程通信[J].電腦知識(shí)與技術(shù),2009(2).
[5] 劉駿,顏鋼鋒.基于Socket的網(wǎng)絡(luò)編程技術(shù)及其實(shí)現(xiàn)[J].江南大學(xué)學(xué)報(bào),2004(3).