Winsocket入門(mén)教程一:多線(xiàn)程阻塞式服務(wù)器和阻塞式客戶(hù)端程序_第1頁(yè)
Winsocket入門(mén)教程一:多線(xiàn)程阻塞式服務(wù)器和阻塞式客戶(hù)端程序_第2頁(yè)
Winsocket入門(mén)教程一:多線(xiàn)程阻塞式服務(wù)器和阻塞式客戶(hù)端程序_第3頁(yè)
已閱讀5頁(yè),還剩34頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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ì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論