???? 首先談談tcp socket
??? tcp簡單的三次握手過程如圖,
??? SYN(Synchronize Sequence Numbers):同步標志
??? ACK(Acknowledgement Number)????????:確認標志
??? 圖中可以看出,三次握手的過程是在c的connect()和s的bind()、listen()、accept()函數中完成的,這樣開辟了相對可靠的連接通道,來傳輸數據。
UDP的socket編程過程如下圖所示:
下面翠花上代碼啦!
服務端:
- #include?<stdio.h> ??
- #include?<Winsock2.h>?//windows?socket的頭文件 ??
- ??
- #pragma?comment(?lib,?"ws2_32.lib"?)//?鏈接Winsock2.h的靜態庫文件 ??
- ??
- void?main()??
- {??
- ????//初始化winsocket ??
- ????WORD?wVersionRequested;??
- ????WSADATA?wsaData;??
- ????int?err;??
- ??
- ????wVersionRequested?=?MAKEWORD(?1,?1?);//第一個參數為低位字節;第二個參數為高位字節 ??
- ??
- ????err?=?WSAStartup(?wVersionRequested,?&wsaData?);//對winsock?DLL(動態鏈接庫文件)進行初始化,協商Winsock的版本支持,并分配必要的資源。 ??
- ????if?(?err?!=?0?)??
- ????{??
- ????????return;??
- ????}??
- ??
- ????if?(?LOBYTE(?wsaData.wVersion?)?!=?1?||HIBYTE(?wsaData.wVersion?)?!=?1?)//LOBYTE()取得16進制數最低位;HIBYTE()取得16進制數最高(最左邊)那個字節的內容?????? ??
- ????{??
- ????????WSACleanup(?);??
- ????????return;??
- ????}??
- ??
- ????SOCKET?sockSrv=socket(AF_INET,SOCK_STREAM,0);//創建socket。AF_INET表示在Internet中通信;SOCK_STREAM表示socket是流套接字,對應tcp;0指定網絡協議為TCP/IP ??
- ??
- ????SOCKADDR_IN?addrSrv;???
- ????addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);?//htonl用來將主機字節順序轉換為網絡字節順序(to?network?long) ??
- ????//INADDR_ANY就是指定地址為0.0.0.0的地址, ??
- ????//表示不確定地址,或“任意地址”。” ??
- ????addrSrv.sin_family=AF_INET;???
- ????addrSrv.sin_port=htons(4000);//htons用來將主機字節順序轉換為網絡字節順序(to?network?short) ??
- ??
- ????bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//將本地地址綁定到所創建的socket上,以使在網絡上標識該socket ??
- ??
- ????listen(sockSrv,5);//socket監聽,準備接受連接請求。 ??
- ??
- ????SOCKADDR_IN?addrClient;??
- ????int?len=sizeof(SOCKADDR);??
- ??
- ????while(1)??
- ????{??
- ????????SOCKET?sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);//為一個連接請求提供服務。addrClient包含了發出連接請求的客戶機IP地址信息;返回的新socket描述服務器與該客戶機的連接 ??
- ??
- ????????char?sendBuf[50];??
- ????????sprintf(sendBuf,"Welcome?%s?to?here!",inet_ntoa(addrClient.sin_addr));//inet_ntoa網絡地址轉換轉點分十進制的字符串指針 ??
- ????????send(sockConn,sendBuf,strlen(sendBuf)+1,0);??
- ??
- ????????char?recvBuf[50];??
- ????????recv(sockConn,recvBuf,50,0);??
- ????????printf("%s\n",recvBuf);??
- ??
- ????????closesocket(sockConn);??
- ????????Sleep(2000);//2000毫秒 ??
- ????}??
- ????WSACleanup();??
- }??
#include <stdio.h>
#include <Winsock2.h> //windows socket的頭文件
#pragma comment( lib, "ws2_32.lib" )// 鏈接Winsock2.h的靜態庫文件
void main()
{
//初始化winsocket
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );//第一個參數為低位字節;第二個參數為高位字節
err = WSAStartup( wVersionRequested, &wsaData );//對winsock DLL(動態鏈接庫文件)進行初始化,協商Winsock的版本支持,并分配必要的資源。
if ( err != 0 )
{
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )//LOBYTE()取得16進制數最低位;HIBYTE()取得16進制數最高(最左邊)那個字節的內容
{
WSACleanup( );
return;
}
SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//創建socket。AF_INET表示在Internet中通信;SOCK_STREAM表示socket是流套接字,對應tcp;0指定網絡協議為TCP/IP
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY); //htonl用來將主機字節順序轉換為網絡字節順序(to network long)
//INADDR_ANY就是指定地址為0.0.0.0的地址,
//表示不確定地址,或“任意地址”。”
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(4000);//htons用來將主機字節順序轉換為網絡字節順序(to network short)
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//將本地地址綁定到所創建的socket上,以使在網絡上標識該socket
listen(sockSrv,5);//socket監聽,準備接受連接請求。
SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);
while(1)
{
SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);//為一個連接請求提供服務。addrClient包含了發出連接請求的客戶機IP地址信息;返回的新socket描述服務器與該客戶機的連接
char sendBuf[50];
sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(addrClient.sin_addr));//inet_ntoa網絡地址轉換轉點分十進制的字符串指針
send(sockConn,sendBuf,strlen(sendBuf)+1,0);
char recvBuf[50];
recv(sockConn,recvBuf,50,0);
printf("%s\n",recvBuf);
closesocket(sockConn);
Sleep(2000);//2000毫秒
}
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?);//對winsock?DLL(動態鏈接庫文件)進行初始化,協商Winsock的版本支持,并分配必要的資源。 ??
- ????if?(?err?!=?0?)??
- ????{??
- ????????return;??
- ????}??
- ??
- ????if?(?LOBYTE(?wsaData.wVersion?)?!=?1?||HIBYTE(?wsaData.wVersion?)?!=?1?)//LOBYTE()取得16進制數最低位;HIBYTE()取得16進制數最高(最左邊)那個字節的內容?????? ??
- ????{??
- ????????WSACleanup(?);??
- ????????return;??
- ????}??
- ????for(int?index=0;;index++)??
- ????{??
- ????????SOCKET?sockClient=socket(AF_INET,SOCK_STREAM,0);??
- ??
- ????????SOCKADDR_IN?addrClt;//需要包含服務端IP信息 ??
- ????????addrClt.sin_addr.S_un.S_addr=inet_addr("192.168.0.30");//?inet_addr將IP地址從點數格式轉換成網絡字節格式整型。 ??
- ????????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();??
- }??
#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 );//對winsock DLL(動態鏈接庫文件)進行初始化,協商Winsock的版本支持,并分配必要的資源。
if ( err != 0 )
{
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )//LOBYTE()取得16進制數最低位;HIBYTE()取得16進制數最高(最左邊)那個字節的內容
{
WSACleanup( );
return;
}
for(int index=0;;index++)
{
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrClt;//需要包含服務端IP信息
addrClt.sin_addr.S_un.S_addr=inet_addr("192.168.0.30");// inet_addr將IP地址從點數格式轉換成網絡字節格式整型。
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,有幾點需要注意:
一、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
?
?二、對于大型文件,一般需要將其剁碎了一部分一部分的傳。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);//from收到客戶端的IP信息 ??
- ????????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;
}
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);//from收到客戶端的IP信息
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();??
- ????}??
- }??