


版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、Winsocket 入門(mén)教程一:多線(xiàn)程 阻塞式服務(wù)器和阻塞式客戶(hù)端 程序(TCP)Winsocket 入門(mén)教程一:多線(xiàn)程阻塞式服務(wù)器和阻塞式客戶(hù)端程序 (TCP) 收藏最近因?yàn)楣ぷ餍枰獙W(xué)習(xí)了 Winsocket 客 戶(hù)端服務(wù)器模型程序的設(shè)計(jì)。在學(xué)習(xí)的過(guò)程中, 我發(fā)現(xiàn)學(xué)習(xí) Winsocket 的資料不多并且十分 的零散。我一直沒(méi)有找到一本學(xué)習(xí) Winsocke t 方面的經(jīng)典國(guó)外著作。而且這些資料中并沒(méi)有 提供源代碼文件, 所以我只有將這些源代碼在自 己敲一遍。 在敲代碼的過(guò)程中, 我發(fā)現(xiàn)了這些源 代碼中的一些錯(cuò)誤的地方和一些已經(jīng)過(guò)時(shí)的 Wi ndows 程序的輸寫(xiě)方法 (Win16?) ?,F(xiàn)將
2、學(xué) 習(xí)經(jīng)驗(yàn)和通過(guò)閱讀各種資料總結(jié)出來(lái)的模型以 及代碼分享出來(lái)。希望對(duì)學(xué)習(xí) Winsocket 的 初學(xué)者有一定的幫助。我們首先來(lái)了解一下什么是 Winsocke t 。 Winsocket是 unix/linux 下的 berkeley socket 在 Windows 下的實(shí)現(xiàn)。 unix/l inux 下的 berkeley socket 是網(wǎng)絡(luò)通訊方 面的基石,應(yīng)用程序通過(guò)調(diào)用 berkeley soc ket 的 API 進(jìn)行相互通訊, berkeley sock et 則利用具體的網(wǎng)絡(luò)通訊協(xié)議和操作系統(tǒng)的調(diào) 用來(lái)為我們完成具體的通訊工作。 Winsocket保留了 berkeley
3、socket 的所有內(nèi)容,并且 為了其能在 Win32 消息機(jī)制和多線(xiàn)程的環(huán)境 下更好的工作。 Winsocket 在 berkeley so cket 原有的基礎(chǔ)上對(duì)其進(jìn)行了擴(kuò)充。如我們可 以利用 WSAAsyncSelect對(duì) Socket 消息進(jìn)行訂閱,以及使用 WSAGetLastError 對(duì)多 線(xiàn)程環(huán)境下的 Winsocket 錯(cuò)誤進(jìn)行捕獲。接著再讓我們來(lái)了解一下服務(wù)器 客戶(hù)端 應(yīng)用程序模型。該模型是構(gòu)建分布式系統(tǒng)的模型 之一。服務(wù)器程序一直處于監(jiān)聽(tīng)的狀態(tài), 等待客 戶(hù)端程序的連接。 客戶(hù)端程序像服務(wù)器程序發(fā)送 連接請(qǐng)求, 服務(wù)器程序接受該連接請(qǐng)求, 同時(shí)與 客戶(hù)端程序建立連接。
4、此時(shí)客戶(hù)端程序就可以向 服務(wù)器發(fā)送具體的請(qǐng)求, 獲取相關(guān)的數(shù)據(jù)。 服務(wù) 器 客戶(hù)端模型有三種連接方式,一種是面向連 接的 (TCP) ,面向連接的服務(wù)是一種可靠的服 務(wù),它通過(guò)數(shù)據(jù)流進(jìn)行數(shù)據(jù)的傳輸, 面向連接的 服務(wù)實(shí)現(xiàn)了無(wú)差錯(cuò)無(wú)重復(fù)的順尋數(shù)據(jù)發(fā)送。 一種 是面向無(wú)連接的 (UDP) ,面向無(wú)連接的服務(wù)是 一種不可靠的服務(wù),它通過(guò)數(shù)據(jù)報(bào)進(jìn)行數(shù)據(jù)傳 輸,由于數(shù)據(jù)報(bào)進(jìn)行傳輸時(shí)的順序是無(wú)序的, 所 以它是不可靠的服務(wù)。最后一種是多播的方式,及服務(wù)器程序主動(dòng)向多個(gè)客戶(hù)端程序發(fā)送信息。 面向連接的服務(wù)器 客戶(hù)端應(yīng)用程序模型的程 序流程圖如下所示:在此模型的阻塞模式中, 服務(wù)端程序在執(zhí)行 a ccept
5、操作、客戶(hù)端程序 connect 操作、以及 服務(wù)端 客戶(hù)端在進(jìn)行 read 和 write 操作時(shí), 如果這些操作既沒(méi)有成功也沒(méi)有失敗, 應(yīng)用程序 會(huì)在執(zhí)行這些操作的地方一直阻塞著。 所以我們 應(yīng)該在服務(wù)端應(yīng)用程序的主線(xiàn)程中不停的調(diào)用 accept 操作,以使服務(wù)端程序能不停地接受客 戶(hù)端程序發(fā)送過(guò)來(lái)的連接請(qǐng)求。 而在接受了一個(gè) 客戶(hù)端的連接請(qǐng)求后, 我們應(yīng)改為每一個(gè)接受的 連接請(qǐng)求開(kāi)辟一個(gè)專(zhuān)門(mén)的線(xiàn)程來(lái)接受客戶(hù)端程 序發(fā)送的請(qǐng)求以及為具體的請(qǐng)求返回特定的信 息。根據(jù)以上的程序流程圖以及說(shuō)明,我們 可以寫(xiě)出以下的服務(wù)端程序源代碼: view plaincopy to clipboardprin
6、t?1. /2./ file ServerMultThreadServerMultThread.cpp4. / brief 阻塞式多線(xiàn)程服務(wù)器程序。 每 當(dāng)客戶(hù)端程序請(qǐng)求與服務(wù)端連接時(shí),服務(wù)端 程序開(kāi)放一個(gè)線(xiàn)程接受客戶(hù)端程序的請(qǐng)求5. / 并且向客戶(hù)端回饋請(qǐng)求的信息。 客戶(hù)端請(qǐng)求的信息輸出到控制臺(tái)中 .6. /7. #include <iostream>8. #include <cassert>9. #include <WinSock2.h>10. #include <process.h>11. #pragma comment(lib, &quo
7、t;ws2_32.lib" )12. #define ASSERT assert13. #define THREAD HANDLE14. #define EVENT HANDLE15. #define CloseThread CloseHandle16. #define CloseEvent CloseHandle17. using std:cin;18. using std:cout;19. using std:endl;20. /21. / struct tagServerRecv22. /23. / brief線(xiàn)程函數(shù)參數(shù)結(jié)構(gòu)體, 其中包含已建立連接的 socket.24./2
8、5./ author Shining10026./ date 2010-05-1827./28. typedef struct tagServerRecv30. SOCKET skAccept; / 已 建立連接的 socket31. CRITICAL_SECTION *pcs; / 同步控制臺(tái)輸出的臨界區(qū)32. EVENT e; / 保證結(jié) 構(gòu)體各個(gè)字段在結(jié)構(gòu)體字段改變之前將其拷 貝到線(xiàn)程中的信號(hào)量33. THREAD t; / 當(dāng)前線(xiàn) 程的內(nèi)核對(duì)象34. DWORD dwThreadID; / 當(dāng)前線(xiàn)程的 ID35. SERVER_RECV, *PSERVER_RE CV;36. /37.
9、 / fn static int ServerRecv(L PVOID lParam)38. /39. / brief 服務(wù)器與建立連接的客戶(hù) 端進(jìn)行通訊 .40. /41. / author Shining10042. / date 2010-05-1843. /44. / param lParam 線(xiàn)程函數(shù)參 數(shù), 詳細(xì)信息見(jiàn)上面說(shuō)明 .45. /46. / return 總是返回 0.47. /48. static int ServerRecv(LPVOID= 1000Param);int c_iPort49.static const1;50.int main()51.52.int iR
10、et= SOCKET_ERROR;53. / 初始化 Winsocket ,所有 Win socket 程序必須先使用 WSAStartup 進(jìn) 行初始化54. WSADATA data;55. ZeroMemory(&data, sizeof(WSADATA);56. iRet = WSAStartup(MAKEW ORD(2, 0), &data);57. ASSERT(SOCKET_ERROR != i Ret);58. / 建立服務(wù)端程序的監(jiān)聽(tīng)套接字59. SOCKET skListen = INVALID_SOCKET;60. skListen = socket(AF
11、_INET, SOCK_STREAM, 0);61. ASSERT(INVALID_SOCKET != skListen);62. / 初始化監(jiān)聽(tīng)套接字地址信息63. sockaddr_in adrSer v; / 表示網(wǎng)絡(luò)地址64. ZeroMemory(&adrServ, size of(sockaddr_in);65. adrServ.sin_family = AF_INET; / 初始化地址格式,只能為 AF_INET66. adrServ.sin_port= htons(c_iPort); / 初始化端口,由于網(wǎng)絡(luò)字 節(jié)順序和主機(jī)字節(jié)順序相反, 所以必須使用 h tons 將
12、主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序67. = I NADDR_ANY; / 初始化 IP ,由于 是服務(wù)器程序,所以可以將 INADDR_AN Y 賦給該字段,表示任意的 IP68. / 綁定監(jiān)聽(tīng)套接字到本地69. iRet = bind(skListen, (sockaddr*)&adrServ, sizeof(sockaddr_i n);70. ASSERT(SOCKET_ERROR != iRet);71. / 使用監(jiān)聽(tīng)套接字進(jìn)行監(jiān)聽(tīng)72. iRet = listen(skListen, SOMAXCONN); / SOMAXCONN 表示可以 連接到該程序的最大連接數(shù)73. ASS
13、ERT(SOCKET_ERROR != iRet);74. / 輸出控制臺(tái)緩沖區(qū),由于可能有 多個(gè)客戶(hù)端程序可能同時(shí)向緩沖區(qū)發(fā)送請(qǐng)求 信息75. / 為了保證輸出時(shí)能夠一次性完整 的輸出完一個(gè)客戶(hù)端的請(qǐng)求信息,所以在輸 出客76. / 戶(hù)程序的信息到控制臺(tái)時(shí),必須 使用臨界區(qū)阻塞其它線(xiàn)程77. CRITICAL_SECTION cs;78. InitializeCriticalSection(&cs);79. / 保證結(jié)構(gòu)體各個(gè)字段在結(jié)構(gòu)體字段改變之前將其拷貝到線(xiàn)程中的信號(hào)量80. / 因?yàn)楫?dāng)該結(jié)構(gòu)體拷貝到線(xiàn)程中之前, 有可能有新的連接到來(lái)并改變了結(jié)構(gòu)體 的值81. / 所以我們必須先
14、保證值拷貝過(guò)后 再接受連接82. EVENT e = NULL;83. e = CreateEvent(NULL, FALSE, FALSE, NULL);84. ASSERT(NULL != e);85. for(;)86. 87. / 客戶(hù)端向服務(wù)器端發(fā)送連接請(qǐng) 求,服務(wù)器端接受客戶(hù)端的連接88. SOCKET skAccept = INVALID_SOCKET;89. sockaddr_in adrClit;90. ZeroMemory(&adrClit, sizeof(sockaddr_in);91. int iLen = sizeof(sockaddr_in);92. skA
15、ccept = accept(skListen, (sockaddr*)&adrClit, &iLen); / 如果沒(méi)有客戶(hù)端程序請(qǐng)求連接,服務(wù)端程 序會(huì)一直阻塞在這里等待連接93. ASSERT(INVALID_SOCKET != skAccept);94. SERVER_RECV sr;95. / 成功創(chuàng)建連接后創(chuàng)建一個(gè)獨(dú)立 的線(xiàn)程應(yīng)答客戶(hù)請(qǐng)求,以防止應(yīng)用程序因?yàn)?阻塞無(wú)法應(yīng)答新的客戶(hù)請(qǐng)求96. / 我們應(yīng)該先將線(xiàn)程掛起,以便 我們能夠在線(xiàn)程執(zhí)行之前初始化線(xiàn)程所需要 的結(jié)構(gòu)體變量中的各個(gè)字段97. THREAD hThread = NULL;98. DWORD dwThre
16、adID = 0;99. hThread = CreateThread(NULL, 0, (LPTHREAD_START_RO UTINE)ServerRecv,100. &sr, CREATE_SUSPENDED, &dwThreadID);101.ASSERT(NULL != hThread);102./ 初始化結(jié)構(gòu)體字段103.sr.skAccept = skAccept;104.sr.pcs = &cs;105.sr.e = e;106. sr.t = hThread;107. sr.dwThreadID = dwThreadID;108. / 啟動(dòng)線(xiàn)程109.
17、 DWORD dwRet = ResumeThread(hThread);110. ASSERT(-1 != dwRet);111.112. / 保證結(jié)構(gòu)體被拷貝到線(xiàn)程中后 再應(yīng)答新的連接113. dwRet = WaitForSingleObject (e, INFINITE);114. ASSERT(WAIT_FAILED != dwRet);115. 116. / 清理線(xiàn)程同步資源117. DeleteCriticalSection(&cs);118. BOOL bRet = FALSE;119. bRet = CloseEvent(e);120. ASSERT(bRet);12
18、1. / 關(guān)閉該套接字的連接122. iRet = shutdown(skListen, S D_SEND);123.ASSERT(SOCKET_ERROR != iRet);124./ 清理該套接字的資源125.iRet = closesocket(skListen);126.ASSERT(SOCKET_ERROR != iRet);127./ 清理 Winsocket 資源128.iRet = WSACleanup();129.ASSERT(SOCKET_ERROR != iRet);130.cin.get();131.return 0;132. 133. int ServerRecv(
19、LPVOID lParam)134. 135./ 拷貝結(jié)構(gòu)體各個(gè)字段到線(xiàn)程中136.PSERVER_RECV psr = (PSERVER_RECV)lParam;137.SERVER_RECV sr= 138.psr->skAccept,139.psr->pcs,140.psr->e,141.psr->t,142.psr->dwThreadID143.;144./ 設(shè)置信號(hào)量,使主線(xiàn)程能夠接受新的連接145.BOOLbRet = FALSE;146.bRet =SetEvent(sr.e);147.ASSERT(bRet);148.constint c_iBuf
20、Len =512;149.charszBufc_iBufLen + 1= '0'150.constchar c_szPrefix = "Server recv:"151.constint c_iPrefLen= strlen(c_szPrefix);152.char szRelyc_iBufLen + 16 + 1 = '0'153. strcpy(szRely, c_szPrefix);154. int iRet = SOCKET_ERROR;155. for(;)156. 157. iRet = recv(sr.skAccept, szB
21、uf, c_iBufLen, 0); /接收客戶(hù)端發(fā)送的信息, 如果客戶(hù)端不發(fā)送信息,則線(xiàn)程 會(huì)阻塞到此處158. if(0 = iRet) /客戶(hù)端優(yōu)雅的關(guān)閉了此連接159. 160. cout << "Connection " << sr.dwThreadID << " shutdown." << endl;161. break;162. 163. else if(SOCKET_ERROR = iRet) / 客戶(hù)端粗魯?shù)年P(guān)閉了此連接或 者接受信息出錯(cuò)164. n " << sr.
22、dwThreadID << " recv e rror." << endl;166.break;167.168.szBufiRet = '0'169.EnterCriticalSection(sr.pcs);170.cout << "Connection " << sr.dwThreadID << " says:" << szBuf << endl; /輸出接收到的信息171.LeaveCriticalSection(sr.pcs);1
23、72./ 向客戶(hù)端發(fā)送信息173.strcpy(szRely + c_iPrefLen, szBuf);174.iRet = send(sr.skAccept, szRely, strlen(szRely), 0); /客戶(hù)端如果沒(méi)有足夠的緩沖區(qū)接受信息,則線(xiàn)程會(huì) 阻塞到此處175. if(SOCKET_ERROR = iRet)176. 177. cout << "Connection " << sr.dwThreadID << " sen d error." << endl;178.break;179.
24、180.181./關(guān)閉該套接口182.iRet= shutdown(sr.skAccept, SD_SEND);183. while(recv(sr.skAccept, szBuf, c_iBufLen, 0) > 0);184. ASSERT(SOCKET_ERROR != i Ret);185. / 清理該套接口的資源186. iRet = closesocket(sr.skAccept);187. ASSERT(SOCKET_ERROR != i Ret);188. / 關(guān)閉該線(xiàn)程對(duì)象189. bRet = CloseThread(sr.t);190. ASSERT(bRet);1
25、91. cout << "Connection " << sr.dwThreadID << " exit." << endl;192. return 0;193. / fileServerMultThreadServerMultThread.c阻塞式多線(xiàn)程服務(wù)器程序。每當(dāng)客戶(hù)端程序并且向客戶(hù)端回饋請(qǐng)求的信息??? brief阻塞式多線(xiàn)程服務(wù)器程序。每當(dāng)客戶(hù)端/ 并且向客戶(hù)端回饋請(qǐng)求的信息/ #include <iostream>#include <cassert> #include
26、<WinSock2.h>#include <process.h> #pragma comment(lib, "ws2_32.lib" ) #define ASSERT assert #define THREAD HANDLE #define EVENT HANDLE #define CloseThread CloseHandle #define CloseEvent CloseHandle using std:cin;using std:cout;在用 Winsocket 編寫(xiě)程序時(shí),我們首 先必須要進(jìn)行如下的操作,以為該進(jìn)程初始化 Winsocke
27、t 和 Ws2_32.dll ,而使后面的函 數(shù)調(diào)用有效。view plaincopy to clipboardprint?1. WSADATA data;2. ZeroMemory(&data, sizeof(WSAD ATA);3. iRet = WSAStartup(MAKEWORD (2, 0), &data);4. ASSERT(SOCKET_ERROR != iRe t);WSADATA data;ZeroMemory(&data, sizeof(WSADATA);iRet = WSAStartup(MAKEWORD(2, 0), &data);AS
28、SERT(SOCKET_ERROR != iRet);WSAStartup 第一個(gè)參數(shù)為要使用的 Winsocket 的版本, MAKEWORD(2, 0) 表示我們使用 Winsocket2.0 。第二個(gè)參數(shù)在 WSAStartup 初始化后,可以獲得一些 Win socket 相關(guān)信息,如該版本 Winsocket 所 支持的最大 socket 數(shù)量以及 UDP 包的最大大 小。在初始化了 Winsocket 后,我們就可 以創(chuàng)建一個(gè) socket 監(jiān)聽(tīng)客戶(hù)端的連接請(qǐng)求了。view plaincopy to clipboardprint?1. SOCKET skListen = INVAL
29、ID_SOC KET;2. skListen = socket(AF_INET, SOC K_STREAM, 0);3. ASSERT(INVALID_SOCKET != skL isten);s ocket 函數(shù)分配相應(yīng)的資源并將該 socket 綁定到一個(gè)特定的傳輸服務(wù)提供者。 socket 的 第一個(gè)參數(shù)為網(wǎng)絡(luò)地址族, 該參數(shù)只能為 AF_I NET ,第二個(gè)參數(shù)可以為 SOCK_STREAM 或 者 SOCK_DGRAM 。 SOCK_STREAM 為一 個(gè)流式套接口, 它提供雙向可靠、面向連接的 T CP 服務(wù)。SOCK_DGRAM 為一個(gè)數(shù)據(jù)報(bào)套接 口,它提供不可靠、面向無(wú)連接的
30、UDP 服務(wù)。第三個(gè)參數(shù)一般選擇為 0 ,表示由 Winsocke t 選擇具體的協(xié)議使用。在建立了一個(gè)監(jiān)聽(tīng) socket 后,我們就 可以將該套接口與本地地址進(jìn)行綁定, 已將其設(shè) 置成為網(wǎng)絡(luò)中一個(gè)獨(dú)一無(wú)二的地址。view plaincopy to clipboardprint?1. iRet = bind(skListen, (sockaddr*)&adrServ, sizeof(sockaddr_in);2. ASSERT(SOCKET_ERROR != iRet);在綁定了本地地址后, 我們就可以將該 s ocket 設(shè)置為監(jiān)聽(tīng)狀態(tài),以使該 socket 可以 檢測(cè)到來(lái)自客戶(hù)端程
31、序的連接請(qǐng)求。view plaincopy to clipboardprint?1. iRet = listen(skListen, SOMAXCONN); / SOMAXCONN 表示可以連接到 該程序的最大連接數(shù)2. ASSERT(SOCKET_ERROR != iRet);iRet = listen(skListen, SOMAXCONN); / SOMAXCONN 表示ASSERT(SOCKET_ERROR != iRet);接下來(lái)我們我們就可以利用套接口接受來(lái)自 客戶(hù)端程序的連接了。 我們以該套接口為參數(shù)調(diào) 用 accept 函數(shù), accept 函數(shù)調(diào)用成功后, 將 建立一個(gè)可以接
32、受和發(fā)送數(shù)據(jù)的套接口 skAcc ept 。view plaincopy to clipboardprint?1. / 客戶(hù)端向服務(wù)器端發(fā)送連接請(qǐng)求,服務(wù) 器端接受客戶(hù)端的連接2. SOCKET skAccept = INVALID_SOCKET;3. sockaddr_in adrClit;4. ZeroMemory(&adrClit, sizeof(soc kaddr_in);5. int iLen = sizeof(sockaddr_in);6. skAccept = accept(skListen, (soc kaddr*)&adrClit, &iLen); /
33、 如果沒(méi)有客戶(hù)端程序請(qǐng)求連接,服務(wù)端程序會(huì)一直阻塞在這里等待連接7. ASSERT(INVALID_SOCKET != skAccept);/ 客戶(hù)端向服務(wù)器端發(fā)送連接請(qǐng)求,服務(wù)器端接受客戶(hù)端的連 SOCKET skAccept = INVALID_SOCKET;sockaddr_in adrClit;ZeroMemory(&adrClit, sizeof(sockaddr_in);int iLen = sizeof(sockaddr_in);skAccept = accept(skListen, (sockaddr*)&adrClit, &i ASSERT(INVAL
34、ID_SOCKET != skAccept);在成功的建立了新套接口后, 我們就可以利用 該套接口在我們的線(xiàn)程函數(shù)中接收和發(fā)送數(shù)據(jù) 了。view plaincopy to clipboardprint?1. iRet = recv(sr.skAccept, szBuf, c _iBufLen, 0); /接收客戶(hù)端發(fā)送的信息, 如果客戶(hù)端不發(fā)送信息,則線(xiàn)程會(huì)阻塞 到此處2. if(0 = iRet) / 客戶(hù)端優(yōu)雅的 關(guān)閉了此連接3. 4. cout << "Connection " << sr.dwThreadID << "
35、shutdown." << endl;5. break;6. 7. else if(SOCKET_ERROR = iRet) / 客戶(hù)端粗魯?shù)年P(guān)閉了此連接或者 接受信息出錯(cuò)8. 9. cout << "Connection " << sr.dwThreadID << " recv error." << endl;10. break;11. 12. szBufiRet = '0'iRet = recv(sr.skAccept, szBuf, c_iBufLen, 0);
36、/接if(0 = iRet) / 客戶(hù)端優(yōu)雅的關(guān)閉了此cout << "Connection " << srbreak;else if(SOCKET_ERROR = iRet) / 客cout << "Connection " << srbreak;szBufiRet = '0'view plaincopy to clipboardprint?1. strcpy(szRely + c_iPrefLen, szBuf);2. iRet = send(sr.skAccept, szRely, s
37、trlen(szRely), 0); / 客戶(hù)端如 果沒(méi)有足夠的緩沖區(qū)接受信息,則線(xiàn)程會(huì)阻 塞到此處3. if(SOCKET_ERROR = iRet)4. 5. cout << "Connection " << sr.dwThreadID << " send error." << endl;6. break;7. strcpy(szRely + c_iPrefLen, szBuf);iRet = send(sr.skAccept, szRely, st if(SOCKET_ERROR = iRet)cou
38、t << "Connection " << sr break;在客戶(hù)端關(guān)閉該套接口或者出現(xiàn)接收發(fā)送數(shù) 據(jù)錯(cuò)誤的時(shí)候, 我們都應(yīng)該關(guān)閉該套接口。 請(qǐng)注 意在調(diào)用關(guān)閉套接口的 closesocket 函數(shù)之 前,我們應(yīng)該先調(diào)用 shutdown 函數(shù),以使對(duì) 方可以收到該套接口已經(jīng)關(guān)閉的信息。 在調(diào)用 s hutdown 函數(shù)之后,我們應(yīng)該使用 recv 函數(shù) 讀取在隊(duì)列之中仍未讀完的數(shù)據(jù), 最后我們就可 以使用 closesocket 函數(shù)關(guān)閉該套接口了, 這 就是所謂的優(yōu)雅關(guān)閉。view plaincopy to clipboardprint?1.
39、/ 關(guān)閉該套接口2. iRet = shutdown(sr.skAccept, SD_SEND);3.while(recv(sr.skAccept, szBuf, c_iBufLen, 0) > 0);4.ASSERT(SOCKET_ERROR != iRet);5./ 清理該套接口的資源6.iRet = closesocket(sr.skAccep t);7.t);ASSERT(SOCKET_ERROR != iRet);/ 關(guān)閉該套接口iRet = shutdown(sr.skAccept, SD_SEND); while(recv(sr.skAccept, szBuf, c_iBu
40、fLen, 0 ASSERT(SOCKET_ERROR != iRet);/ 清理該套接口的資源 iRet = closesocket(sr.skAccept);ASSERT(SOCKET_ERROR != iRet);監(jiān)聽(tīng) socket 的關(guān)閉也與上面套接口關(guān) 閉的方法一致。 在關(guān)閉了監(jiān)聽(tīng)套接口后, 我們的 服務(wù)器程序應(yīng)該調(diào)用 WSACleanup 函數(shù),已 完成對(duì) Winsocket 和 ws2_32.dll 的清理。 上述就是該類(lèi)型服務(wù)器程序應(yīng)用程序執(zhí)行的全 過(guò)程了??蛻?hù)端程序的代碼跟服務(wù)器程序的代碼 相似,程序代碼如下所示:view plaincopy to clipboardprin
41、t?1. /2. / file ClientBlockClientBlock.cpp3. /4. / brief 連接服務(wù)器并向服務(wù)器發(fā)送 信息,然后接受服務(wù)器發(fā)送的信息 .5. /6. #include <iostream>7. #include <cassert>8. #include <WinSock2.h>9. #pragma comment(lib, "ws2_32.lib" )10. #define ASSERT assert11. using std:cin;12.using std:cout;13.using std:en
42、dl;14.static constchar c_szIP= ""15.static constint c_iPort =10001;16.int main()17.18.int iRet = SOCKET_ERROR;19./ 初始化Winsocket ,所有 Winsocket 程序必須先使用 WSAStartup 進(jìn)行初始化20. WSADATA data;21. ZeroMemory(&data, sizeof (WSADATA);22. iRet = WSAStartup(MAKEWORD(2, 0), &data);23. AS
43、SERT(SOCKET_ERROR != i Ret);24. / 建立連接套接字25. SOCKET skClient = INVALID _SOCKET;26. skClient = socket(AF_INET, SOCK_STREAM, 0);27. ASSERT(INVALID_SOCKET != skClient);28. / 初始化連接套接字地址信息29. sockaddr_in adrServ; / 表示網(wǎng)絡(luò)地址30. ZeroMemory(&adrServ, size of(sockaddr_in);31. adrServ.sin_family = AF_INET;
44、/ 初始化地址格式, 只能 為 AF_INET32. adrServ.sin_port = hton s(c_iPort); / 初始化端口,由于網(wǎng) 絡(luò)字節(jié)順序和主機(jī)字節(jié)順序相反,所以必須 使用 htons 將主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字 節(jié)順序33. = inet_addr(c_szIP); / 初始化 IP, 由于網(wǎng)絡(luò)字節(jié)順序和主機(jī)字節(jié)順序相反, 所以必須使用 inet_addr 將主機(jī)字節(jié)順序 轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序34. / 使用連接套接字進(jìn)行連接35. iRet = connect(skClient, (sockaddr*)&adrServ, sizeof(sockad dr_in)
45、;36. ASSERT(SOCKET_ERROR != iRet);37. const int c_iBufLen = 512;38. char szBufc_iBufLen + 16 + 1 = '0'39. for(;)40. 41. cout << "what you will say:"42. cin >> szBuf;43. if(0 = strcmp("exit", szBuf)44. 45. break;46. 47. / 向服務(wù)器端發(fā)送信息48. iRet = send(skClient, szBu
46、f, strlen(szBuf), 0); /服務(wù)器端如果沒(méi)有足夠的緩沖區(qū)接受信息,則線(xiàn)程會(huì)阻塞 到此處49. if(SOCKET_ERROR = iRet)50. 51. cout << "send error." << endl;52. break;53. 54. / 接收服務(wù)器端發(fā)送的信息55. iRet = recv(skClient, szBuf, c_iBufLen, 0); /如果服務(wù)器端沒(méi)有發(fā)送數(shù)據(jù),則會(huì)阻塞到此處56. if(0 = iRet)57. 58. cout << "connection shutdown." << endl;59. break;60. 61.else if(SOCKET_ERROR = iRet)62.63.cout << "recv error." << endl;64.break;65.66.szBufiRet = '0'67.cout << szBuf << endl;68.69./ 關(guān)閉該套接口70.iRet = shutdown(skClient,
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 遠(yuǎn)程在線(xiàn)安全教育的應(yīng)用與即時(shí)反饋分析
- 跨境電商平臺(tái)的物流管理與優(yōu)化策略
- 自我提升的行動(dòng)清單計(jì)劃
- 高質(zhì)量生活與血液病的先進(jìn)診斷技術(shù)
- 2025智能物聯(lián)網(wǎng)計(jì)算終端
- 財(cái)務(wù)風(fēng)險(xiǎn)管理在教育行業(yè)的應(yīng)用
- 增強(qiáng)前臺(tái)文員溝通能力的工作計(jì)劃
- 跨領(lǐng)域科技創(chuàng)新的發(fā)展策略研究報(bào)告
- 科技類(lèi)企業(yè)如何通過(guò)競(jìng)品廣告提升品牌形象
- 超聲科操作技巧解析專(zhuān)家級(jí)教程
- 2025年山東泰山財(cái)產(chǎn)保險(xiǎn)股份有限公司招聘筆試參考題庫(kù)含答案解析
- 初中物理競(jìng)賽及自主招生講義:第7講 密度、壓強(qiáng)與浮力(共5節(jié))含解析
- 農(nóng)村自建房施工合同范本(包工包料)
- 高中主題班會(huì) 梁文鋒和他的DeepSeek-由DeepSeek爆火開(kāi)啟高中第一課-高中主題班會(huì)課件
- 污水處理設(shè)施運(yùn)維服務(wù)投標(biāo)方案(技術(shù)標(biāo))
- 一年級(jí)下冊(cè)書(shū)法教案 (一)
- 2025年復(fù)工復(fù)產(chǎn)安全開(kāi)工第一課專(zhuān)題培訓(xùn)
- 2025幼兒園疫情報(bào)告制度及流程
- GB/T 41869.3-2024光學(xué)和光子學(xué)微透鏡陣列第3部分:光學(xué)特性測(cè)試方法
- 2024年八年級(jí)語(yǔ)文下冊(cè)《經(jīng)典常談》第一章《說(shuō)文解字》練習(xí)題卷附答案
- 華為基建項(xiàng)目管理手冊(cè)
評(píng)論
0/150
提交評(píng)論