日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

(转)API SOCKET基础(一) TCP建立连接并通信

發布時間:2023/12/18 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (转)API SOCKET基础(一) TCP建立连接并通信 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

寫這篇日志,并不是要記錄令人眼前一亮的算法,只是為了本人健忘的腦袋做一點準備。

要進行網絡通信編程,就要用到socket(套接字),下面以TCP為例展示如何利用socket通信。

要 進行socket編程,首先要為工程鏈接導入庫文件 ws2_32.lib ,然后添加頭文件 #include <Winsock2.h> ,然后在App類的InitInstance()函數里面加載套接字庫,加載套接字庫的代碼可查看MSDN里WSAStartup函數頁面下端 example的代碼,在加載套接字庫的代碼里面有一句wVersionRequested = MAKEWORD( 2, 2 );這句是指定采用2.2版本的套接字庫,可根據需要修改為其他版本的套接字庫。

?

1.TCP下的socket通信:

?? TCP是面向鏈接的通信,通信的socket雙方中必須有一個是服務器端socket,另一端是客戶端socket。下面用代碼來展示服務器端socket和客戶端socket是如何建立鏈接并通信的。

服務器端連接過程:

1. 使用socket函數創建一個服務器端socket:

?? SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);

?? 如上用socket函數創建了一個名字叫sockSrv的socket,socket函數的第二個參數指定了這個是什么類型的socket,如果是TCP類型的socket則為SOCK_STREAM,如果是UDP類型的socket則為SOCK_DGRAM。

?

2.創建一個地址結構體,為地址結構體指定地址,然后使用bind函數把socket和地址綁定:


SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);

bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

如上,創建了一個名字叫addrSrv的地址結構體,addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
是 指定IP地址為本機的IP地址,addrSrv.sin_port=htons(6000);是指定端口為6000端口,一定要使用1024以上的端口 號,1024以下的端口后是系統保留的。在這里有兩個函數htonl和htons,這兩個函數的作用以后再說。然后使用bind函數把sockSrvt和 addrSrv綁定起來。

?

3.設置socket為監聽模式:

listen(sockSrv,5);

使 用listen函數把sockSrv設為監聽模式,第二個參數是等待連接隊列的最大長度。如果設置為SOMAXCONN,那么將這個套接字設置為最大的合 理值。這個值不是在一個端口上同時可以進行連接的數目,例如:如果把參數設置為2,當有3個連接請求同時到來時,前兩個連接請求被放到等待請求連接隊列 中,然后程序依次為這些請求服務,而第三個連接請求就被拒絕了。對多個連接請求的處理不是同時進行的,必須完成請求連接隊列中一個連接請求的連接,才能開 始進行請求連接隊列中下一個連接請求的連接。

?

4.等待客戶端的連接請求到來:

SOCKADDR_IN addrClient;

int len=sizeof(SOCKADDR);

SOCKET sockConn=accept (sockSrv,(SOCKADDR*) &addrClient ,&len);

首 先創建一個地址結構體addrClient ,當有客戶端請求連接sockSrv,accept 函數就會執行,建立連接并把客戶端的IP地址和端口信息記錄到地址結構體addrClient 里。必須注意到,accept函數的返回值是一個socket,在上面的代碼中是SOCKET sockConn,這個名字叫sockConn(名字可由程序員隨便取)的socket有什么用呢?其實當成功完成連接后,與客戶端socket連接的是 sockConn,而不是sockSrv,以后與客戶端socket進行數據傳送的socket也是sockConn,而不是sockSrv,簡單的說, 服務器端socket sockSrv只負責接收連接請求和進行連接操作,當連接操作完成后,與客戶端socket連接的是accpet函數返回的socket,以后與客戶端 socket進行數據傳輸的也是這個accpet函數返回的socket。

?

再來看TCP客戶端是怎樣發起連接請求的。

客戶端編程也是用到socket,因此鏈接導入庫文件,包含頭文件,加載套接字庫也必須先在客戶端進行。

客戶端請求連接過程:

1.使用socket函數創建一個客戶端socket:

SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);

?

2.向服務器端socket發出連接請求:

SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);

connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

上 面創建了一個名字叫addrSrv的地址結構體,然后把服務器端socket的IP地址(假設服務器端IP地址是127.0.0.1)和端口來設置這個地 址結構體,然后通過connect函數向服務器端socket發出連接請求。在設置addrSrv地址結構體的IP地址時用了一個inet_addr函 數,這個函數的作用以后再說。

?

由此可見,客戶端socket并不需要綁定IP地址和端口,為什么服務器端socket需要 綁定而客戶端socket不用綁定呢?如果服務器端socket不綁定IP和端口,客戶端socket又哪知道向哪個IP地址和端口發起連接請求呢?因此 服務器端socket是必須綁定IP端口的。而客戶端socket不需要用bind函數綁定端口,系統會自動為socket綁定一個隨機的端口,服務器只 要用accept函數返回的socket與客戶端socket交換數據就行了,如果服務器需要查詢客戶端socket的IP和端口,可以查看accept 函數的第二個參數記錄的客戶端socket地址信息。

?

?

連接的操作在這里完成了,然后是進行數據的傳輸:

發送數據使用send函數:

int send(

SOCKET s,??????????????????? //要發送數據的socket,例如設置為服務器端的sockConn,則數據會發送到

????????????????????????????????????????? 與sockConn相連接的客戶端socket sockClient上去。相反若設置為sockClient,

????????????????????????????????????????? 則數據發送到與sockClient相連接的服務器端socket sockConn上去。

const char FAR *buf,???? //要發送數據buf的地址。

int len,?????????????????????????? //要發送數據buf的長度

int flags???????????????????????? //一般設置為0即可。

);

?

?

接收數據使用recv函數:

int recv(

SOCKET s,?????????????? //要接收數據的socket,例如設置為服務器端的sockConn,則接收與sockConn相

???????????????????????????????????? 連接的客戶端socket sockClient發送的數據。相反若設置為sockClient,則接收

???????????????????????????????????? 與sockClient相連接的服務器端socket sockConn發送的數據。

char FAR *buf,???????? //要發送數據buf的地址。

int len,???????????????????? //要接收數據buf的長度

int flags??????????????????? //一般設置為0即可。

);

?

socket使用完畢后調用closesocket()函數關閉一個socket以回收資源。在程序關閉之前,必須調用WSACleanup函數終止對套接字庫的使用,注意必須在App類(應用程序類)的析構函數中調用WSACleanup函數。

?

接上一篇,建立連接后,服務器端的sockConn與客戶端的sockClient就連接起來并且可以互相傳輸數據了,使用closesocket函數關閉一個socket,socket被關閉,連接也就斷開了。下面是斷開連接后的各種情況。

?

情況1:關閉服務器端sockConn--closesocket(sockConn)之后

關閉服務器端sockConn后,對sockConn使用recv函數接收數據,recv函數會馬上返回SOCKET_ERROR,對sockConn使用send函數發送數據,send函數也會馬上返回SOCKET_ERROR。

之后對客戶端sockClient的情況分析就有點復雜了:

1.sockClient 使用send函數發送數據。第一次send函數能成功返回發送數據的大小,并不會返回SOCKET_ERROR 。雖然sockClient成功send了數據,但sockConn是無法接收到的。但sockClient使用send函數發送數據成功僅限于第一次 send,之后使用send函數返回的都是SOCKET_ERROR 。

2.sockClient使用recv函數接收數據。如果 sockClient使用recv函數之前沒有使用過send函數,那么recv函數的返回值總是0(感覺好像很奇怪,recv函數返回0,難道還有成功 接收到0個字節的數據這種說法?不明白)。直到sockClient調用過send函數之后,recv函數的返回值總是SOCKET_ERROR。

?

?

情況2:關閉客戶端sockClient--closesocket(sockClient)之后

這時結果就和情況1相反:

關閉客戶端sockClient后,對sockClient使用recv函數接收數據,recv函數會馬上返回SOCKET_ERROR,對sockClient使用send函數發送數據,send函數也會馬上返回SOCKET_ERROR。

之后對服務器端sockConn的情況分析也一樣:

1.sockConn 使用send函數發送數據。第一次send函數能成功返回發送數據的大小,并不會返回SOCKET_ERROR 。雖然sockConn成功send了數據,但sockClient是無法接收到的。但sockConn使用send函數發送數據成功僅限于第一次 send,之后使用send函數返回的都是SOCKET_ERROR 。

2.sockConn使用recv函數接收數據。如果sockConn使用recv函數之前沒有使用過send函數,那么recv函數總是返回0。直到sockConn調用過send函數之后,recv函數的返回值總是SOCKET_ERROR。

?

知 道上面兩個斷開連接的情況后就要考慮客戶端和服務器端如何協調關閉連接。TCP連接的雙方Asocket和Bsocket,Asocket想要關閉連 接,Asocket的計算機除了closesocket(Asocket)之外,還要另外通知Bsocket的計算機連接已經斷開了,Bsocket的計 算機收到通知后closesocket(Bsocket)來回收資源,避免Bsocket還在哪里傻傻的接收/發送數據,如果不想這樣明顯通知 Bsocket的計算機連接已斷開,Bsocket也可以嘗試自己判斷,如果Bsocket多次調用send函數總是返回SOCKET_ERROR或者 Bsocket多次調用recv函數總是返回0或SOCKET_ERROR,那就要意識到連接很可能已經斷開了。

?

如果數據的流向是單向的,例如說數據只從Asocket流向Bsocket(類似文件傳輸就是這樣),那么Asocket只會調用send函數,而Bsocket只會調用recv函數,這時候如果其中一方要停止數據的傳輸,就會有兩種情況出現:

1. 如果Asocket的計算機不想發送數據而closesocket(Asocket),由于Bsocket從來不調用send函數,因此Bsocket的 recv函數總是返回0,那么Bsocket的計算機就要意識到Asocket很可能已經關閉了,讓Bsocket的計算機 closesocket(Bsocket)。

?

2.如果Bsocket的計算機不想接收數據而 closesocket(Bsocket),這時Asocket繼續調用send函數發送數據,第一次send還是成功的,但從第二次send開始就總會 返回SOCKET_ERROR,但是Asocket的計算機無法判斷send函數返回SOCKET_ERROR是由Bsocket關閉造成的還是 Asocket關閉造成的(因為Asocket關閉后Asocket調用send函數也是返回SOCKET_ERROR),因此Asocket的計算機無 法判斷Asocket關閉了沒有,簡單的解決方法是如果Bsocket的計算機不想接收數據,先不要關閉Bsocket,而是發通知給Asocket的計 算機告訴它我不想收數據了,Asocket的計算機收到通知后關閉Asockt,這樣情形就回到上面情況1去了,而且也知道Asocket調用send函 數返回SOCKET_ERROR肯定是由于Asockt關閉造成的而不是由Bsocket關閉造成的。

轉載于:https://www.cnblogs.com/blog5258/archive/2013/04/11/socket.html

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的(转)API SOCKET基础(一) TCP建立连接并通信的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。