程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> WinSock編程流程

WinSock編程流程

編輯:關於C語言

看了王艷平老師編著的《Windows程序設計》裡的WinSock編程,在這裡整理一下。 


   u_short htons(u_short hostshort) //轉換一個u_short類型從主機字節順序到TCP/IP網絡字節順序 
   u_long htonl(u_long hostlong) //轉換一個u_long類型從主機字節順序到TCP/IP網絡字節順序 
   u_short ntohs(u_short netshort) //轉換一個u_short類型從TCP/IP網絡字節順序到主機字節順序 
   u_long ntohl(u_long netlong) //轉換一個u_long類型從TCP/IP網絡字節順序到主機字節順序

 


使用WinSock編程的一般步驟是確定的。可分為以下幾步:

①WinSock庫的裝入,初始化和釋放。

所有的WinSock函數都是從WS2_32.dll庫導出的,VC++在默認情況下並沒有連接到該庫,如果想使用WinSock API,就必須包含相應的庫文件。

#pragma comment(lib, “wsock32.lib”);

WSAstartup必須是應用程序首先調用的WinSock函數。他允許應用程序指定所需的Windows Sockets API的版本,獲取特定的WinSock實現的詳細信息。僅當這個函數調用成功之後,應用程序才能調用其他的Winsock API。

int WSAstartup(

WORD wVersionRequested, //應用程序支持的最高Winsock版本。高字節為次版本號,低字節為主版本號。

LPWSADATA lpWSAData)); //一個指向WSADATA結構的指針,他用來返回DLL庫的詳細信息。

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

每一個對WSAStartup的調用必須對應一個WSACleanup的調用,這個函數釋放Winsock庫。

int WSACleanup(void)。

②套接字的創建和關閉

使用套接字之前,必須調用socket函數創建一個套接字對象。此函數調用成功將返回套接字句柄,調用失敗返回INVALID_SOCKET(-1),可以通過WSAGetLastError取得錯誤信息。

SOCKET socket(

int af, //用來指定套接字使用的地址格式。WinSock中只支持AF_INET

int type, //用來指定套節字的類型,有SOCK_STREAM, SOCK_DGRAM, SOCK_RAW。

int protocol); //配合type參數使用,用來指定使用的協議類型,可以是IPPROTO_TCP等

當type參數指定為SOCK_STREAM或者SOCK_DGRAM時,系統已經明確確定使用TCP或者UDP協議來工作,所以protocol參數可以指定為0.

當不使用socket創建的套接字時,應該用closesocket函數將他關閉。如果沒有錯誤發生,函數返回0,否則返回SOCKET_ERROR。函數用法如下:

int closesocket(SOCKET s);

③綁定套接字到指定的IP地址和端口號(服務器)

為套接字關聯本地地址的函數是bind,用法如下

int bind(

SOCKET s, //套接字句柄

const struct sockaddr* name, //要關聯的本地地址

int namelen); //地址的長度

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

//填充sockaddr_sin結構

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_family字段用來指定地址家族,該字段和socket函數中的af參數含義相同,所以唯一可用的值就是AF_INET。sin_port和sin_addr字段分別指定套接字需要綁定的端口號的IP地址。放入這兩個字段的數據的字節順序必須是網絡字節順序。由於網絡字節順序和Intel CPU的字節順序剛好相反,所以必須首先調用htons函數進行轉換。

如果應用程序不關心所使用的地址,可以為互聯網地址指定為INADDR_ANY(系統自動使用當前主機配置的IP地址),為端口號指定為0(程序執行時會分配一個唯一的端口號到這個應用程序,其值在1024到5000之間)。應用程序可以在bind之後使用getsockname來知道為它分配的地址。但是要注意,知道套接字連接上之後getsockname才可能填寫互聯網地址,因為對一個主機來說可能有多個地址是可用的。

④設置套接字進入監聽狀態(服務器)

listen函數置套接字進入監聽狀態。

int listen(

SOCKET s, //套接字句柄

int backlog); //監聽對列中允許保—持的尚未處理的最大連接數量。

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

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

在同一時間內處理多個連接請求的服務器通常使用listen函數:如果一個連接請求到達,並且排隊已滿,客戶端將接收WSAECONNREFUSED錯誤。

⑤接受連接(服務器)

accept函數用於接收到來的請求。

SOCKET accept(

SOCKET s, //套接字句柄

struct sockaddr* addr, //一個指向sockaddr結構的指針,用於取得對方的地址信息

int* addrlen); //是一個指向地址長度的指針

該函數在s上取出未處理連接中的第一個連接,然後為這個連接創建一個新的套接字,返回它的句柄。新創建的套接字是處理實際連接的套接字。它與s有相同的屬性。

程序默認工作在阻塞模式下,這種方式下如果沒有未處理的連接存在,accept函數會一直等待下去直到有新的連接才返回。

addrlen參數用於指定addr所指空間的大小,也用於返回返回地址的實際長度。如果addr或者addrlen是NULL,則沒有關於遠程地址的信息返回。

⑥請求連接(客戶端)

客戶端在創建套接字之後,要用connect函數請求與服務器連接,函數原型如下。

int connect(

SOCKET s, //套接字句柄

const struct sockaddr FAR* name, //指向sockaddr結構的指針,包含想要連接的服務器的地址信息

int namelen); //sockaddr結構的長度

第一個參數是此連接使用的客戶端套接字。另兩個參數用來尋址遠程套接字。

⑦收發數據

對流套接字來說,一般使用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 flags); //指定了調用方式,通常設為0

send函數在一個連接的套接字上發送緩沖區內的數據,返回發揮數據的實際字節數。recv函數從對方接受數據,並存儲到他指定的緩沖區。flags參數在這兩個函數中通常指定為0.

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

image

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved