C++基於TCP和UDP的socket通訊

TCP和UDP屬於傳輸層協議。其中TCP提供IP環境下的數據可靠傳輸,它事先爲要發送的數據開闢好鏈接通道(三次握手),而後再進行數據發送;而UDP則不爲IP提供可靠性,通常用於實時的視頻流傳輸,像rtp、rtsp就是創建在udp的基礎上的。node

     首先談談tcp socket編程

    tcp簡單的三次握手過程如圖,windows

    SYN(Synchronize Sequence Numbers):同步標誌  服務器

    ACK(Acknowledgement Number)        :確認標誌網絡

    圖中能夠看出,三次握手的過程是在c的connect()和s的bind()、listen()、accept()函數中完成的,這樣開闢了相對可靠的鏈接通道,來傳輸數據。socket

UDP的socket編程過程以下圖所示:tcp

下面翠花上代碼啦!大笑函數

服務端:spa

[cpp]  view plain  copy
 
  1. #include <stdio.h>  
  2. #include <Winsock2.h> //windows socket的頭文件  
  3.   
  4. #pragma comment( lib, "ws2_32.lib" )// 連接Winsock2.h的靜態庫文件  
  5.   
  6. void main()  
  7. {  
  8.     //初始化winsocket  
  9.     WORD wVersionRequested;  
  10.     WSADATA wsaData;  
  11.     int err;  
  12.   
  13.     wVersionRequested = MAKEWORD( 1, 1 );//第一個參數爲低位字節;第二個參數爲高位字節  
  14.   
  15.     err = WSAStartup( wVersionRequested, &wsaData );//對winsock DLL(動態連接庫文件)進行初始化,協商Winsock的版本支持,並分配必要的資源。  
  16.     if ( err != 0 )  
  17.     {  
  18.         return;  
  19.     }  
  20.   
  21.     if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )//LOBYTE()取得16進制數最低位;HIBYTE()取得16進制數最高(最左邊)那個字節的內容        
  22.     {  
  23.         WSACleanup( );  
  24.         return;  
  25.     }  
  26.   
  27.     SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//建立socket。AF_INET表示在Internet中通訊;SOCK_STREAM表示socket是流套接字,對應tcp;0指定網絡協議爲TCP/IP  
  28.   
  29.     SOCKADDR_IN addrSrv;   
  30.     addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY); //htonl用來將主機字節順序轉換爲網絡字節順序(to network long)  
  31.     //INADDR_ANY就是指定地址爲0.0.0.0的地址,  
  32.     //表示不肯定地址,或「任意地址」。」  
  33.     addrSrv.sin_family=AF_INET;   
  34.     addrSrv.sin_port=htons(4000);//htons用來將主機字節順序轉換爲網絡字節順序(to network short)  
  35.   
  36.     bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//將本地地址綁定到所建立的socket上,以使在網絡上標識該socket  
  37.   
  38.     listen(sockSrv,5);//socket監聽,準備接受鏈接請求。  
  39.   
  40.     SOCKADDR_IN addrClient;  
  41.     int len=sizeof(SOCKADDR);  
  42.   
  43.     while(1)  
  44.     {  
  45.         SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);//爲一個鏈接請求提供服務。addrClient包含了發出鏈接請求的客戶機IP地址信息;返回的新socket描述服務器與該客戶機的鏈接  
  46.   
  47.         char sendBuf[50];  
  48.         sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(addrClient.sin_addr));//inet_ntoa網絡地址轉換轉點分十進制的字符串指針  
  49.         send(sockConn,sendBuf,strlen(sendBuf)+1,0);  
  50.   
  51.         char recvBuf[50];  
  52.         recv(sockConn,recvBuf,50,0);  
  53.         printf("%s\n",recvBuf);  
  54.   
  55.         closesocket(sockConn);  
  56.         Sleep(2000);//2000毫秒  
  57.     }  
  58.     WSACleanup();  
  59. }  

 

客戶端:.net

 

[cpp]  view plain  copy
 
  1. #include <stdio.h>  
  2. #include <Winsock2.h>  
  3.   
  4. #pragma comment( lib, "ws2_32.lib" )   
  5.   
  6.   
  7. void main()  
  8. {  
  9.     WORD wVersionRequested;  
  10.     WSADATA wsaData;  
  11.     int err;  
  12.   
  13.     wVersionRequested = MAKEWORD( 1, 1 );//第一個參數爲低位字節;第二個參數爲高位字節  
  14.   
  15.     err = WSAStartup( wVersionRequested, &wsaData );//對winsock DLL(動態連接庫文件)進行初始化,協商Winsock的版本支持,並分配必要的資源。  
  16.     if ( err != 0 )  
  17.     {  
  18.         return;  
  19.     }  
  20.   
  21.     if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )//LOBYTE()取得16進制數最低位;HIBYTE()取得16進制數最高(最左邊)那個字節的內容        
  22.     {  
  23.         WSACleanup( );  
  24.         return;  
  25.     }  
  26.     for(int index=0;;index++)  
  27.     {  
  28.         SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);  
  29.   
  30.         SOCKADDR_IN addrClt;//須要包含服務端IP信息  
  31.         addrClt.sin_addr.S_un.S_addr=inet_addr("192.168.0.30");// inet_addr將IP地址從點數格式轉換成網絡字節格式整型。  
  32.         addrClt.sin_family=AF_INET;   
  33.         addrClt.sin_port=htons(4000);  
  34.   
  35.         connect(sockClient,(SOCKADDR*)&addrClt,sizeof(SOCKADDR));//客戶機向服務器發出鏈接請求  
  36.         char recvBuf[50];  
  37.         recv(sockClient,recvBuf,50,0);  
  38.         printf("my reply is : %s\n",recvBuf);  
  39.   
  40.         char sendBuf[50];  
  41.         sprintf(sendBuf,"%3d,",index);  
  42.         strcat(sendBuf,"server node of: yaopeng");  
  43.         send(sockClient,sendBuf,strlen(sendBuf)+1,0);  
  44.   
  45.         closesocket(sockClient);  
  46.         Sleep(2000);  
  47.     }  
  48.     WSACleanup();  
  49. }  



 

對於tcp socket,有幾點須要注意:

1、TCP的TIME_WAIT狀態(等待客戶端的相應)    

注*  TIME_WAIT 狀態最大保持時間是2 * MSL,也就是1-4分鐘(MSL是最大分段生存期,指明TCP報文在Internet上最長生存時間)

    當服務器端socket綁定本地地址並佔用了端口,此時若是匆忙結束;或者鏈接的服務器異常退出,這個時候被佔用的端口不能立刻釋放,須要TIME_WAIT。即使調用closesocket()通常也不會當即關閉socket,仍可繼續重用該socket。因此從新啓動服務器時可能會出現問題。例如MFC中在子窗口中實現socket通訊,那麼關閉子窗口再打開就會出問題了。

     解決方法是在bind()以前添加setsockopt()函數,解除端口綁定。

介紹setsockopt()以前咱們再來回顧一下三次握手協議的具體流程:

第一次握手:創建鏈接時,客戶端發送syn包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認; 
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態; 
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。 

完成三次握手,客戶端與服務器開始傳送數據。

setsockopt()使用方法以下:

    1. 若是在已經處於 ESTABLISHED狀態下的socket(通常由端口號和標誌符區分)調用closesocket(通常不會當即關閉而經歷TIME_WAIT的過程)後想繼續重用該socket:
BOOL bReuseaddr=TRUE;
setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));

    2. 若是要已經處於鏈接狀態的soket在調用closesocket後強制關閉,不經歷TIME_WAIT的過程:
BOOL  bDontLinger = FALSE; 
setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));

更多setsockopt()函數用例可參考百度百科:http://baike.baidu.com/view/569217.htm

 

 2、對於大型文件,通常須要將其剁碎了一部分一部分的傳。TCP不能保證接收方順序的收到包,對於須要實時顯示的文件能夠在發送方發出包後設置來自接收方的響應,即對方收到前一個包後再發送下一個包。

目前就這麼多,各位看官有其餘的注意事項拜託請留言補充,小弟感激啊。

 

下面簡單說下UDP socket

    UDP不能保證雙方的可靠鏈接,容易出現丟包現象。

    UDP的socket編程過程以下圖所示:

 

上代碼了,哈哈。

服務端:

[cpp]  view plain  copy
 
  1. #include <stdio.h>  
  2. #include <Winsock2.h>  
  3.   
  4. #pragma comment( lib, "ws2_32.lib" )   
  5.    
  6. void main()  
  7. {  
  8.     WORD wVersionRequested;  
  9.     WSADATA wsaData;  
  10.     int err;  
  11.   
  12.     wVersionRequested = MAKEWORD( 1, 1 );   
  13.   
  14.     err = WSAStartup( wVersionRequested, &wsaData );   
  15.     if ( err != 0 ) {  
  16.         return;  
  17.     }  
  18.   
  19.     if ( LOBYTE( wsaData.wVersion ) != 1 ||  
  20.         HIBYTE( wsaData.wVersion ) != 1 ) {   
  21.             WSACleanup( );  
  22.             return;  
  23.     }  
  24.     SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0);  
  25.       
  26.     int len=sizeof(SOCKADDR);  
  27.       
  28.     SOCKADDR_IN from;     
  29.     SOCKADDR_IN local;   
  30.     local.sin_addr.S_un.S_addr=htonl(INADDR_ANY);   
  31.     local.sin_family=AF_INET;   
  32.     local.sin_port=htons(27015);   
  33.   
  34.     int a = bind(sockSrv,(SOCKADDR*)&local,len);  
  35.    
  36.       
  37.   
  38.     while(1)  
  39.     {  
  40.         char recvBuf[50];  
  41.         recvfrom(sockSrv,recvBuf,50,0,(SOCKADDR*)&from,&len);//from收到客戶端的IP信息  
  42.         printf("%s\n",recvBuf);  
  43.         printf("%s\n",inet_ntoa(local.sin_addr));  
  44.         char sendBuf[50];  
  45.         sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(from.sin_addr));    
  46.         sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&from,len);  
  47.           
  48.         Sleep(2000);  
  49.     }  
  50.     closesocket(sockSrv);  
  51.     WSACleanup();  
  52. }  


客戶端:

[cpp]  view plain  copy
 
  1. #include <stdio.h>  
  2. #include <Winsock2.h>  
  3.   
  4. #pragma comment( lib, "ws2_32.lib" )   
  5.   
  6.   
  7. void main()  
  8. {  
  9.     WORD wVersionRequested;  
  10.     WSADATA wsaData;  
  11.     int err;  
  12.   
  13.     wVersionRequested = MAKEWORD( 1, 1 );   
  14.   
  15.     err = WSAStartup( wVersionRequested, &wsaData );   
  16.     if ( err != 0 ) {  
  17.         return;  
  18.     }  
  19.   
  20.     if ( LOBYTE( wsaData.wVersion ) != 1 ||  
  21.         HIBYTE( wsaData.wVersion ) != 1 ) {   
  22.             WSACleanup( );  
  23.             return;  
  24.     }  
  25.       
  26.   
  27.     for(int index=0;;index++)  
  28.     {  
  29.         SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);  
  30.   
  31.         int len = sizeof(SOCKADDR);  
  32.   
  33.         SOCKADDR_IN local;  
  34.         local.sin_addr.S_un.S_addr=inet_addr("192.168.0.30");   
  35.         local.sin_family=AF_INET;   
  36.         local.sin_port=htons(27015);   
  37.    
  38.         char sendBuf[30];  
  39.         sprintf(sendBuf,"%3d,",index);  
  40.         strcat(sendBuf,"server node of: yaopeng");  
  41.         sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&local,len);  
  42.   
  43.         char recvBuf[50];  
  44.         recvfrom(sockClient,recvBuf,50,0,(SOCKADDR*)&local,&len);  
  45.         printf("my reply is : %s\n",recvBuf);  
  46.         printf("%s\n",inet_ntoa(local.sin_addr));  
  47.   
  48.         closesocket(sockClient);  
  49.         Sleep(2000);  
  50.         WSACleanup();  
  51.     }  
  52. }  

 
   完了。關於socket編程還有不少函數沒有涉及,急待跟進完善。歡迎你們給我留言!大笑

相關文章
相關標籤/搜索