Winsock 編程流程


近期看了《Window程序設計》感覺在網絡方面講的不錯,講的非常通俗易懂。與大家一同交流吐舌頭

轉載請注明出處:http://blog.csdn.net/u010484477謝謝^_^

使用 Winsock 編程的一般步驟是比較固定的。


1.Winsock 庫的裝入、初始化和釋放


全部的 WinSock 函數都是從 WS2_32.DLL 庫導出的。VC++在默認情況下並沒有連接到該庫,假設想使用 Winsock API。就必須包括對應的庫文件。
#pragma commment(lib, "wsock32.lib")
WSAstartup 必須是應用程序首先調用的 Winsock 函數。它同意應用程序指定所需的Windows Sockets API 的版本號,獲取特定 Winsock 實現的具體信息。僅當這個函數成功運行之后。應用程序才干調用其它 Winsock API。

int WSAStartup(
WORD wVersionRequested, // 應用程序支持的最高WinSock庫版本號。高字節為次版本號號,低字節為主版本號號
LPWSADATA lpWSAData // 一個指向WSADATA結構的指針。它用來返回DLL 庫的具體信息
);
lpWSAData 參數用來取得 DLL 庫的具體信息。結構定義例如以下。
typedef struct WSAData {
WORD wVersion; // 庫文件建議應用程序使用的版本號
WORD wHighVersion;// 庫文件支持的最高版本號
char szDescription[WSADESCRIPTION_LEN+1]; // 庫描寫敘述字符串

char szSystemStatus[WSASYS_STATUS_LEN+1]; // 系統狀態字符串

unsigned short iMaxSockets;// 同一時候支持的最大套節字的數量

unsigned short iMaxUdpDg;// 2.0 版中已廢棄的參數

char FAR * lpVendorInfo; // 2.0 版中已廢棄的參數

} WSADATA, FAR * LPWSADATA;

函數調用成功返回 0。否則要調用 WSAGetLastError 函數查看出錯的原因。此函數的作用相當於 Win32 API GetLastError。它取得最后錯誤發生的代碼。


每個對WSAStartup的調用必須相應一個對WSACleanup的調用。這個函數釋放Winsock庫。
int  WSACleanup(void);


2.套節字的創建和關閉


使用套節字之前,必須調用 socket 函數創建一個套節字對象,此函數調用成功將返回套節字句柄

SOCKET socket(
int af,// 用來指定套節示使用的地址格式。WinSock中僅僅支持AF_INET
int type,// 用來指定套節字的類型

int protocol// 配合type 參數使用,用來指定使用的協議類型。能夠是IPPROTO_TCP等
);
type 參數用來指定套節字的類型。

套節字有流套節字數據報套節字原始套節字等,
以下是常見的幾種套節字類型定義:


SOCK_STREAM 流套節字。使用 TCP 協議提供有連接的可靠的傳輸
SOCK_DGRAM 數據報套節字,使用 UDP 協議提供無連接的不可靠的傳輸
SOCK_RAW 原始套節字,WinSock 接口並不使用某種特定的協議去封裝它。而是有程序自行處理數據報以及協議首部


當 type 參數指定為 SOCK_STREAM 和 SOCK_DGRAM 時。系統已經明白確定使用 TCP和 UDP 協議來工作,所以 protocol 參數能夠指定為 0。

函數運行失敗返回 INVALID_SOCKET(即-1),能夠通過調用 WSAGetLastError 取得錯誤代碼。
當不使用 socket 創建的套節字時,應該調用 closesocket 函數將它關閉。假設沒有發生錯誤,函數返回 0。否則返回 SOCKET_ERROR。

closesocket函數使用方法例如以下。


int closesocket(SOCKET s); // 函數惟一的參數就是要關閉的套節字的句柄


3.綁定套節字到指定的 IP 地址和port號


為套節字關聯本地地址的函數是 bind,使用方法例如以下。


int bind(
SOCKET s,// 套節字句柄
const struct sockaddr* name,// 要關聯的本地地址
int namelen// 地址的長度
     );
bind 函數用在沒有建立連接的套節字上,它的作用是綁定面向連接的或者無連接的套節字。當一個套節字被 socket 函數創建以后,他存在於指定的地址家族里,可是它是未命名的。

bind 函數通過安排一個本地名稱到未命名的 socket 建立此 socket 的本地關聯。

本地名稱包括 3個部分:主機地址協議號(分別為 UDP 或 TCP)port號

// 填充sockaddr_in結構
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.S_un.S_addr = INADDR_ANY;


// 綁定這個套節字到一個本地地址
if(::bind(s, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf("Failed bind() \n");
::WSACleanup();
return 0;
}
sockaddr_in 結構中的 sin_familly 字段用來指定地址家族。該字段和 socket 函數中的 af 參數的含義同樣。所以惟一能夠使用的值就是 AF_INET。

sin_port 字段和 sin_addr 字段分別指定套節字須要綁定的端口號IP 地址

放入這兩個字段的數據的字節順序必須是網絡字節順序
因為網絡字節順序和 Intel CPU 的字節順序剛好相反,所以必須首先用 htons 函數進行轉換。假設應用程序不關心所使用的地址。能夠為互聯網地址指定 INADDR_ANY,為port號指定 0。假設互聯網地址等於 INADDR_ANY,系統會自己主動使用當前主機配置的全部 IP 地址,這簡化了程序設計;假設port號等於 0,程序運行時系統會分配一個惟一的port號到這個應用程序,其值在 1024 到 5000 之間。應用程序能夠在 bind 之后使用 getsockname 來知道為它分配的地址。可是要注意。直到套節字連接上之后 getsockname 才可能填寫互聯網地址,由於對一個主機來說可能有多個地址是可用的。


4.設置套節字進入監聽狀態


listen 函數置套節字進入監聽狀態。
int listen(
SOCKET s,// 套節字句柄
int backlog// 監聽隊列中同意保持的尚未處理的最大連接數量
);


為了接受連接,首先使用 socket 函數創建一個套節字,然后使用 bind 函數綁定它到一個本地地址,再用 listen 函數為到達的連接指定一個 backlog。最后使用 accept 接受請求的連接

listen 僅應用在支持連接的套節字上,如 SOCK_STREAM 類型。函數成功運行之后,套節字 s 進入了被動模式,到來的連接會被通知,排隊等候接受處理。

在同一時間處理多個連接請求的server通常使用 listen 函數:假設一個連接請求到達,而且排隊已滿,client將接接收 WSAECONNREFUSED 錯誤。



5.接受連接請求
accept 函數用於接受到來的連接。
SOCKET accept(
SOCKET s,// 套節字句柄
struct sockaddr* addr,// 一個指向sockaddr_in結構的指針。用於取得對方的地址信息
int* addrlen// 是一個指向地址長度的指針
    );
該函數在 s 上取出未處理連接中的第一個連接,然后為這個連接創建一個新的套節字,返回它的句柄。新創建的套節字是處理實際連接的套節字,它與 s 有同樣的屬性。


程序默認工作在堵塞模式下,這樣的方式下假設沒有未處理的連接存在,accept 函數會一直等待下去直到有新的連接發生才返回。


addrlen 參數用於指定 addr 所指空間的大小。也用於返回返回地址實際長度

假設 addr或者 addrlen 是 NULL,則沒有關於遠程地址的信息返回。client程序在創建套節字之后,要使用 connect 函數請求與server連接。函數原型例如以下。



int connect(
SOCKET s,// 套節字句柄
const struct sockaddr FAR * name, // 一個指向 sockaddr_in 結構的指針。包括了要連接的server的地址信息。
int namelen// sockaddr_in結構的長度
   );
第一個參數 s 是此連接使用的client套節字。另兩個參數 name 和 namelen 用來尋址遠程套節字(正在監聽的server套節字)。


6.收發數據


對流套節字來說,一般使用 send 和 recv 函數來收發數據。

int send(
SOCKET s,// 套節字句柄
const char FAR * buf,// 要發送數據的緩沖區地址
int len,// 緩沖區長度
int flags// 指定了調用方式,通常設位0
     );
int recv( SOCKET s, char FAR * buf, int len, int );
send 函數在一個連接的套節字上發送緩沖區內的數據,返回發送數據的實際字節數。

recv函數從對方接收數據,並存儲指定的緩沖區。flags 參數在這兩函數中通常設為 0。


在堵塞模式下。send 將會堵塞線程的運行直到全部的數據發送完成(或者一個發生錯誤),
    recv 函數將返回盡可能多的當前可用信息。一直到緩沖區指定的大小。

下圖為server程序和客戶程序的創建過程:


 
        

最后是送大家一句我非常喜歡的話:

你必須用自己的努力換取成功,然后成功就會像一個大巴掌,打在那些以前看不起你的人臉上


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM