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
- #include <stdio.h>
- #include <Winsock2.h> //windows socket的頭文件
-
- #pragma comment( lib, "ws2_32.lib" )// 連接Winsock2.h的靜態庫文件
-
- void main()
- {
-
- WORD wVersionRequested;
- WSADATA wsaData;
- int err;
-
- wVersionRequested = MAKEWORD( 1, 1 );
-
- err = WSAStartup( wVersionRequested, &wsaData );
- if ( err != 0 )
- {
- return;
- }
-
- if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )
- {
- WSACleanup( );
- return;
- }
-
- SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);
-
- SOCKADDR_IN addrSrv;
- addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
-
-
- addrSrv.sin_family=AF_INET;
- addrSrv.sin_port=htons(4000);
-
- bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
-
- listen(sockSrv,5);
-
- SOCKADDR_IN addrClient;
- int len=sizeof(SOCKADDR);
-
- while(1)
- {
- SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
-
- char sendBuf[50];
- sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(addrClient.sin_addr));
- send(sockConn,sendBuf,strlen(sendBuf)+1,0);
-
- char recvBuf[50];
- recv(sockConn,recvBuf,50,0);
- printf("%s\n",recvBuf);
-
- closesocket(sockConn);
- Sleep(2000);
- }
- WSACleanup();
- }
客戶端:.net
- #include <stdio.h>
- #include <Winsock2.h>
-
- #pragma comment( lib, "ws2_32.lib" )
-
-
- void main()
- {
- WORD wVersionRequested;
- WSADATA wsaData;
- int err;
-
- wVersionRequested = MAKEWORD( 1, 1 );
-
- err = WSAStartup( wVersionRequested, &wsaData );
- if ( err != 0 )
- {
- return;
- }
-
- if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )
- {
- WSACleanup( );
- return;
- }
- for(int index=0;;index++)
- {
- SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
-
- SOCKADDR_IN addrClt;
- addrClt.sin_addr.S_un.S_addr=inet_addr("192.168.0.30");
- addrClt.sin_family=AF_INET;
- addrClt.sin_port=htons(4000);
-
- connect(sockClient,(SOCKADDR*)&addrClt,sizeof(SOCKADDR));
- char recvBuf[50];
- recv(sockClient,recvBuf,50,0);
- printf("my reply is : %s\n",recvBuf);
-
- char sendBuf[50];
- sprintf(sendBuf,"%3d,",index);
- strcat(sendBuf,"server node of: yaopeng");
- send(sockClient,sendBuf,strlen(sendBuf)+1,0);
-
- closesocket(sockClient);
- Sleep(2000);
- }
- WSACleanup();
- }
對於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編程過程以下圖所示:
上代碼了,哈哈。
服務端:
- #include <stdio.h>
- #include <Winsock2.h>
-
- #pragma comment( lib, "ws2_32.lib" )
-
- void main()
- {
- WORD wVersionRequested;
- WSADATA wsaData;
- int err;
-
- wVersionRequested = MAKEWORD( 1, 1 );
-
- err = WSAStartup( wVersionRequested, &wsaData );
- if ( err != 0 ) {
- return;
- }
-
- if ( LOBYTE( wsaData.wVersion ) != 1 ||
- HIBYTE( wsaData.wVersion ) != 1 ) {
- WSACleanup( );
- return;
- }
- SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0);
-
- int len=sizeof(SOCKADDR);
-
- SOCKADDR_IN from;
- SOCKADDR_IN local;
- local.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
- local.sin_family=AF_INET;
- local.sin_port=htons(27015);
-
- int a = bind(sockSrv,(SOCKADDR*)&local,len);
-
-
-
- while(1)
- {
- char recvBuf[50];
- recvfrom(sockSrv,recvBuf,50,0,(SOCKADDR*)&from,&len);
- printf("%s\n",recvBuf);
- printf("%s\n",inet_ntoa(local.sin_addr));
- char sendBuf[50];
- sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(from.sin_addr));
- sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&from,len);
-
- Sleep(2000);
- }
- closesocket(sockSrv);
- WSACleanup();
- }
客戶端:
- #include <stdio.h>
- #include <Winsock2.h>
-
- #pragma comment( lib, "ws2_32.lib" )
-
-
- void main()
- {
- WORD wVersionRequested;
- WSADATA wsaData;
- int err;
-
- wVersionRequested = MAKEWORD( 1, 1 );
-
- err = WSAStartup( wVersionRequested, &wsaData );
- if ( err != 0 ) {
- return;
- }
-
- if ( LOBYTE( wsaData.wVersion ) != 1 ||
- HIBYTE( wsaData.wVersion ) != 1 ) {
- WSACleanup( );
- return;
- }
-
-
- for(int index=0;;index++)
- {
- SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);
-
- int len = sizeof(SOCKADDR);
-
- SOCKADDR_IN local;
- local.sin_addr.S_un.S_addr=inet_addr("192.168.0.30");
- local.sin_family=AF_INET;
- local.sin_port=htons(27015);
-
- char sendBuf[30];
- sprintf(sendBuf,"%3d,",index);
- strcat(sendBuf,"server node of: yaopeng");
- sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&local,len);
-
- char recvBuf[50];
- recvfrom(sockClient,recvBuf,50,0,(SOCKADDR*)&local,&len);
- printf("my reply is : %s\n",recvBuf);
- printf("%s\n",inet_ntoa(local.sin_addr));
-
- closesocket(sockClient);
- Sleep(2000);
- WSACleanup();
- }
- }
完了。關於socket編程還有不少函數沒有涉及,急待跟進完善。歡迎你們給我留言!