版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、 Winsock網(wǎng)絡(luò)編程使用阻塞套接字模式【ITjob課程資料】Windows網(wǎng)絡(luò)編程的三種模式阻塞套接字模式、異步套接字模式(基于消息的非阻塞套接字模式)、MFC異步套接字模式使用阻塞套接字模式進(jìn)行Windows網(wǎng)絡(luò)編程利用TCP進(jìn)行通信程序開發(fā)TCP進(jìn)行通信開發(fā)有點(diǎn)類似于步話機(jī)(對(duì)講機(jī))對(duì)話的方式,那么步驟是首先要找到對(duì)方對(duì)講機(jī)的位置,然后根據(jù)此對(duì)講機(jī)的位置進(jìn)行呼叫,對(duì)方接受到呼叫信號(hào)之后按下應(yīng)答按鈕,發(fā)送回應(yīng)信號(hào),呼叫方收到回應(yīng)信號(hào)后,兩人連線成功,進(jìn)行通話。我們也可以稱之為通信領(lǐng)域的三次握手。這種需要通過握手確認(rèn)建立通道進(jìn)行通訊的方式就是TCP通信。下面我們來編寫服務(wù)器端的代碼:初始化
2、套接字庫創(chuàng)建服務(wù)端套接字綁定套接字開始監(jiān)聽客戶端接受客戶端套接字向客戶端發(fā)送數(shù)據(jù)接受客戶端數(shù)據(jù)關(guān)閉客戶端套接字關(guān)閉服務(wù)端套接字釋放套接字庫資源一、引入頭文件#include <Winsock2.h>#include <stdio.h>并引入動(dòng)態(tài)鏈接庫#pragma comment (lib, "ws2_32.lib")二、初始化套接字庫.WSADATA wsaData;WORD wVersionRequested = MAKEWORD(1, 1);int err = WSAStartup(wVersionRequested, &wsaData
3、);if (err != 0)return;SOCKET編程的概念如何通訊?最直觀的方式就是記錄兩邊的通訊地址(就像兩個(gè)電話號(hào)碼一樣),但是在計(jì)算機(jī)中每個(gè)機(jī)器不僅僅有IP地址,還有很多端口,分別負(fù)責(zé)不同的任務(wù),比如:80端口就是負(fù)責(zé)IIS默認(rèn)的HTTP服務(wù)的,而21端口則是負(fù)責(zé)FTP通訊,在操作系統(tǒng)中將這些內(nèi)容封裝到一個(gè)結(jié)構(gòu)體中,用于描述IP地址和端口。這個(gè)就是Socket(也稱作“套接字”),當(dāng)然還要包括傳輸協(xié)議(TCP或UDP)。從技術(shù)的底層來看,操作系統(tǒng)通過套接字描述符來針對(duì)網(wǎng)絡(luò)通訊的點(diǎn)對(duì)點(diǎn)的關(guān)系進(jìn)行描述,每個(gè)進(jìn)程的進(jìn)程空間里都有一個(gè)套接字描述符表,比如:套接字描述符表socket描述地
4、址1952內(nèi)核位置的內(nèi)存地址1內(nèi)核位置的內(nèi)存地址2內(nèi)核緩沖地址具體描述內(nèi)核位置的內(nèi)存地址16000TCP內(nèi)核位置的內(nèi)存地址26000TCP該表中存放著套接字描述符和套接字結(jié)構(gòu)的對(duì)應(yīng)關(guān)系。該表中有一個(gè)字段存放新創(chuàng)建的套接字的描述符,另一個(gè)字段存放套接字結(jié)構(gòu)的地址,因此根據(jù)套接字描述符就可以找到其對(duì)應(yīng)的套接字結(jié)構(gòu)。每個(gè)進(jìn)程在自己的進(jìn)程空間里都有一個(gè)套接字描述符表但是套接字結(jié)構(gòu)都是在操作系統(tǒng)的內(nèi)核緩沖里。c+中通過socket函數(shù)在內(nèi)核中建立一個(gè)socket空結(jié)構(gòu),并返回整數(shù)類型的套接字描述符,對(duì)應(yīng)這個(gè)socket結(jié)構(gòu)。所以我們知道socket描述符后就能
5、明確知道對(duì)方的地址和端口,以備發(fā)送信息。WSAStartup函數(shù)WSAStartup函數(shù)用于初始化網(wǎng)絡(luò)資源,并建立一個(gè)套接字庫。參數(shù)說明:wVersionRequested:用于指定準(zhǔn)備加載的Winsock庫的版本。高位字節(jié)指定所需要的Winsock庫的副版本,而低位字節(jié)則是主版本??捎肕AKEWORD(x,y)(其中,x是高位字節(jié),y是低位字節(jié))方便地獲得wVersionRequested的正確值。lpWSAData:是指向WSADATA結(jié)構(gòu)的指針,WSAStartup用其加載的庫版本有關(guān)的信息填在這個(gè)結(jié)構(gòu)中。WSADATA結(jié)構(gòu)定義如下:struct WSAData WORDwVersion
6、;WORDwHighVersion;charszDescriptionWSADESCRIPTION_LEN+1;charszSystemStatusWSASYS_STATUS_LEN+1;unsigned short iMaxSockets;unsigned short iMaxUdpDg;char FAR *lpVendorInfo;wVersion:打算使用的Winsock版本。wHighVersion :現(xiàn)有的Winsock庫的最高版本。szDescription:Winsock庫的具體描述。szSystemStatus:sock狀態(tài)。iMaxSockets:同時(shí)最多可打開多少套接字iM
7、axUdpDg:數(shù)據(jù)報(bào)的最大長度lpVendorInfo:為Winsock實(shí)施方案有關(guān)的指定廠商信息預(yù)留的。一般用不到。三、創(chuàng)建服務(wù)端套接字SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);if (INVALID_SOCKET = sockSrv)WSACleanup();return;socket函數(shù)c+中通過socket函數(shù)在內(nèi)核中建立一個(gè)socket空結(jié)構(gòu),并返回整數(shù)類型的套接字描述符,對(duì)應(yīng)這個(gè)socket結(jié)構(gòu)。所以我們知道socket描述符后就能明確知道對(duì)方的地址和端口,以備發(fā)送信息。函數(shù)原型:SOCKET socket(intaf,in
8、ttype,intprotocol); 此函數(shù)用于建立一個(gè)新的套接字結(jié)構(gòu),并分配一個(gè)套接字描述符。參數(shù)描述:af:一種地址格式描述。對(duì)于TCP/IP協(xié)議的套接字,它只能是AF_INET。type:要建立的套接字的類型描述,類型有兩種:SOCK_STREAM(TCP傳輸),SOCK_DGRAM(UDP傳輸)protocol:套接字使用的特定協(xié)議,如果調(diào)用者不希望指定協(xié)議,則置為0,使用默認(rèn)的連接模式。返回值: 如果沒有錯(cuò)誤發(fā)生,socket()返回一個(gè)與建立的套接字相關(guān)的描述符。否則它返回-1四、綁定套接字到具體的地址和端口SOCKADDR_IN addrTemp;addrTemp.sin_ad
9、dr.S_un.S_addr = htonl(INADDR_ANY);addrTemp.sin_port = htons(6000);addrTemp.sin_family = AF_INET;err = bind(sockSrv, (SOCKADDR*)&addrTemp, sizeof(SOCKADDR_IN);if (err = -1) WSACleanup();return;bind函數(shù)函數(shù)原型:int bind(SOCKETs,const struct sockaddr *name,intnamelen); 將套接字句柄與地址、端口進(jìn)行綁定。參數(shù)描述:s:數(shù)據(jù)報(bào)或流套接字的描
10、述符。name: 賦給套接字的地址,具體內(nèi)容用一個(gè)結(jié)構(gòu)體sockaddr表示:struct sockaddr u_short sa_family; char sa_data14;字段描述:sa_family:指定該地址家族,在這里因?yàn)槭莟cp,udp,所以設(shè)定為AF_INET。sa_data :表示所有的不同socket地址結(jié)構(gòu)的最大尺寸。注意:實(shí)際編程中,我們一般用sockaddr_in結(jié)構(gòu)替換sockaddr,因?yàn)閟ockaddr_in比sockaddr清晰(注意,系統(tǒng)支持自動(dòng)轉(zhuǎn)換)。struct sockaddr_in short sin_family; u_short sin_port
11、; struct in_addr sin_addr; char sin_zero8;而struct in_addr的定義如下:struct in_addr union struct u_char s_b1,s_b2,s_b3,s_b4; S_un_b; struct u_short s_w1,s_w2; S_un_w; u_long S_addr; S_un;SOCKADDR_IN結(jié)構(gòu)的字段描述:sin_family:置為AF_INETsin_port:連接的端口號(hào)。這里用了addrTemp.sin_port = htons(6000);下面解釋htnos函數(shù):htons函數(shù)此函數(shù)將一個(gè)u_s
12、hort類型數(shù)(16位無符號(hào)整數(shù))從主機(jī)字節(jié)順序轉(zhuǎn)換成TCP/IP網(wǎng)絡(luò)字節(jié)順序。hostshort參數(shù):主機(jī)字節(jié)順序表示的16位無符號(hào)整數(shù)。返回值: htons()返回一個(gè)TCP/IP網(wǎng)絡(luò)字節(jié)順序表示的16位值。它的逆向函數(shù)為:ntohs()htons 是把你機(jī)器上的整數(shù)轉(zhuǎn)換成“網(wǎng)絡(luò)字節(jié)序”, 網(wǎng)絡(luò)字節(jié)序是 大字節(jié)存儲(chǔ)次序,也就是整數(shù)的高位字節(jié)存放在內(nèi)存的低地址處。 而我們常用的 x86 CPU (intel, AMD) 電腦是 小字節(jié)存儲(chǔ)次序,也就是整數(shù)的低位字節(jié)放在內(nèi)存的低字節(jié)處。例如: port是 0x1234, 在網(wǎng)絡(luò)字節(jié)序里 這個(gè)port放到內(nèi)存中就應(yīng)該顯示成 addr,addr+1
13、:0x12,0x34;而在x86電腦上,0x1234放到內(nèi)存中實(shí)際是:addr,addr+1: 0x34,0x12。htons 的用處就是把實(shí)際內(nèi)存中的整數(shù)存放方式調(diào)整成“網(wǎng)絡(luò)字節(jié)序”的方式。相關(guān)的函數(shù)有:htonl函數(shù)此函數(shù)將一個(gè)u_long類型數(shù)(32位無符號(hào)整數(shù))從主機(jī)字節(jié)順序轉(zhuǎn)換成TCP/IP網(wǎng)絡(luò)字節(jié)順序。hostlong :主機(jī)字節(jié)順序表示的32位無符號(hào)整數(shù)。返回值: htonl()返回一個(gè)TCP/IP網(wǎng)絡(luò)字節(jié)順序表示的32位值。它的逆向函數(shù)為:ntohl()第三個(gè)重要的參數(shù)為sin_addr,即IP地址。這是一個(gè)32位的無符號(hào)整數(shù),addrTemp.sin_addr. s_addr
14、 =inet_addr(""); 或者addrTemp.sin_addr.s_addr =inet_addr("60");假設(shè)地址為60。下面解釋inet_addr函數(shù)簡述:將一個(gè)點(diǎn)間隔地址轉(zhuǎn)換成一個(gè)sin_addr.S_un.S_addr。如果我們只想讓套接字使用多個(gè)IP中的一個(gè)地址,就必須指定實(shí)際地址,要做到這一點(diǎn),可以用inet_addr()函數(shù),這個(gè)函數(shù)需要一個(gè)字符串作為其參數(shù),該字符串指定了以點(diǎn)分十進(jìn)制格式表示的IP地址(如6)。而且inet_addr()函數(shù)會(huì)
15、返回一個(gè)適合分配給S_addr的u_long類型的數(shù)值。它的逆向函數(shù)為:inet_ntoa()函數(shù)會(huì)完成相反的轉(zhuǎn)換,它接受一個(gè)in_addr結(jié)構(gòu)體類型的參數(shù)并返回一個(gè)以點(diǎn)分十進(jìn)制格式表示的IP地址字符串。inet_ntoa函數(shù)語法: char * inet_ntoa ( IN struct in_addr in ); 此函數(shù)將一個(gè)網(wǎng)際地址轉(zhuǎn)換成點(diǎn)分十進(jìn)制表示法表示的字符串。它接受由參數(shù)in指定的網(wǎng)際地址結(jié)構(gòu),返回以點(diǎn)分表示法如“a.b.c.d”表示的地址的ASCII字符串。參數(shù)描述:in:表示主機(jī)網(wǎng)際地址的結(jié)構(gòu)。返回值: 如果沒有錯(cuò)誤發(fā)生,inet_ntoa()返回一個(gè)字符指針,該指針指向含有
16、以點(diǎn)分十進(jìn)制表示法表示的正文地址的靜態(tài)緩沖區(qū)。但是現(xiàn)在語句表示為:addrTemp.sin_addr. s_addr = INADDR_ANY;關(guān)于INADDR_ANY宏INADDR_ANY的定義如下:#defineINADDR_ANY(unsigned long int) 0x00000000)如果服務(wù)器有多個(gè)網(wǎng)卡(每個(gè)網(wǎng)卡上有不同的IP地址),當(dāng)用任何一個(gè)IP地址都可以的時(shí)候,將IP地址指定為INADDR_ANY。sin_zero:是為了讓sockaddr與sockaddr_in兩個(gè)數(shù)據(jù)結(jié)構(gòu)保持大小相同而保留的空字節(jié),用bzero函數(shù)置為8個(gè)零, bzero(&(my_addr.s
17、in_zero),8);回到bind函數(shù),最后一個(gè)參數(shù)namelen,表示地址緩沖區(qū)長度。返回值: 如果沒有錯(cuò)誤發(fā)生,bind()返回0。五、 費(fèi)了很長的時(shí)間終于綁定了socket,那么下面開始進(jìn)行監(jiān)聽listen(sockSrv, SOMAXCONN);listen函數(shù)listen ( IN SOCKET s, IN int backlog ); 此函數(shù)只用于流套接字,它執(zhí)行兩個(gè)操作:1、若沒有為s調(diào)用過bind(),則listen()完成套接字s所必須的連接。2、建立長度為backlog的連接請(qǐng)求隊(duì)列來存放即將到來的連接請(qǐng)求。 參數(shù)描述:s:標(biāo)識(shí)一個(gè)已綁扎、沒有連接的套接字的描述符。bac
18、klog:未處理連接隊(duì)列的最大長度。返回值: 如果沒有錯(cuò)誤發(fā)生,listen()返回0。六、下面開始監(jiān)聽客戶端,代碼如下SOCKADDR_IN addrClient; int len = sizeof(SOCKADDR); SOCKET sockClient = accept(sockSrv, (SOCKADDR*)&addrClient, &len);accept函數(shù)語法: SOCKET WSAAPI accept ( IN SOCKET s, OUT struct sockaddr FAR* addr, OUT int FAR* addrlen ); 這個(gè)函數(shù)是一個(gè)阻塞函數(shù)
19、,用于接受客戶端的socket信息。accept()函數(shù)讓服務(wù)器接收客戶的連接請(qǐng)求。在建立好輸入隊(duì)列后,服務(wù)器就調(diào)用accept函數(shù),然后睡眠并等待客戶的連接請(qǐng)求。當(dāng)然accept函數(shù)發(fā)生在三次握手之后:第一次握手:客戶端發(fā)送syn包(syn=j)到服務(wù)器。第二次握手:服務(wù)器收到syn包,必須確認(rèn)客戶的SYN(ack=j+1),同時(shí)自己也發(fā)送一個(gè)ASK包(ask=k)。第三次握手:客戶端收到服務(wù)器的SYNACK包,向服務(wù)器發(fā)送確認(rèn)包ACK(ack=k+1)。三次握手完成后,客戶端和服務(wù)器就建立了tcp連接。這時(shí)可以調(diào)用accept函數(shù)獲得此連接 sockfd是被監(jiān)聽的socket描述符,add
20、r通常是一個(gè)指向sockaddr_in變量的指針,該變量用來存放提出連接請(qǐng)求服務(wù)的主機(jī)的信息(某臺(tái)主機(jī)從某個(gè)端口發(fā)出該請(qǐng)求);addrten通常為一個(gè)指向值為sizeof(struct sockaddr_in)的整型指針變量。出現(xiàn)錯(cuò)誤時(shí)accept函數(shù)返回-1并置相應(yīng)的errno值。首先,當(dāng)accept函數(shù)監(jiān)視的 socket收到連接請(qǐng)求時(shí),socket執(zhí)行體將建立一個(gè)新的socket,執(zhí)行體將這個(gè)新socket和請(qǐng)求連接進(jìn)程的地址聯(lián)系起來,收到服務(wù)請(qǐng)求的 初始socket仍可以繼續(xù)在以前的 socket上監(jiān)聽,同時(shí)可以在新的socket描述符上進(jìn)行數(shù)據(jù)傳輸操作。七、接收到了客戶端的socke
21、t信息之后,則可以通過recv函數(shù)來獲得數(shù)據(jù)。char recvBuf100;char sendBuf100;while (1)recv(sockClient, recvBuf, 100, 0);printf("%s:%sn",inet_ntoa (addrClient.sin_addr),recvBuf);recv函數(shù)語法: int WSAAPI recv ( IN SOCKET s, OUT char FAR* buf, IN int len, IN int flags ); 此函數(shù)用于在參數(shù)s指定的已連接的數(shù)據(jù)報(bào)或流套接字上讀取輸入數(shù)據(jù)。參數(shù)描述:s:已連接的套接字描
22、述符。buf:指向接收輸入數(shù)據(jù)緩沖區(qū)的指針。len:buf參數(shù)所指緩沖區(qū)的長度。flags:指定調(diào)用的方式,它可用來與套接字相關(guān)的選項(xiàng)一起影響函數(shù)的功能。就是說,recv()函數(shù)的意義由套接字選項(xiàng)和flags參數(shù)共同決定。flags可取下列值: MSG_OOB 讀取套接字上的帶外數(shù)據(jù)。MSG_PEEK 查看輸入數(shù)據(jù),數(shù)據(jù)被拷入緩沖區(qū)中, 但不從輸入隊(duì)列中清除。返回值: 如果沒有錯(cuò)誤發(fā)生,recv()返回收到的字節(jié)數(shù)。如果連接被關(guān)閉,返回0。八、接受到數(shù)據(jù)之后,向用戶發(fā)送數(shù)據(jù):gets(sendBuf);send(sockClient, sendBuf, 100, 0);send函數(shù)語法: in
23、t WSAAPI send ( IN SOCKET s, IN const char FAR * buf, IN int len, IN int flags ); 此函數(shù)用于在參數(shù)s指定的已連接的數(shù)據(jù)報(bào)或流套接字上發(fā)送輸出數(shù)據(jù)。參數(shù)描述:s:已連接的套接字描述符。buf:指向存有發(fā)送數(shù)據(jù)的緩沖區(qū)的指針。len:緩沖區(qū)buf中數(shù)據(jù)長度。flags:指定調(diào)用的方式,它可用來與套接字相關(guān)的選項(xiàng)一起影響函數(shù)的功能。就是說,send()函數(shù)的意義由套接字選項(xiàng)和flags參數(shù)共同決定。flags可取下述值: MSG_DONTROUTE 指出數(shù)據(jù)不提交給路由選擇。MSG_OOB 發(fā)送帶外數(shù)據(jù)。返回值: 如果
24、沒有錯(cuò)誤發(fā)生,send()返回總共發(fā)送的字節(jié)數(shù)(注意,這可能比len指示的長度?。>?、關(guān)閉套接字,關(guān)閉網(wǎng)絡(luò)資源。/8. 關(guān)閉客戶端套節(jié)字closesocket(sockClient);/9.關(guān)閉服務(wù)端套接字closesocket(sockSrv);/10.釋放套接字庫資源WSACleanup();closesocket函數(shù)語法: int WSAAPI closesocket ( IN SOCKET s );函數(shù)關(guān)閉套接字s,并釋放分配給該套接字的資源,以后對(duì)s 的引用都將產(chǎn)生錯(cuò)誤WSAENOTSOCK。如果s涉及一個(gè)打開的TCP連接,該連接被釋放。參數(shù)描述:s:待關(guān)閉的套接字描述符。返回值:
25、如果沒有錯(cuò)誤發(fā)生,closesocket()返回0。十、下面我們開始寫客戶端:10.1 包含頭文件#include <Winsock2.h>#include <stdio.h>#pragma comment (lib, "ws2_32.lib")10.2創(chuàng)建套接字資源庫WSADATA wsaData;WORD wVersionRequested = MAKEWORD(1, 1);int err = WSAStartup(wVersionRequested, &wsaData);if (err != 0) return;10.3 創(chuàng)建客戶端套接
26、字SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);10.4指明服務(wù)器SOCKADDR_IN addrSrv;addrSrv.sin_addr.S_un.S_addr = inet_addr("");addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(6000);/4.發(fā)出連接請(qǐng)求connect(sockClient, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR_IN);connect函數(shù)當(dāng)用socket建立
27、了套接口后,可以調(diào)用connect為這個(gè)套接字指明遠(yuǎn)程端的地址;如果是字節(jié)流套接口,connect就使用三次握手建立一個(gè)連接;如果是數(shù)據(jù)報(bào)套接口,connect僅指明遠(yuǎn)程端地址,而不向它發(fā)送任何數(shù)據(jù)。返回:0表示成功-1表示失敗 參數(shù)解釋:第一個(gè)參數(shù)是socket函數(shù)返回的套接口描述字;第二和第三個(gè)參數(shù)分別是一個(gè)指向套接口地址結(jié)構(gòu)的指針和該結(jié)構(gòu)的大小。10.5向服務(wù)器端發(fā)送數(shù)據(jù)char sendBuf100;char recvBuf100;while(1) gets(sendBuf);send(sockClient, sendBuf, 100, 0);10.6 接收接受服務(wù)端數(shù)據(jù)recv(so
28、ckClient,recvBuf,100,0);printf("%s:%sn",inet_ntoa(addrSrv.sin_addr),recvBuf);10.7關(guān)閉客戶端套接字和套節(jié)字庫資源closesocket(sockClient); WSACleanup();下面解釋上面的代碼中提及的概念和函數(shù):服務(wù)器端和客戶端的創(chuàng)建過程如下表:TCP服務(wù)端偽代碼TCP客戶端偽代碼1創(chuàng)建服務(wù)端套接字socket()創(chuàng)建連接套接字socket()2綁定套接字bind()指明服務(wù)器,并發(fā)出連接請(qǐng)求connect()3開始監(jiān)聽客戶端listen()4接受客戶端套接字accept() 阻塞
29、等待5接受客戶端數(shù)據(jù)recv() 阻塞等待向服務(wù)端發(fā)送數(shù)據(jù)send()6向客戶端發(fā)送數(shù)據(jù)send()接受服務(wù)端數(shù)據(jù)recv() 阻塞等待7關(guān)閉客戶端套接字closesocket()關(guān)閉連接套接字closesocket()8關(guān)閉服務(wù)端套接字closesocket()9釋放套接字庫資源WSACleanup()釋放套接字庫資源WSACleanup()利用UDP進(jìn)行通信程序開發(fā)的基本步驟UDP通訊是不需要握手確認(rèn)的,就好像兩個(gè)人直接會(huì)話,比較形象的比喻是發(fā)送電報(bào),而這種方式也不需要任何的確認(rèn)過程。服務(wù)器端代碼如下:#include <Winsock2.h>#include <stdi
30、o.h>void main()/1. 初始化套接字庫WSADATA wsaData;WORD wVersionRequested = MAKEWORD(1, 1);int err = WSAStartup(wVersionRequested, &wsaData);if (err != 0) return;/2. 創(chuàng)建服務(wù)端套接字SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);/3. 綁定套接字SOCKADDR_IN addrTemp;addrTemp.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
31、addrTemp.sin_family = AF_INET;addrTemp.sin_port = htons(6000);err = bind(sockSrv, (SOCKADDR*)&addrTemp, sizeof(SOCKADDR_IN);if (0 != err)WSACleanup();return;char recvBuf100;char sendBuf100;SOCKADDR_IN addrClient;int len = sizeof(SOCKADDR);while (1)/4.接受客戶端數(shù)據(jù)recvfrom(sockSrv, recvBuf, 100, 0, (SO
32、CKADDR*)&addrClient, &len);/阻塞printf("%s:%sn",inet_ntoa(addrClient.sin_addr),recvBuf);/5.發(fā)數(shù)據(jù)到客戶端gets(sendBuf);sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrClient, len);/6. 關(guān)閉服務(wù)端套接字closesocket(sockSrv);/7.釋放套接字庫資源WSACleanup();客戶端代碼如下:#include <Winsock2.h&g
33、t;#include <stdio.h>void main()/1. 初始化套接字庫WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested = MAKEWORD(1, 1);err = WSAStartup(wVersionRequested, &wsaData);if (err != 0) return;/2. 創(chuàng)建客戶端套接字SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0);/3.指明服務(wù)器SOCKADDR_IN addrSrv;addrSrv.
34、sin_addr.S_un.S_addr = inet_addr("");addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(6000);char recvBuf100;char sendBuf100;int len = sizeof(SOCKADDR);while (1)/4.發(fā)數(shù)據(jù)到服務(wù)器端gets(sendBuf);sendto(sockClient, sendBuf, 100, 0, (SOCKADDR*)&addrSrv, len);/5.接受服務(wù)端數(shù)據(jù)recvfrom(sockCl
35、ient, recvBuf, 100, 0, (SOCKADDR*)&addrSrv, &len);printf("%s:%sn", inet_ntoa(addrSrv.sin_addr),recvBuf);/6. 關(guān)閉客戶端套接字closesocket(sockClient);/7.釋放套接字庫資源WSACleanup();UDP服務(wù)端偽代碼UDP客戶端偽代碼1初始化套接字庫WSAStartup()初始化套接字庫WSAStartup()2創(chuàng)建服務(wù)端套接字socket(SOCK_DGRAM)創(chuàng)建客戶端套接字socket(SOCK_DGRAM)3綁定套接字bi
36、nd()4接受客戶端數(shù)據(jù)recvfrom() 阻塞發(fā)數(shù)據(jù)到服務(wù)端sendto()5發(fā)數(shù)據(jù)到客戶端sendto()接受服務(wù)端數(shù)據(jù)recvfrom() 阻塞6關(guān)閉服務(wù)端套接字closesocket()關(guān)閉客戶端套接字closesocket()7釋放套接字庫資源WSACleanup()釋放套接字庫資源WSACleanup()TCP與UDP的對(duì)比TCP是可靠傳輸,UDP是不可靠傳輸TCP是面向連接的,UDP是無連接的TCP被廣泛應(yīng)用在文件傳輸、遠(yuǎn)程連接等需要數(shù)據(jù)被可靠傳輸?shù)念I(lǐng)域;UDP比TCP相對(duì)簡單且容易管理,它被應(yīng)用在一些局域網(wǎng)系統(tǒng)的應(yīng)用程序中。UDP非???,具有低開銷要求,TCP比較慢,具有更高
37、的開銷要求tcp和udp的應(yīng)用如下:應(yīng) 用應(yīng)用層協(xié)議用來支撐的傳輸協(xié)議電子郵件SMTP ( RFC82 )TCP遠(yuǎn)程終端訪問TelenetTCPWEBHTTPTCP文件傳送FTPTCP遠(yuǎn)程文件服務(wù)器NFSTCP 或 UDP流多媒體專屬UDP 或 TCP因特網(wǎng)電話專屬一般為 UDP多線程應(yīng)用注意,剛剛的程序有一個(gè)特點(diǎn)就是就是每次需要接受到信息以后才能再次發(fā)送信息,不能連續(xù)多次發(fā)送信息,這是因?yàn)槌绦蚪Y(jié)構(gòu)導(dǎo)致:while (1)/4.接受數(shù)據(jù)進(jìn)入等待recvfrom();/5.發(fā)送數(shù)據(jù)sendto();這段程序在主線程中完成。是一種同步方式,需要先接收再發(fā)送。下面我們采用多線程的方式就可以解決這個(gè)問
38、題,單獨(dú)開辟一個(gè)線程用于接受信息。服務(wù)器端程序如下:#include <Winsock2.h>#include <stdio.h>SOCKADDR_IN addrClient; /客戶端地址SOCKET sockSrv;BOOL bEnableSend=FALSE;DWORD WINAPI RecvProc(LPVOID lpParameter)/1. 初始化套接字庫WSADATA wsaData;WORD wVersionRequested = MAKEWORD(1, 1);int err = WSAStartup(wVersionRequested, &ws
39、aData);if (err != 0) return 0;/2. 創(chuàng)建服務(wù)端套接字sockSrv = socket(AF_INET, SOCK_DGRAM, 0);/3. 綁定套接字SOCKADDR_IN addrTemp;addrTemp.sin_addr.S_un.S_addr = htonl(INADDR_ANY);addrTemp.sin_family = AF_INET;addrTemp.sin_port = htons(6000);err = bind(sockSrv, (SOCKADDR*)&addrTemp, sizeof(SOCKADDR_IN);if (0 !=
40、err)WSACleanup();return 0;char recvBuf100;intlen = sizeof(SOCKADDR_IN);while (1)/4.接受客戶端數(shù)據(jù)recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrClient, &len);printf("%s:%sn",inet_ntoa(addrClient.sin_addr),recvBuf);bEnableSend=TRUE;/6. 關(guān)閉服務(wù)端套接字closesocket(sockSrv);/7.釋放套接字庫資源WSACleanup
41、();return 0;void main()HANDLE m_hThread;m_hThread = :CreateThread(NULL, 0, RecvProc, NULL, 0, NULL);char sendBuf100;while (1)/5.發(fā)數(shù)據(jù)到客戶端if (bEnableSend)gets(sendBuf);int a=sendto(sockSrv, sendBuf, 100, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR);客戶端程序如下:#include <Winsock2.h>#include <std
42、io.h>SOCKET sockClient;SOCKADDR_IN addrSrv;/服務(wù)器地址BOOLEAN bEnableRev=FALSE;/是否可以接收DWORD WINAPI RecvProc(LPVOID lpParameter)char recvBuf100;intlen = sizeof(SOCKADDR_IN);while (1)if (bEnableRev)/5.接受服務(wù)器數(shù)據(jù)recvfrom(sockClient, recvBuf, 100, 0, (SOCKADDR*)&addrSrv, &len);printf("%s:%sn&quo
43、t;,inet_ntoa(addrSrv.sin_addr),recvBuf);void main()HANDLE m_hThread;m_hThread = :CreateThread(NULL, 0, RecvProc, NULL, 0, NULL);/1. 初始化套接字庫WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested = MAKEWORD(1, 1);err = WSAStartup(wVersionRequested, &wsaData);if (err != 0) return;/2. 創(chuàng)建客戶
44、端套接字sockClient = socket(AF_INET, SOCK_DGRAM, 0);/3.指明服務(wù)器地址addrSrv.sin_addr.S_un.S_addr = inet_addr("");addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(6000);char sendBuf100;int len = sizeof(SOCKADDR);while (1)/4.發(fā)數(shù)據(jù)到服務(wù)器gets(sendBuf);sendto(sockClient, sendBuf, 100, 0, (SOCKA
45、DDR*)&addrSrv, len);bEnableRev=TRUE;/6. 關(guān)閉客戶端套接字closesocket(sockClient);/7.釋放套接字庫資源WSACleanup();用對(duì)話框?qū)崿F(xiàn)UDP多線程對(duì)話在這個(gè)程序中,我們建立一個(gè)基于UDP多線程的對(duì)話框程序,其中一個(gè)線程用于接收信息,另外一個(gè)用于發(fā)送信息,界面如下:設(shè)定兩個(gè)全局變量SOCKET sockSrv;/服務(wù)端WSADATA wsaData;/套接字庫(1) 首先在Dlg的OnInitDialog事件中完成初始化工作:/1. 初始化套接字庫WORD wVersionRequested = MAKEWORD(1,
46、 1);int err = WSAStartup(wVersionRequested, &wsaData);if (err != 0) return 0;/2. 創(chuàng)建服務(wù)端套接字sockSrv = socket(AF_INET, SOCK_DGRAM, 0);/3. 綁定套接字/注意:一個(gè)端口只能綁定一個(gè),所以控制程序只能啟動(dòng)一次SOCKADDR_IN addrTemp;addrTemp.sin_addr.S_un.S_addr = htonl(INADDR_ANY);addrTemp.sin_family = AF_INET;addrTemp.sin_port = htons(600
47、0);err = bind(sockSrv, (SOCKADDR*)&addrTemp, sizeof(SOCKADDR_IN);if (0 != err)WSACleanup();return 0;HANDLE m_hThread;m_hThread = :CreateThread(NULL, 0, RecvProc, this, 0, NULL);SetDlgItemText(IDC_EDIT_IP, "");SetDlgItemText(IDC_EDIT_SEND, "hello");(2)在Dlg的OnDestroy事件
48、中釋放資源:/6. 關(guān)閉服務(wù)端套接字closesocket(sockSrv);/7.釋放套接字庫資源WSACleanup();(3)做一個(gè)線程啟動(dòng)接收數(shù)據(jù)DWORD WINAPI RecvProc(LPVOID lpParameter)CUDPChatDlg *pDlg = (CUDPChatDlg *)lpParameter;char recvBuf100;intlen = sizeof(SOCKADDR_IN);SOCKADDR_IN addrClient; /客戶端地址while (1)/4.接受客戶端數(shù)據(jù)recvfrom(sockSrv, recvBuf, 100, 0, (SOCKA
49、DDR*)&addrClient, &len);/顯示數(shù)據(jù)CString str;str.Format("%s: %s", inet_ntoa(addrClient.sin_addr), recvBuf);pDlg->m_list.AddString(str);:SendMessage(pDlg->m_list.m_hWnd, WM_VSCROLL, SB_BOTTOM, NULL);return 0;(4)在Button點(diǎn)擊按鈕利用主線程傳送數(shù)據(jù):CString sIP, sSend;GetDlgItemText(IDC_EDIT_IP, sI
50、P);GetDlgItemText(IDC_EDIT_SEND, sSend);SOCKADDR_IN addrClient; /客戶端地址/3.指明服務(wù)器地址addrClient.sin_addr.S_un.S_addr = inet_addr(sIP);addrClient.sin_family = AF_INET;addrClient.sin_port = htons(6000);sendto(sockSrv, sSend, 100, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR);m_list.AddString(sSend);SetDl
51、gItemText(IDC_EDIT_SEND, "");:SendMessage(m_list.m_hWnd, WM_VSCROLL, SB_BOTTOM, NULL);使用異步套接字模式進(jìn)行Windows網(wǎng)絡(luò)編程上面我們使用了阻塞套接字模式,并發(fā)現(xiàn)利用阻塞套接字的時(shí)候單線程已經(jīng)不能解決問提了,微軟提供了另外一種方法,即異步套接字模式。所謂異步套接字,是指基于消息的、非阻塞模式的套接字,在這種模式下我們是可以采用單線程解決問題的。該異步策略主要是通過Windows Sockets的異步選擇函數(shù)WSAAsyncSelect()來實(shí)現(xiàn)的,該函數(shù)提供了消息機(jī)制的網(wǎng)絡(luò)事件選擇,當(dāng)使用它登記的網(wǎng)絡(luò)事件發(fā)生時(shí),Windows應(yīng)用程序相應(yīng)的窗口函數(shù)將收到一個(gè)消息,消息中
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年稀土發(fā)光材料合作協(xié)議書
- 2024年電子裝聯(lián)專用設(shè)備合作協(xié)議書
- 鹽城師范學(xué)院《統(tǒng)計(jì)學(xué)》2021-2022學(xué)年第一學(xué)期期末試卷
- 鹽城師范學(xué)院《數(shù)字創(chuàng)意項(xiàng)目體驗(yàn)》2021-2022學(xué)年第一學(xué)期期末試卷
- 2023年北京市平谷初三二模語文試卷及答案
- 2024自媒體代運(yùn)營合同(簡單版)
- 人教版四年級(jí)上冊(cè)數(shù)學(xué)第六單元《除數(shù)是兩位數(shù)的除法》測試卷帶答案(培優(yōu))
- 北師大版一年級(jí)下冊(cè)數(shù)學(xué)第四單元 有趣的圖形 測試卷及下載答案
- 2024床位租用合同范本
- 超臨界低碳物理發(fā)泡鞋底智能化升級(jí)改造項(xiàng)目環(huán)評(píng)報(bào)告表
- xx學(xué)校未成年人性教育工作方案
- 廣開(含解析)《形式與政策》你所從事的行業(yè)和工作《決定》中提出怎樣的改革舉措
- 什么是美術(shù)作品 課件-2024-2025學(xué)年高中美術(shù)湘美版(2019)美術(shù)鑒賞
- 2024-2030年組氨酸行業(yè)市場現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 教育信息化教學(xué)資源建設(shè)規(guī)劃
- 職業(yè)衛(wèi)生技術(shù)服務(wù)機(jī)構(gòu)檢測人員考試真題題庫
- 上海市交大附中附屬嘉定德富中學(xué)2024-2025學(xué)年九年級(jí)上學(xué)期期中考數(shù)學(xué)卷
- 屠宰場食品安全管理制度
- 部編版(2024秋)語文一年級(jí)上冊(cè) 6 .影子課件
- 2024秋期國家開放大學(xué)專科《刑事訴訟法學(xué)》一平臺(tái)在線形考(形考任務(wù)一至五)試題及答案
- 2024年大學(xué)生就業(yè)創(chuàng)業(yè)知識(shí)競賽題庫及答案(共350題)
評(píng)論
0/150
提交評(píng)論