




已閱讀5頁,還剩38頁未讀, 繼續(xù)免費閱讀
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
5socket 簡介在網(wǎng)絡(luò)編程中最常用的方案便是 Client/Server (客戶機/服務(wù)器)模型。在這種方案中客戶應用程序向服務(wù)器程序請求服務(wù),一個服務(wù)器程序通常用一個眾所周知的地址監(jiān)聽對服務(wù)的請求,也就是說服務(wù)器進程一直處于休眠狀態(tài),直到一個客戶向這個服務(wù)器的地址提出了連接請求。在這個時刻,服務(wù)程序被驚醒并且為客戶提供服務(wù),即對客戶的請求作出適當?shù)姆磻?。為了方便這種 Client/Server 模型的網(wǎng)絡(luò)編程,90年代初由微軟聯(lián)合了其他幾家公司共同制定了一套WINDOWS 下的網(wǎng)絡(luò)編程接口,即 Windows Sockets 規(guī)范,它不是一種網(wǎng)絡(luò)協(xié)議,而是一套開放的、支持多種協(xié)議的 Windows 下的網(wǎng)絡(luò)編程接口?,F(xiàn)在的 Winsock 已經(jīng)基本上實現(xiàn)了與協(xié)議無關(guān),可以使用 Winsock 來調(diào)用多種協(xié)議的功能,但較常使用的是 TCP/IP 協(xié)議。Socket 實際在計算機中提供了一個通信端口,可以通過這個端口與任何一個具有 Socket 接口的計算機通信。應用程序在網(wǎng)絡(luò)上傳輸,接收的信息都通過這個 Socket 接口來實現(xiàn)。微軟為 Visual C+定義了 Winsock 類,如 CAsyncSocket 類和派生于 CAsyncSocket 的 CSocket 類,它們簡單易用,可以使用這些類來實現(xiàn)自己的網(wǎng)絡(luò)程序。但是為了更好的了解 Winsock API 編程技術(shù),這里先探討怎樣使用底層的 API 函數(shù)實現(xiàn)簡單的 Winsock 網(wǎng)絡(luò)應用程式設(shè)計,分別說明如何在 Server 端和 Client 端操作 Socket,實現(xiàn)基于 TCP/IP 的數(shù)據(jù)傳送,最后給出相關(guān)的代碼。l 一些網(wǎng)絡(luò)的基本概念n 同步:指發(fā)送方發(fā)出數(shù)據(jù)后,等收到接收方發(fā)回的響應,才發(fā)下一個數(shù)據(jù)包的通信方式n 異步:指的是發(fā)送方不等接收方響應,便接著發(fā)下個數(shù)據(jù)包的通信方式n 阻塞:指調(diào)用某函數(shù)時,直到該函數(shù)完成操作,才返回;否則一直阻塞在該調(diào)用上n 非阻塞:指調(diào)用某操作時,不管操作是否成功都立即返回,而不會掛在該操作上5.1 C/S模式下的socket通信C/S模式即我們平時經(jīng)常提到的 客戶端服務(wù)器 模式。l 網(wǎng)絡(luò)軟件的通用體系結(jié)構(gòu)客戶(Client)和服務(wù)器(Server)是指通信中所涉及的兩個應用進程。客戶端服務(wù)器方式所描述的是進程之間服務(wù)和被服務(wù)的關(guān)系。在下圖中,主機A運行客戶程序而主機B運行服務(wù)器程序。l 最簡單的Socket通信流程一個只有客戶方向服務(wù)方發(fā)信息的單向通信,并且也只有客戶方會主動提出斷開連接的最簡單的情形(相反過程的原理是一樣的),其雙方Socket之間的關(guān)系如下圖所示。由上面這個十分簡單的過程很容易得出最簡單的Socket通信流程,如下圖所示??蛻魴C/服務(wù)器模式的建立基于以下兩點:l 非對等作用l 通信完全是異步的??蛻魴C/服務(wù)器模式在操作過程中采取的是主動請示方式:首先服務(wù)器方要先啟動,并根據(jù)請示提供相應服務(wù)。網(wǎng)絡(luò)字節(jié)順序不同的計算機存放多字節(jié)值的順序不同,有的機器在起始地址存放低位字節(jié)(低位先存),有的機器在起始地址存放高位字節(jié)(高位先存)。基于Intel的CPU,即我們常用的PC機采用的是低位先存。為保證數(shù)據(jù)的正確性,在網(wǎng)絡(luò)協(xié)議中需要指定網(wǎng)絡(luò)字節(jié)順序,TCP/IP協(xié)議使用16位整數(shù)和32位整數(shù)的高位先存格式。由于不同的計算機存放數(shù)據(jù)字節(jié)的順序不同,這樣發(fā)送方發(fā)送數(shù)據(jù)后,即使接收方接受到該數(shù)據(jù),也有可能無法查看所接收到的數(shù)據(jù)。所以在網(wǎng)絡(luò)中不同主機間進行通信,要統(tǒng)一采用網(wǎng)絡(luò)字節(jié)順序。大端、小端l 小端法(Little-Endian)就是低位字節(jié)排放在內(nèi)存的低地址端即該值的起始地址,高位字節(jié)排放在內(nèi)存的高地址端。 (主機字節(jié)順序)l 大端法(Big-Endian)就是高位字節(jié)排放在內(nèi)存的低地址端即該值的起始地址,低位字節(jié)排放在內(nèi)存的高地址端。(網(wǎng)絡(luò)字節(jié)順序)舉個簡單的例子,對于整形0x12345678。它在大端法和小端法的系統(tǒng)內(nèi)中,分別如圖所示的方式存放。字節(jié)順序轉(zhuǎn)換Windows Sockets 的htons函數(shù)將把一個u_short類型的值從主機字節(jié)順序轉(zhuǎn)換為TCP/IP網(wǎng)絡(luò)字節(jié)順序,函數(shù)原型如下:u_short htons(u_short hostshort);參數(shù):l hostshort:一個以主機字節(jié)順序表示的16位數(shù)。與htons函數(shù)相類似的還有一個函數(shù)htonl,該函數(shù)將把一個u_long類型的值從主機字節(jié)順序轉(zhuǎn)換為TCP/IP網(wǎng)絡(luò)字節(jié)順序,函數(shù)原型如下:u_long htonl(u_long hostlong);參數(shù):l hostlong:一個以主機字節(jié)順序表示的32位數(shù)。5.2. 基于 TCP/IP 的 socket 編程TCP/IP協(xié)議的核心部分是傳輸層協(xié)議(TCP、UDP),網(wǎng)絡(luò)層協(xié)議(IP)和物理接口層,這三層通常是在操作系統(tǒng)內(nèi)核中實現(xiàn)。因此用戶一般不涉及。編程時,編程界面有兩種形式:一、是由內(nèi)核直接提供的系統(tǒng)調(diào)用;二、使用以庫函數(shù)方式提供的各種函數(shù)。前者為核內(nèi)實現(xiàn),后者為核外實現(xiàn)。用戶服務(wù)要通過核外的應用程序才能實現(xiàn),所以要使用套接字(socket)來實現(xiàn)。5.2.1 服務(wù)器端操作流程l 加載套接字庫。在初始化階段調(diào)用 WSAStartup()此函數(shù)在應用程序中初始化 Windows Sockets DLL,只有此函數(shù)調(diào)用成功后,應用程序才可以再調(diào)用其他Windows Sockets DLL 中的 API 函數(shù)。該函數(shù)原型如下:int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);參數(shù):n wVersionRequested:Socket的版本號WORD 類型、MAKEWORD、LOBYTE、HIBYTE 宏 u WORD 類型是一個 16 位的無符號整型, 在 WTYPES.H 中被定義為: typedef unsigned short WORD; 其目的是提供兩個字節(jié)的存儲, 在 Socket 中這兩個字節(jié)可以表示主版本號和副版本號。 u 使用 MAKEWORD 宏可以給一個 WORD 類型賦值。例如要表示主版本號 2,副版本號 0,可以使用如下代碼: WORD wVersionRequested; wVersionRequested = MAKEWORD(2, 0); 注意:低位內(nèi)存存儲主版本號2,高位內(nèi)存存儲副版本號0,其值為 0x0002。 u 使用宏 LOBYTE 可以讀取 WORD 的低位字節(jié)HIBYTE 可以讀取 WORD 的高位字節(jié)。n lpWSAData:指向一個用于存儲 Socket 庫信息的WSAStartup結(jié)構(gòu)。返回值:n 等于0,初始化成功n 不等于0, 初始化失敗l 創(chuàng)建套接字 Socket初始化 WinSock 的動態(tài)連接庫后,需要在服務(wù)器端建立一個監(jiān)聽的 Socket,為此可以調(diào)用 Socket()函數(shù)用來建立這個監(jiān)聽的 Socket,并定義此 Socket 所使用的通信協(xié)議.此函數(shù)調(diào)用成功返回 Socket 對象,失敗則返回 INVALID_SOCKET.調(diào)用 WSAGetLastError()可得知原因,所有 WinSocket 的 API 函數(shù)都可以使用這個函數(shù)來獲取失敗的原因。函數(shù)原型如下:SOCKET socket(int af,int type,int protocol)參數(shù): n af: 代表網(wǎng)絡(luò)地址族,目前只有一種取值有效,即 AF_INET, 代表 internet 地址族。n type: 代表網(wǎng)絡(luò)協(xié)議類型,SOCK_DGRAM代表UDP協(xié)議,SOCK_STREAM代表TCP協(xié)議。 n protocol: 指定網(wǎng)絡(luò)地址族特殊協(xié)議,目前無用,賦值0即可。如果要建立的是遵從 TCP/IP 協(xié)議的 socket,第二個參數(shù) type 應為 SOCK_STREAM,如為 UDP(數(shù)據(jù)報)的socket,應為 SOCK_DGRAM。sockets(套接字)編程有三種:流式套接字(SOCK_STREAM),數(shù)據(jù)報套接字(SOCK_DGRAM),原始套接字(SOCK_RAW)?;?TCP 的 socket 編程是采用的流式套接字。返回值:SOCKET 類型SOCKET 是 socket 套接字類型,在 WINSOCK2.H 中有如下定義: typedef unsigned u_int; typedef u_int SOCKET; 可知套接字實際上就是一個無符號整形,它將被 Socket 環(huán)境管理和使用。 套接字將被創(chuàng)建、設(shè)置、用來發(fā)送和接收數(shù)據(jù),最后會被關(guān)閉。 l 將創(chuàng)建的套接字綁定到本機地址的某一端口上。bind接下來要為服務(wù)器端定義的這個監(jiān)聽的 Socket 指定一個地址及端口(Port),這樣客戶端才知道待會要連接哪一個地址的哪個端口,為此要調(diào)用 bind()函數(shù),該函數(shù)調(diào)用成功返回 0,否則返回 SOCKET_ERROR.int bind( SOCKET s,const struct sockaddr FAR *name,int namelen);參 數(shù): n s:被綁定的套接字n name: 是一個sockaddr結(jié)構(gòu)指針,該結(jié)構(gòu)中包含了要綁定的地址和端口。n namelen:第二個參數(shù)name 的長度;如果使用者不在意地址或端口的值,那么可以設(shè)定地址為 INADDR_ANY,及 Port 為 0。對于多接口主機使用INADDR_ANY指定了一個通配地址,讓該主機的任何一個IP地址都匹配。Windows Sockets會自動將其設(shè)定適當之地址及 Port(1024 到 5000 之間的值)。此后可以調(diào)用 getsockname()函數(shù)來獲知其被設(shè)定的值。下面對其涉及的類型作一番解析: n sockaddr_in類型: sockaddr_in 定義了socket發(fā)送和接收數(shù)據(jù)包的地址,其定義如下: strucr sockaddr_in short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero8; ; 其中 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; ; 首先闡述 in_addr 的信義。 很顯然它是一個存儲 ip 地址的聯(lián)合體,有三種表達方式: u 第一種用四個字節(jié)來表示IP地址的四個數(shù)字; u 第二種用兩個雙字節(jié)來表示IP地址; u 第三種用一個長整型來表示IP地址; 給 in_addr 賦值的一種最簡單方法是使用 inet_addr 函數(shù),它可以把一個代表IP地址的字符串賦值。轉(zhuǎn)換為in_addr類型。如: addrServer.sin_addr = inet_addr(); 其反函數(shù)是 inet_ntoa,可以把一個 in_addr 類型轉(zhuǎn)換為一個字符串。 sockaddr_in的含義比in_addr的含義要廣泛,其各個字段的含義和取值如下: u 第一字段 short sin_family,代表網(wǎng)絡(luò)地址族,如前所述,只能取值A(chǔ)F_INET; u 第二字段 u_short sin_port,代表IP地址端口,由程序員指定; u 第三字段 struct in_addr sin_addr,代表IP地址; u 第四個字段char sin_zero8,是為了保證sockaddr_in與SOCKADDR類型的長度相等而填充進來的字段。 n sockaddr 類型sockaddr 類型是用來表示 Socket 地址的類型,同 socketaddr_in 類型相比,sockaddr 的適用范圍更廣,因為sockeaddr_in只適用于 TCP/IP 地址。sockaddr 的定義如下: struct sockaddr ushort sa_family; char sa_data14; ; 可知sockaddr 的16個字節(jié),而sockaddr_in也有16個字節(jié),所以sockaddr_in是可以強制類型轉(zhuǎn)換為sockadddr的。事實上也往往使用這種方法。l 為套接字設(shè)置監(jiān)聽模式,準備客戶請求。listen當服務(wù)器端的 Socket 對象綁定完成之后,服務(wù)器端必須建立一個監(jiān)聽的隊列來接收客戶端的連接請求.listen()函數(shù)使服務(wù)器端的 Socket 進入監(jiān)聽狀態(tài),并設(shè)定可以建立的最大連接數(shù)(目前最大值限制為 5,最小值為 1).該函數(shù)調(diào)用成功返回 0,否則返回 SOCKET_ERROR。int listen(SOCKET s,int backlog );參 數(shù): n s:需要建立監(jiān)聽的 Socket;n backlog:最大連接個數(shù);l 等待客戶請求到來。當請求到來,將接受連接請求,并返回一個新的對應于此次連接的套接字。accept當 Client 提出連接請求時,Server 端 hwnd 視窗會收到 Winsock Stack 送來自定義的一個消息,這時可以分析 lParam,然后調(diào)用相關(guān)的函數(shù)來處理此事件。為了使服務(wù)器端接受客戶端的連接請求,就要使用accept()函數(shù),該函數(shù)新建一 Socket 與客戶端的 Socket 相通,原先監(jiān)聽之 Socket 繼續(xù)進入監(jiān)聽狀態(tài),等待他人的連接要求。該函數(shù)調(diào)用成功返回一個新產(chǎn)生的 Socket 對象,否則返回 INVALID_SOCKET。SOCKET accept(SCOKET s,struct sockaddr FAR *addr,int FAR *addrlen );參數(shù): n s: 監(jiān)聽套接字n addr:存放來連接的客戶端的地址、端口信息;n addrlen:addr 的長度l 用新返回的套接字和客戶端進行通信。send/recvsend函數(shù)通過一個已建立連接的套接字發(fā)送數(shù)據(jù),函數(shù)聲明如下:int send(SOCKET s, const char FAR *buf, int len, int flags);參數(shù):n s: 是一個已建立連接的套接字。n buf:指向一個緩沖區(qū),該緩沖區(qū)包含將要傳遞的數(shù)據(jù)。n len:緩沖區(qū)的長度。n flags:收發(fā)數(shù)據(jù)方式的標識,如果不需要特殊要求可以設(shè)置為0。調(diào)用send函數(shù)向客戶端發(fā)送數(shù)據(jù),注意這個函數(shù)使用的套接字需要使用已建立連接的那個套接字,而不是用于監(jiān)聽的那個套接字。recv函數(shù)從一個已連接的套接字接收數(shù)據(jù)。函數(shù)原型如下:int recv(SOCKET s,char FAR* buf, int len, int flags);參數(shù):n s:建立連接之后準備接收數(shù)據(jù)的那個套接字。n buf:指向緩沖區(qū)的指針,用來保存接收的數(shù)據(jù)。n len:緩沖區(qū)的長度。n flags:收發(fā)數(shù)據(jù)方式的標識,如果不需要特殊要求可以設(shè)置為0。發(fā)送完數(shù)據(jù)之后還可以從客戶端接收數(shù)據(jù),這可以使用recv函數(shù),應注意該函數(shù)的第一個參數(shù)也應該是建立連接之后的那個套接字,并且定義一個字符數(shù)組recvBuf,用來保存接收的數(shù)據(jù)。l 通信結(jié)束后,關(guān)閉套接字。 closesocket結(jié)束服務(wù)器和客戶端的通信連接是很簡單的,這一過程可以由服務(wù)器或客戶機的任一端啟動,只要調(diào)用closesocket()就可以了,而要關(guān)閉 Server 端監(jiān)聽狀態(tài)的 socket,同樣也是利用此函數(shù).另外,與程序啟動時調(diào)用 WSAStartup()憨數(shù)相對應,程式結(jié)束前,需要調(diào)用 WSACleanup()來通知 Winsock Dll 釋放 Socket 所占用的資源.這兩個函數(shù)都是調(diào)用成功返回 0,否則返回 SOCKET_ERROR。int PASCAL FAR closesocket( SOCKET s );參數(shù): n s:Socket 的識別碼;int PASCAL FAR WSACleanup( void );n 參數(shù): 無5.2.2 服務(wù)器端代碼實現(xiàn)#include #include using namespace std;int main()/加載套接字庫WORD wVersion;WSADATA wsaData;int err;wVersion = MAKEWORD(1, 1);err = WSAStartup(wVersion, &wsaData);/加載套接字庫失敗if (err != 0)return -1;/判斷版本號是否正確if (LOBYTE(wsaData.wVersion) != 1 | HIBYTE(wsaData.wVersion) != 1)WSACleanup();return -2;/創(chuàng)建套接字, 流式套接字SOCKET socSev = socket(AF_INET, SOCK_STREAM, 0);/定義結(jié)構(gòu)體變量,存儲ip,端口號SOCKADDR_IN addr_in;addr_in.sin_family = AF_INET;/地址族addr_in.sin_addr.S_un.S_addr = htonl(INADDR_ANY);/IPaddr_in.sin_port = htons(6000);/端口/綁定套接字bind(socSev, (sockaddr*)&addr_in, sizeof(addr_in);/將套接字設(shè)置為監(jiān)聽模式,準備接收客戶請求listen(socSev, 5);/定義結(jié)構(gòu)體變量,接收客戶端IP,端口號SOCKADDR_IN client_addr;int len = sizeof(client_addr);while (1)/等待客戶請求cout 服務(wù)器等待客戶端請求. endl;SOCKET socClient = accept(socSev, (SOCKADDR*)&client_addr, &len);char sendBuf1024;/格式化字符串sprintf(sendBuf, 這里是 %s 傳智播客-C+學院!, inet_ntoa(client_addr.sin_addr);/發(fā)送數(shù)據(jù)send(socClient, sendBuf, strlen(sendBuf) + 1, 0);char recvBuf1024;/接收數(shù)據(jù)recv(socClient, recvBuf, strlen(recvBuf) + 1, 0);/打印接收到的數(shù)據(jù)cout 接收到的數(shù)據(jù): recvBuf endl;/關(guān)閉套接字closesocket(socClient);return 0;5.2.3 客戶端操作流程l 加載套接字庫(WSAStartup())l 創(chuàng)建套接字(socket()。l 向服務(wù)器發(fā)出連接請求(connect()。對于客戶端來說,它不需要綁定,可以直接連接服務(wù)器。這可以通過調(diào)用connect函數(shù)與服務(wù)器建立一個連接。其函數(shù)原型如下:int connect(SOCKET s, const struct Sockaddr FAR* name , int namelen);參數(shù):n s:客戶端用于首發(fā)數(shù)據(jù)的套接字。n name:指定網(wǎng)路主機的IP地址和端口信息。n namelen:第二個參數(shù)的長度。想要與服務(wù)器建立連接,首先需要定義一個地址結(jié)構(gòu)體(SOCKADDR_IN)變量,并對其成員進行賦值,設(shè)定服務(wù)端的IP地址和端口號,這里的端口需要與服務(wù)器使用的端口保存一致,而且使用網(wǎng)路字節(jié)順序。l 和服務(wù)器端進行通信(send()/recv().l 通信結(jié)束后關(guān)閉套接字和加載的套接字庫(closesocket()/WSACleanup()。5.2.4 客戶端代碼實現(xiàn)#include #include using namespace std;int main()WORD wVersion;WSADATA wsaData;int err;/初始化版本號信息wVersion = MAKEWORD(1, 1);/加載套接字庫err = WSAStartup(wVersion, &wsaData);if (err != 0)return -1;/檢測套接字版本信息if (LOBYTE(wsaData.wVersion) != 1 | HIBYTE(wsaData.wVersion) != 1)WSACleanup();return -2;/創(chuàng)建套接字SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);/定義結(jié)構(gòu)體變量,存儲ip,端口號SOCKADDR_IN addr_in;addr_in.sin_family = AF_INET;/地址族addr_in.sin_addr.S_un.S_addr = inet_addr();/IPaddr_in.sin_port = htons(6000);/端口號 與服務(wù)器相同/向服務(wù)器發(fā)送連接請求connect(sock, (sockaddr*)&addr_in, sizeof(addr_in);/接收數(shù)據(jù)char recvBuf1024;recv(sock, recvBuf, strlen(recvBuf) + 1, 0);cout 接收到服務(wù)器數(shù)據(jù): recvBuf endl;/向服務(wù)器發(fā)送數(shù)據(jù)char sendBuf = Windows Socket 通信測試程序;send(sock, sendBuf, sizeof(sendBuf), 0);/關(guān)閉套接字closesocket(sock);WSACleanup();system(pause);return 0;5.2.5面向連接(TCP)的套接字的系統(tǒng)調(diào)用時序圖5.3. 基于 UDP 無連接的 socket 編程5.3.1 服務(wù)器端操作流程l 加載套接字庫(WSAStartup)l 創(chuàng)建套接字 (socket)l 將創(chuàng)建的套接字綁定到一個本地地址和端口上 (bind)l 等待接收數(shù)據(jù)。后與客戶端實現(xiàn)實時交流 (recvfrom / sendto)recvfrom函數(shù)將接收一個數(shù)據(jù)報信息并保存源地址。函數(shù)原型如下:int recvfrom(SOCKET s, char FAR* buf, int len, int flags,struct sockaddr FAR* from, int FAR* fromlen);參數(shù):n s:準備接收數(shù)據(jù)的套接字。n buf:指向緩沖區(qū)的指針,該緩沖區(qū)用來接收數(shù)據(jù)。n len:緩沖區(qū)的長度。n flags:收發(fā)數(shù)據(jù)方式的標識,如果不需要特殊要求可以設(shè)置為0。n from:指向地址的結(jié)構(gòu)體指針,用來接收發(fā)送數(shù)據(jù)方的地址信息。n fromlen:地址結(jié)構(gòu)的大小。sendto函數(shù)將向一個特定的目的方放松數(shù)據(jù)。其函數(shù)原型如下:int sendto(SOCKET s, const char FAR* buf, int len, int flags,const struct sockaddr FAR* to, int tolen);參數(shù):n s:是一個(可能已建立連接的)套接字描述符。n buf:指向緩沖區(qū)的指針,該緩沖區(qū)包含將要發(fā)送的數(shù)據(jù)。n len:指定緩沖區(qū)中的數(shù)據(jù)長度。n flags:收發(fā)數(shù)據(jù)方式的標識,如果不需要特殊要求可以設(shè)置為0。n to:該指針指向目標套接字的地址;n tolen:指定的地址長度l 關(guān)閉套接字 (closesocket)5.3.2 服務(wù)器端代碼實現(xiàn)#include #include using namespace std;int main()WORD wVersion;WSADATA wsaData;int err;wVersion = MAKEWORD(1, 1);/加載套接字庫err = WSAStartup(wVersion, &wsaData);if (err != 0)return -1;/判斷套接字版本號if (LOBYTE(wsaData.wVersion) != 1 | HIBYTE(wsaData.wVersion) != 1)/終止對套接字庫的使用WSACleanup();return -2;/創(chuàng)建套接字SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);/定義結(jié)構(gòu)體變量,保存ip和端口號sockaddr_in addr_in;addr_in.sin_addr.S_un.S_addr = htonl(INADDR_ANY);/IPaddr_in.sin_family = AF_INET;/地址族addr_in.sin_port = htons(6000);/端口號/綁定套接字bind(sock, (sockaddr*)&addr_in, sizeof(addr_in);/等待并接受數(shù)據(jù)/定義結(jié)構(gòu)體,保存客戶端ip,端口號sockaddr_in addrClient;int len = sizeof(sockaddr_in);char recvBuf1024;recvfrom(sock, recvBuf, sizeof(recvBuf), 0, (sockaddr*)&addrClient, &len);cout 接收到的客戶端數(shù)據(jù): recvBuf endl;/向客戶端發(fā)送數(shù)據(jù)char sendBuf = 你好,我是程序猿.;sendto(sock, sendBuf, sizeof(sendBuf), 0, (sockaddr*)&addrClient, len);/關(guān)閉套接字closesocket(sock);WSACleanup();system(pause);return 0;5.3.3 客戶端操作流程l 加載套接字庫 (WSAStartup)l 創(chuàng)建套接字 (socket)l 向服務(wù)器發(fā)送數(shù)據(jù)。后與服務(wù)端實現(xiàn)實時交流 (recvfrom / sendto)l 關(guān)閉套接字 (closesocket)5.3.4 客戶端代碼實現(xiàn)#include #include using namespace std;int main()WORD wVersion;WSADATA wsaData;int err;wVersion = MAKEWORD(1, 1);/加載套接字庫err = WSAStartup(wVersion, &wsaData);if (err != 0)return -1;/檢查套接字庫版本if (LOBYTE(wsaData.wVersion) != 1 | HIBYTE(wsaData.wVersion) != 1)WSACleanup();return -2;/創(chuàng)建套接字SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);/定義結(jié)構(gòu)體變量,保存IP端口號sockaddr_in addrIn;addrIn.sin_addr.S_un.S_addr = inet_addr();addrIn.sin_family = AF_INET;addrIn.sin_port = htons(6000);/發(fā)送數(shù)據(jù)int len = sizeof(addrIn);char sendBuf = 你好,我是峨眉滅絕師太!;sendto(sock, sendBuf, sizeof(sendBuf), 0, (sockaddr*)&addrIn, len);/接收數(shù)據(jù)char recvBuf1024;recvfrom(sock, recvBuf, sizeof(recvBuf), 0, (sockaddr*)&addrIn, &len);cout 接收到服務(wù)器數(shù)據(jù): recvBuf endl;/關(guān)閉套接字closesocket(sock);WSACleanup();system(pause);return 0;5.3.5 無連接協(xié)議(UDP)的套接字調(diào)用時序圖5.4. MFC下的Socket編程5.4.1 CAsyncSocket類l 簡介CAsyncSocket 類是在很低的層次上對windows socket API進行了封裝,它的成員函數(shù)和winsock API的函數(shù)調(diào)用直接對應,一個CAsyncSocket對象代表了一個windows套接字,它是網(wǎng)絡(luò)通信的端點。該類將根據(jù)不同的windows套接字消息嗲用CAsyncSocket類的回調(diào)函數(shù)。如果熟悉網(wǎng)絡(luò)通信細節(jié),仍希望充分利用winsock API編程的靈活性,并能安全的控制程序,同時還希望利用windows對網(wǎng)絡(luò)事件通知的回調(diào)函數(shù)的便利,就應該使用CAsyncSocket類進行編程。同時必須自己處理阻塞問題、字節(jié)順序問題和字符串轉(zhuǎn)換問題。CAsyncSocket屬于異步非阻塞類。CAsyncSocket類采用了windows socket中的WSAAsyncSelect模型。l 覆蓋通知函數(shù) OnAccept 通知偵聽套接字,它可以通過調(diào)用Accept,接受掛起連接請求 OnClose 通知套接字,關(guān)閉對它的套接字連接 OnConnect 通知連接套接字,連接嘗試已經(jīng)完成,無論成功或失敗 OnOutOfBandData 通知接收套接字,在套接字上有帶外數(shù)據(jù)讀入,通常是忙消息 OnReceive 通知偵聽套接字,通過調(diào)用Receive恢復數(shù)據(jù) OnSend 通知套接字,通過調(diào)用Send,它可以發(fā)送數(shù)據(jù) l 一般編程步驟n 服務(wù)器端:CAsyncSocket m_server;/創(chuàng)建套接字if (m_server.Create(m_iPort) = FALSE)MessageBox(Lserver socket create failed);return;/監(jiān)聽套接字if (m_server.Listen() = FALSE)MessageBox(Lserver socket listen failed);return; 至于為什么沒有bind,是因為create函數(shù)里已經(jīng)做了bind工作。n 客戶端if (m_client.Create() = FALSE)MessageBox(Lclient socket create failed);return;/此處不能通過返回值來判斷連接失敗,因為此socket是異步的if (m_client.Connect(L,m_iPort) = FALSE)MessageBox(Lclient socket connect failed); return;注意看注釋,所以代碼不能這樣判斷是否連接成功。要重載OnConnect,然后在函數(shù)中來判斷是否連接成功。void CMyAsyncSocket:OnConnect(int nErrorCode)/ TODO: Add your specialized code here and/or call the base classif (nErrorCode = 0)AfxMessageBox(Lconnect success);CAsyncSocket:OnConnect(nErrorCode);l CAsyncSocket編程注意問題:n 阻塞處理。CAsyncSocket對象專用于異步操作,不支持阻塞工作模式,如果應用程序需要支持阻塞操作,必須自己解決。n 字節(jié)順序的轉(zhuǎn)換。在不同的結(jié)構(gòu)類型的計算機之間進行數(shù)據(jù)傳輸時,可能會有計算機之間字節(jié)存儲順序不一致的情況,需要自己對不用的字節(jié)順序進行轉(zhuǎn)換。n 字符串轉(zhuǎn)換。同樣不同結(jié)構(gòu)類型的計算機的字符串順序也可能不同,需要自行轉(zhuǎn)換。n 在使用CAsyncSocket之前,必須調(diào)用AfxSocketInit初始化WinSock環(huán)境,而AfxSocketInit會創(chuàng)建一個隱藏的CSocketWnd對象,由于這個對象由Cwnd派生,因此它能夠接收Windows消息。一方面它會接受各個CAsyncSocket的狀態(tài)報告,另一方面它能捕捉系統(tǒng)發(fā)出的各種SOCKET事件。l 通信流程5.4.2 CSocket類CSocket類是從CAsyncsocket派生而來的,它繼承了CAsyncsocket對WindowsSockets API的封裝。與CAsyncsocket對象相比,CSocket對象代表了WindowsSockets API的更高一級的抽象化。CSocket是MFC在CAsyncSocket基礎(chǔ)上派生的一個同步阻塞Socket的封裝類。5.4.1 常用的函數(shù)和注意事項l 常用函數(shù)類成員函數(shù)功能CSocketCreate初始化,創(chuàng)建套接字CASyncSocketSocket創(chuàng)建socketCASyncSocketSetSockOpt設(shè)置套接字選項CASyncSocketBind綁定地址端口CASyncSocketConnect對對等套接字建立連接CASyncSocketListen監(jiān)聽即將到來的連接請求CASyncSocketAccept接受套接字上的連接CASyncSocketSend給連接的套接字發(fā)送數(shù)據(jù)CASyncSocketReceive從連接的套接字接收數(shù)據(jù)CASyncSocketClose關(guān)閉套接字(不等于delete)l 注意事項n 在使用MFC編寫socket程序時,必須要包含都文件。n AfxSocketInit() 這個函數(shù),在使用CSocket前一定要先調(diào)用該函數(shù),否則使用CSocket會出錯;并且該函數(shù)還有一個重要的使用方式,就是在某個線程下使用 CSocket 前一定要調(diào)用,就算主線程調(diào)用了該函數(shù),在子線程下使用 CSocket 也要先調(diào)用該函數(shù),要不會出錯。n 還要注意的是, Create 方法已經(jīng)包含了 Bind 方法,如果是以 Create 方法初始化的前提下不能再調(diào)用 Bind ,要不一定出錯。5.4.2 代碼實現(xiàn)設(shè)計兩個對話框應用程序,通過TCP/IP進行通信,使用MFC的CSocket類實現(xiàn)服務(wù)器端和客戶端之間的相互通信。服務(wù)器服務(wù)器端的socket通信需要用到兩個socket:1. 用來監(jiān)聽連接的socket2. 用來和客戶端通信的socket,此socket中保存了客戶端信息因此服務(wù)器端需要添加兩個繼承自CSocket的類,分別起名為CServerSocket、CConnectSocket。l CServerSocket 此socket主要用來監(jiān)聽客戶端請求,當有請求到來時,MFC框架將調(diào)用OnAccept函數(shù),所以我們需要重寫CSocket類的OnAccept函數(shù)。/# ServerSocket.h 文件/class CServerSocket : public CSocketpublic:CServerSocket();virtual CServerSocket();void OnAccept(int nErrorCode);/開啟socket服務(wù)void StartServer(UINT nPort);/發(fā)送消息函數(shù)void MessageSend(const char* pMesg);private:/保存的是客戶端的socket信息CConnectSocket m_clientSock;在OnAccept函數(shù)中調(diào)用Accept函數(shù),接受客戶端請求,另外將客戶信息保存到m_clientSock中,使用此套接字對象與客戶端進行通信,發(fā)送信息可以直接調(diào)用API函數(shù)Send。在StartServer函數(shù)中做了兩件事兒,創(chuàng)建套接字,監(jiān)聽套接字。需要主要的是Create函數(shù)內(nèi)部已經(jīng)對套接字進行了綁定,所以不需要再次綁定。/# ServerSocket.cpp 文件/void CServerSocket:OnAccept(int nErrorCode)Accept(m_clientSock);CSocket:OnAccept(nErrorCode);void CServerSocket:StartServer(UINT nPort)if (!Create(nPort)AfxMessageBox(_T(Socket 創(chuàng)建失敗!);return;if (!Listen(5)AfxMessageBox(_T(Socket 監(jiān)聽失敗!);return;void CServerSocket:MessageSend(const char* pMesg)m_clientSock.Send(pMesg, strlen(pMesg) + 1);l CConnectSocket類此類中保存了客戶端信息,所以用來與客戶端進行通
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 企業(yè)業(yè)績評價課件
- 宣傳部干事工作總結(jié)
- 大班幼兒老師個人工作方案
- 兒童線描課件素材
- 社會工作小組方案設(shè)計
- 2025年中國保險絲護套行業(yè)市場發(fā)展前景及發(fā)展趨勢與投資戰(zhàn)略研究報告
- 中國靜止式變流器行業(yè)發(fā)展前景預測及投資方向研究報告
- 企業(yè)S管理培訓課件
- 兒童素描課件公眾號
- 企業(yè)EHS社會責任培訓課件
- (正式版)JBT 106-2024 閥門的標志和涂裝
- 肺結(jié)節(jié)手術(shù)的術(shù)后護理措施
- 2024版《工程項目現(xiàn)場簽證單、工程委托單》模版
- 節(jié)能環(huán)保抹灰施工管理策略
- 2023年貴州公務(wù)員考試申論試題(A卷)含解析
- 上尿路感染教學查房
- 水上光伏施工組織設(shè)計
- 讀書分享讀書交流會《全球通史》課件感
- 《企業(yè)信息管理》2023期末試題及答案
- DB53-T+1176.15-2023云南省行政村、社區(qū)編碼+第15部分:德宏傣族景頗族自治州
- 福建省南平市2022-2023學年高二下學期期末生物試題(解析版)
評論
0/150
提交評論