1. 程式人生 > >windows socket程式設計總結

windows socket程式設計總結

windows的網路程式設計介面沒有像linux那麼豐富,功能也要少很多,下面針對幾個主要的介面做一下介紹:

1.  int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);

這個函式比較簡單,目的是初始化socket,所有其他socket介面呼叫之前都要呼叫這個介面,呼叫成功會返回0,呼叫失敗返回非0值。

例:

    WSADATA wsaData;
    if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0)
    {                               */
        printf("WSAStartup failed with error: %d\n"
, err); return 1; }

2. int WSACleanup(void);

此函式與 WSAStartup 相對,是釋放socket庫資源,到所有的socket函式後面使用,一般在程式結束的地方呼叫。

3. SOCKET socket(int af,int type,int protocol);

此函式為建立一個SOCKET物件,針對TCP和UDP傳入的引數會有點不一樣,呼叫失敗會返回INVALID_SOCKET。

例:TCP:SOCKET s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

UDP:SOCKET s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

4. int connect(SOCKETs, const  struct  sockaddr*name, intnamelen); 

函式呼叫失敗返回SOCKET_ERROR.

此函式在TCP中的使用:

a.此函式呼叫成功客戶端與伺服器之間就建立了一條連結。

b.若TCP客戶沒有收到SYN分節的響應,則返回ETIMEOUT錯誤。

c.若TCP客戶收到RST響應,則返回ECONNREFUSED錯誤。

d.若TCP客戶connect失敗,則必須關閉socket重新建立以後才能再次呼叫connect函式。

e.呼叫此函式的TCP狀態變化:CLOSED->SYN_SENT->ESTABLISHED。


阻塞模式:

(1)如果TCP服務端沒有開啟,則connect會立馬返回SOCKET_ERROR(-1),呼叫WSAGetLastError會返回WSAECONNREFUSED(10061)錯誤。

(2)如果TCP服務端執行正常(服務端必須呼叫監聽函式(listen)以後,TCP/IP核心才會接收TCP客戶端的連線),則connect會立馬返回0,表示連線成功。

非阻塞模式:

(1)如果TCP服務端沒有開啟,則connect會立馬返回SOCKET_ERROR(-1),呼叫WSAGetLastError會返回WSAEWOULDBLOCK(10035)錯誤。

(2)如果TCP服務端執行正常,則connect會立馬返回SOCKET_ERROR(-1),呼叫WSAGetLastError會返回WSAEWOULDBLOCK(10035)錯誤。

由以上可知,如果將socket設定為非阻塞,通過返回值是沒有辦法判斷connect是否連線成功此時要判斷connect是否呼叫成功,需要先呼叫select,發現描述符變成可寫以後,則connect成功,否則失敗。

例.阻塞模式呼叫:

    struct sockaddr_in clientService;
    clientService.sin_family = AF_INET;
    clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
    clientService.sin_port = htons(27015);
    // Connect to server.    
    connect(s, (SOCKADDR *) & clientService, sizeof (clientService));
非阻塞模式呼叫:
	FD_SET mask;
u_long value=1;
TIMEVAL timeout;
       struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(27015);
serveraddr.sin_addr.s_addr =
inet_addr("127.0.0.1");
ioctlsocket(sSocket,FIONBIO,&value);//設定為非阻塞
connect(sSocket,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
timeout.tv_sec=2;
timeout.tv_usec=0;
FD_ZERO(&mask);
FD_SET(sSocket,&mask);
value=select(NULL,NULL,&mask,NULL,&timeout);
if(value && value!=SOCKET_ERROR)
{ 
//連線成功
value = 0;
ioctlsocket(sSocket,FIONBIO,&value);//設定為阻塞
}
else
{
shutdown(sSocket,SD_BOTH);
closesocket(sSocket);
sSocket = 0;
}

此函式在UDP中的使用:

a.UDP中也能呼叫此函式,但是並沒有建立一條真正的連結,只是限定UDP的通訊對端的IP和埠。由於UDP有了對端的IP和埠,因此傳送和接收資料的時候也可以使用send和recv函式,後面會介紹這2個函式。

5. SOCKET accept(SOCKET s,struct  sockaddr  *addr,int  *addrlen);

此函式為TCP伺服器用來接收TCP客戶端的連線的。

如果將 s 監聽的socket設定阻塞模式的,此函式會一直阻塞到與客戶端連線到來為止。

如果將 s 監聽的socket設定為非阻塞模式,此函式呼叫失敗會返回INVALID_SOCKET,如果呼叫WSAGetLastError函式返回WSAEWOULDBLOCK(10035)表示還沒有TCP客戶端來連線TCP伺服器。

如果監聽的 s 為非阻塞模式,則收到的socket也為非阻塞模式。

如果監聽的 s 為阻塞模式,則收到的socket也為阻塞模式。

例:阻塞模式:SOCKET sockClient=accept(sockListen,NULL,NULL);//此函式返回表示已經有客戶端連線到來,所以sockClient不會為INVALID_SOCKET.

非阻塞模式:SOCKETsockClient=accept(sockListen,NULL,NULL);
if(INVALID_SOCKET==sockClient)//表示此次呼叫沒有客戶端連線到來
{
printf("TCP_Server accept failed!\n");
continue;
}

6.int listen(SOCKETs,intbacklog);

此函式為TCP伺服器用來將 SOCKET s由主動模式變為被動模式。 backlog 為TCP協議棧中已經完成3次握手和正在進行3次握手的連線數。比如通常將backlog設定成5,則最多同一時間只能有5個客戶端連線過來。等其中有一連線已經被accept介面取出以後,又可以有新的客戶端連線進來。

函式呼叫失敗返回:SOCKET_ERROR。

例:if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR)

        printf("listen function failed with error: %d\n", WSAGetLastError());

7.int bind(SOCKETs,const struct sockaddr*name,intnamelen);

此函式為 SOCKET s 關聯一個本地地址。呼叫失敗,返回SOCKET_ERROR。

如果不呼叫bind函式,當呼叫connect(TCP客戶端)和listen(TCP服務端)時,核心就要為相應的套接字選擇一個臨時埠。指定埠0為未指定埠。bind被呼叫時選擇一個臨時埠,若未指定IP地址(INADDR_ANY),則核心將等到套接字已經連線(TCP)或已在套接字上發出資料報(UDP)時選擇IP。例:

    struct service.sin_family = AF_INET;
    service.sin_addr.s_addr = inet_addr("127.0.0.1");
    service.sin_port = htons(27015);
    // Bind the socket.
    iResult = bind(ListenSocket, (SOCKADDR *) &service, sizeof (service));
    if (iResult == SOCKET_ERROR) {}

8.intsendto(SOCKET s,const char  * buf,int len,int flags,const struct sockaddr * to,int tolen);

int recvfrom(SOCKET s,char * buf,int len,int flags,struct sockaddr * from,int * fromlen);

int send(SOCKET s,const char * buf,int len,int flags);

阻塞模式:

(1)如果對端沒有接收,並且TCP/IP緩衝區也已經滿,則send函式會阻塞,直到有緩衝區存放為止。

(2)如果對端關閉socket,則send函式返回SOCKET_ERROR(-1),呼叫WSAGetLastError函式返回WSAECONNABORTED(10053)或者WSAECONNRESET(10054)錯誤。

非阻塞模式:

(1)如果對端沒有接收,並且TCP/IP緩衝區也已經滿,則send函式會返回SOCKET_ERROR(-1),呼叫WSAGetLastError函式返回WSAEWOULDBLOCK(10035)錯誤。

(2)如果對端關閉socket,則send函式返回SOCKET_ERROR(-1),呼叫WSAGetLastError函式返回WSAECONNABORTED(10053)或者WSAECONNRESET(10054)錯誤。

int recv(SOCKET s,char * buf,int len,int flags);

阻塞模式:

(1)recv會一直等待,如果有資料到來則接收資料,返回接收的資料大小。

(2)recv會一直等待,如果對端關閉了socket,則recv會返回0.

非阻塞模式:

(1)recv會立馬返回,如果沒有任何資料能接收,則返回SOCKET_ERROR(-1),呼叫WSAGetLastError函式則返回WSAEWOULDBLOCK(10035)錯誤。

(2)recv會立馬返回,如果對端關閉了socket,則recv會返回0.

(3)recv會立馬返回,如果TCP有資料接收,則直接返回接收到的資料大小。

以上4個函式我TCP和UDP用來接收和傳送資料的,在呼叫這幾個介面之前可以呼叫ioctlsocket 函式將這幾個函式變成非阻塞式的。

返回值為真實發送和接收的資料大小。

9.intshutdown(SOCKET s,int how);

此函式為關閉連線的接收部分或者傳送部分或者傳送接收都關閉,函式呼叫失敗返回:SOCKET_ERROR。

how引數對應下表:

Value Meaning
SD_RECEIVE
0

關閉接收這一部分

SD_SEND
1

關閉傳送這一部分

SD_BOTH
2

關閉傳送和接收

10. int closesocket(SOCKET s);

預設行為:把套接字標記為已關閉,然後返回到呼叫程序,TCP傳送完已經排隊的資料,傳送完畢以後發生正常的TCP 4分組終止序列,減少對應套接字的一個引用計.關閉傳送和接收。可以通過呼叫setsocketopt函式來改變這個預設行為。函式呼叫失敗返回:SOCKET_ERROR。

11.int getsockname(SOCKET s,struct sockaddr * name,int * namelen);
intgetpeername(SOCKET s,struct sockaddr * name,int * namelen);

此函式的作用:檢視核心選擇的臨時埠和IP地址,返回與套接字有關的本地協議地址和外地協議地址,TCP客戶沒有呼叫bind就直接呼叫connect,用getsockname來返回核心賦予該連線的本地IP地址和本地埠號.函式呼叫失敗返回:SOCKET_ERROR.

12.//主機位元組序和網路位元組序的轉換
u_shorthtons(u_short hostshort);
u_longhtonl(u_long hostlong);
u_shortntohs(u_short netshort);
u_longntohl(u_long netlong);

13.IPV4字串和整形(為網路位元組序)之間的轉換
unsigned long inet_addr(const char * cp);
char  * inet_ntoa(struct in_addr in);

14 . int ioctlsocket(SOCKET s,long cmd,u_long FAR* argp); 
 此函式為設定connect,accept,recv,recvfrom是否為阻塞模式。如果呼叫失敗,返回:SOCKET_ERROR 。

例:
u_long mode = 0;
ioctlsocket(s,FIONBIO,&mode);
控制為阻塞方式。

u_long mode = 1;
ioctlsocket(s,FIONBIO,&mode);
控制為非阻塞方式。 

14.設定獲取獲取socket的配置項。

int getsockopt(SOCKET s,int level,int optname,char * optval,int * optlen);
intsetsockopt(SOCKET s,int level,int optname,const char * optval,int optlen);

由於這2個函式的引數輸入情況比較多,所以要想知道具體使用,請參考一下連結: SetSockOpt使用

15.int select( int nfds, fd_set FAR , fd_set FAR , fd_set FAR , const struct timeval FAR );

The select function returns the total number of socket handles that are ready and contained in the fd_set structures, zero if the time limit expired, or SOCKET_ERROR if an error occurred. If the return value is SOCKET_ERROR, WSAGetLastError can be used to retrieve a specific error code.

Error code Meaning
WSANOTINITIALISED A successful WSAStartup call must occur before using this function.
WSAEFAULT The Windows Sockets implementation was unable to allocate needed resources for its internal operations, or the readfds, writefds, exceptfds, or timeval parameters are not part of the user address space.
WSAENETDOWN The network subsystem has failed.
WSAEINVAL The time-out value is not valid, or all three descriptor parameters were NULL.
WSAEINTR A blocking Windows Socket 1.1 call was canceled through WSACancelBlockingCall.
WSAEINPROGRESS A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.
WSAENOTSOCK One of the descriptor sets contains an entry that is not a socket.

readfds:

  • If listen has been called and a connection is pending, accept will succeed.
  • Data is available for reading (includes OOB data if SO_OOBINLINE is enabled).
  • Connection has been closed/reset/terminated.

writefds:

  • If processing a connect call (nonblocking), connection has succeeded.
  • Data can be sent.

exceptfds:

  • If processing a connect call (nonblocking), connection attempt failed.
  • OOB data is available for reading (only if SO_OOBINLINE is disabled).

對TCP和UDP的一些總結:

TCP是基於流模式的,傳送端經過多次傳送的資料,接收端也許一次就能全部接收。

UDP是基於報式的,傳送端每次傳送了多大的資料包,接收端就會接收到多大的資料包,如果因為傳送端傳送的某個資料包太大,接收端的資料緩衝區太小,則recvfrom函式會返回-1。表示此函式呼叫失敗。所以建議UDP接收緩衝區要設定的大一點。防止出現以上錯誤。

下面是我寫的一個TCP和UDP通訊demo,裡面測試了以上的部分介面,有些介面呼叫被註釋了,要測試的話要去掉註釋。