Windows Embedded Compact 7網絡編程概述(上)

     現在,不管是嵌入式設備、PDA仍是智能手機,網絡都是必不可少的模塊。網絡令人們更方便地共享設備上的信息和資源。並且,利用智能手機瀏覽互聯網,也逐漸成爲生活中的常見手段。物聯網所倡導的物物相聯,也離不開設備中的網絡。所以,熟練掌握網絡編程技術,是Windows CE開發的基本技能。跟以前Windows CE的版本以及其餘的Windows系統同樣,Windows Embedded CE 7的網絡編程也是基於套接字來實現的。程序員

本章首先將介紹套接字的相關原理和編程基礎,而後介紹幾種套接字的實際應用,包括了Ping編程、RAS編程,以及最經常使用的UDP編程和TCP編程。編程

11.1 套接字編程基礎

Windows Socket Winsock)是Windows CE網絡編程的基礎。Winsock是基於U.C. Berkeley大學開發的套接字接口,定義的一套Windows環境下通用的網絡編程接口。Winsock不只支持了對多種傳統傳輸協議如TCPUDP等協議的訪問,也已經可以支持IPv6等新的協議。相應地,應用程序能夠建立多種類型的套接字,知足不一樣網絡環境和特定需求下的網絡鏈接。另外,Winsock包含了一組針對Windows的擴展庫函數,便於程序員利用Windows的消息驅動機制。緩存

Winsock提供的不是協議,而是與協議無關的交互規範或是接口。這個接口能充分發揮底層傳輸協議的通訊特性。因爲Winsock不是協議,它不會改變物理傳輸線路上的信號。服務器

Windows的開發系統架構框架(WOSA)下,WinsockAPI和協議棧之間定義了一套標準的服務提供接口(SPI)。程序員或是網絡軟件供應商能夠利用SPI實現一個分層服務提供商(LSP,來建立新的傳輸服務提供商或是擴展示有的傳輸服務提供商。網絡

Winsock接口的目的在於爲程序員提供一套簡單的API,並讓各網絡軟件供應商共同遵照。此外,Winsock還定義了一個二進制接口(ABI),保證利用了Winsock API的應用程序可以在全部符合Winsock規範但屬於不一樣網絡軟件供應商的平臺上運行。架構

從代碼的角度上看,Winsock就是實現了一套庫函數調用,以及相關的語義。從功能層次上看,Winsock向上爲應用程序提供了能夠調用的API,實現不一樣網絡中應用程序間的通信;向下經過操控網絡傳輸協議,完成網絡間數據的傳輸和通訊。它們的關係如圖11.1所示。框架

Winsock在不一樣的Windows系統中,提供的API稍有差別。下面將具體介紹Windows Embedded CE環境下提供的Winsock API異步

11.1.1 Winsock初始化和釋放

在應用程序調用Winsock提供的API以前,相應版本的Winsock動態庫必須加載進來。若是在沒有初始化Winsock的狀況下而直接調用Winsock中的函數,將會返回錯誤SOCKET_ERRORsocket

WSAStartup函數是初始化Winsock的函數,它的原型以下:ide

int WSAStartup(

  WORD wVersionRequested,

  LPWSADATA lpWSAData

);

函數的返回值爲0,表示函數執行正確。不然,函數執行失敗。下面是函數返回的錯誤碼及其相應的描述。,如表11.1所示

錯誤碼

描述

WSASYSNOTREADY

底層的網絡子系統沒有準備好進行網絡通訊

WSAVERNOTSUPPORTED

請求Winsock的版本不被現有的Winsock實現所支持

WSAEPROCLIM

請求的任務數已達上限

WSAEFAULT

lpWSAData結構體不合法

        表11.1錯誤碼及描述

參數wVersionRequested指定須要加載的Winsock動態庫的版本。Winsock庫的主版本由低位字節指定,而副版本由高位字節指定。

參數lpWSAData是一個指向WSADATA結構體的指針,用於存儲Winsock的具體實現細節。WSADATA結構體的聲明以下:

typedef struct WSAData {

  WORD wVersion;

  WORD wHighVersion;

  char szDescription[WSADESCRIPTION_LEN+1];

  char szSystemStatus[WSASYS_STATUS_LEN+1];

  unsigned short iMaxSockets;

  unsigned short iMaxUdpDg;

  char FAR* lpVendorInfo;

} WSADATA, *LPWSADATA;

wVersion域表示Winsock動態庫指望用戶使用的版本。

wHighVersion域是Winsock動態庫所能容乃的最高版本。通常而言,這個域的值與wVersion相同。

szDescription域保存了Winsock動態庫對其實現的描述。這個域最可能用於在狀態消息中打印。

szSystemStatus域存儲的是Winsock動態庫的相關狀態或是配置信息。

iMaxSockets域表示同時最多能打開的套接字的數目。它爲了向後兼容而保留。不過在Winsock 2.0及之後的版本中,這個域將被忽略。

iMaxUdpDg域表示同時最多能打開的報文的數目。在Winsock 2.0及之後的版本中,它被忽略。

lpVendorInfo域是爲Winsock具體實現的廠商信息預留的。在Winsock 2.0及之後的版本中,它也被忽略。

應用程序在調用完Winsock後,須要調用WSACleanup函數來釋放已分配的資源。WSACleanup函數的原型以下:

int WSACleanup (void);

若是沒有出錯,函數返回值爲0。不然,函數返回的錯誤碼以及對應的描述如表11.2所示:

錯誤碼

描述

WSANOTINITIALISED

必須在成功調用WSAStartup函數以後,才能調用WSACleanup函數

WSAENETDOWN

網絡子系統出錯

WSAEINPROGRESS

阻塞性的Winsock函數正在被調用,或是服務提供者正在處理回調函數

           表11.2函數錯誤碼返回以及對應描述

下列示例程序的功能是應用程序只加載版本號爲2.2Winsock動態庫。

WORD wVersionRequested;

WSADATA wsaData;

int err;

 

wVersionRequested = MAKEWORD( 2, 2 );

 

err = WSAStartup( wVersionRequested, &wsaData );

if ( err != 0 ) {

    /* Tell the user that we could not find a usable */

    /* WinSock DLL.                                  */

    return;

}

 

/* Confirm that the WinSock DLL supports 2.2.*/

/* Note that if the DLL supports versions later    */

/* than 2.2 in addition to 2.2, it will still return */

/* 2.2 in wVersion since that is the version we      */

/* requested.                                        */

 

if ( LOBYTE( wsaData.wVersion ) != 2 ||

        HIBYTE( wsaData.wVersion ) != 2 ) {

    /* Tell the user that we could not find a usable */

    /* WinSock DLL.                                  */

    WSACleanup( );

    return; 

}

11.1.2 建立套接字

函數socket用於爲程序建立一個套接字。函數的原型以下:

SOCKET socket(

  int af,

  int type,

  int protocol

);

參數af指定了通信家庭地址,常見的是AF_INET

參數type指定了套接字的類型。在Winsock 1.1版本中,只有SOCK_STREAMSOCK_DGRAM兩種類型。SOCK_STREAM類型的套接字支持有序的、可靠的、雙向的、基於鏈接的,且支持帶外數據的數據傳輸機制。它利用TCP協議完成數據的傳輸。SOCK_DGRAM類型的套接字利用數據包進行傳輸,是無序的、不可靠的協議。它利用UDP協議傳輸數據。Winsock 2.2版本增長了許多新的套接字協議。程序可以經過函數WSAEnumProtocols動態發現全部能被支持的套接字類型。參數protocol指定了協議的類型,包括IPICMPTCPUDP等協議。

函數若是執行成功,則返回此套接字的句柄。不然,返回值爲INVALID_SOCKET。經過調用WSAGetLastError函數,程序員能夠查看對應的錯誤碼。可能錯誤碼的描述以下:如表11.3所示。

錯誤碼

描述

WSANOTINITIALISED

必須在成功調用WSAStartup函數以後,才能調用此函數

WSAENETDOWN

網絡子系統出錯或者相關的服務提供者出現故障

WSAEAFNOSUPPORT

指定的通信家庭地址不被支持

WSAEINPROGRESS

阻塞性的Winsock函數正在被調用,或是服務提供者正在處理回調函數

WSAEMFILE

沒有套接字描述符可用

WSAENOBUFS

沒有緩存空間供套接字使用

WSAEPROTONOSUPPORT

指定的協議不被支持

WSAEPROTOTYPE

指定的協議與套接字類型不兼容

WSAESOCKTNOSUPPORT

通信家庭地址不支持指定的套接字類型

11.3錯誤碼以及描述

11.1.3 關閉套接字

函數closesocket用來關閉現有的套接字。它的原型以下:

int closesocket(

  SOCKET s

);

參數s指定要關閉套接字的句柄。

若是函數執行成功,則返回值爲0。不然,能夠調用WSAGetLastError函數查看具體的錯誤信息。可能的錯誤信息如表11.3所示:

錯誤碼

描述

WSANOTINITIALISED

必須在成功調用WSAStartup函數以後,才能調用此函數

WSAENETDOWN

網絡子系統出錯或者相關的服務提供者出現故障

WSAENOTSOCK

參數不是一個正確的套接字句柄

WSAEINPROGRESS

阻塞性的Winsock函數正在被調用,或是服務提供者正在處理回調函數

WSAEINTR

套接字已經被關閉

WSAEWOULDBLOCK

套接字被設置爲不能阻塞狀態

      表11.3套接字錯誤碼與描述

函數closesocket將釋放套接字相關的全部資源,包括了相關的命名信息,以及發送或接受隊列中的數據。同時,當前進程中的異步調用以及等待中的阻塞操做都沒取消,並且不會發出通知消息。此外,處於等待狀態的發送和接受操做也被取消,可是已經完成的操做會繼續執行。

爲了不函數closesocket操做的數據或操做的丟失,程序應先調用函數shutdown從容中斷鏈接。所謂「從容中斷鏈接「是爲了保證通訊方可以收到程序發出的全部數據,應該通知接收端再也不發送數據,一樣地,通訊方也應該如此。函數shutdown的原型以下:

int shutdown(

  SOCKET s,

  int how

);

參數s指定了待關閉的套接字句柄。

參數how表示要中斷的操做類型。可選的類型以及相應的描述如表11.4所示:

錯誤碼

描述

SD_RECEIVE

不容許調用recv函數。對於TCP套接字來講,不論是數據在等待接收,仍是數據接連到達,都要重設鏈接。對於UDP套接字來講,到達的數據包仍然會被接收並加入到數據隊列中。

SD_SEND

不容許調用send函數。對於TCP套接字來講,在當前的數據被所有發送出去且收到接收者的確認後,發出FIN信號。

SD_BOTH

不容許調用recv函數以及send函數

            表11.4中斷操做類型錯誤碼與描述

函數執行成功會返回0;不然,表示出錯。此時,函數WSAGetLastError能返回的錯誤碼以及相應的描述如表11.5所示:

錯誤碼

描述

WSANOTINITIALISED

必須在成功調用WSAStartup函數以後,才能調用此函數

WSAENETDOWN

網絡子系統出錯或者相關的服務提供者出現故障

WSAEINVAL

參數how不合法或是與當前的套接字類型不一致。

WSAEINPROGRESS

阻塞性的Winsock函數正在被調用,或是服務提供者正在處理回調函數

WSAENOTCONN

套接字鏈接不通

WSAENOTSOCK

參數不是一個正確的套接字句柄

11.5函數WSAGetLastError能返回的錯誤碼與描述

11.1.4 綁定套接字

函數bind的功能在於將一個網絡地址與套接字綁定。函數bind的原型以下:

int bind(

  SOCKET s,

  const struct SOCK_ADDR* name,

  int namelen

);

參數s指定待綁定的套接字。

參數name是指定sockaddr結構的地址,指定了要綁定的地址。若是沒有指定的地址,則參數被設置爲ADDR_ANY。例如在服務器端的代碼中,能夠接受任意地址的客戶端請求,此時參數name被設置爲ADDR_ANY

參數namelen指定了參數name的大小。

name域須要的值是一個網絡地址,主要包括如下幾個部分:

1) 地址家族

2) 主機地址

3) 端口號

結構體sockaddrsockaddr_in就是包括了以上三個部分的結構體。它們的聲明以下:

struct sockaddr {

        ushort  sa_family;

        char    sa_data[14];

};

struct sockaddr_in {

        short   sin_family;

        u_short sin_port;

        struct  in_addr sin_addr;

        char    sin_zero[8];

};

這兩個結構體的大小同樣,只是sockaddr_in結構體的描述更加詳細。下面對sockaddr_in結構體的成員做簡單的介紹。

sin_family域只能是AF_INET,表示Winsock正是用IP地址家族。

sin_port域指定了通信的端口。在底層協議的實現中,有一部分端口有特定的用途,例如FTP22號端口,以及HTTP80號端口。這些具備特定用途的端口,是由「互聯網端口分配認證(IANA)」控制和分配的。從本質上說,端口號可分爲「已知」端口、已註冊端口、動態和私用端口三類。這三類的端口號分佈以下:

l 01023IANA控制,爲固定服務保留;

l 102449151IANA列出的已註冊端口,供應用程序使用。

l 4915265535是動態和私用端口。

對於TCP/IP協議來講,若是程序指定的端口是0,則服務提供者會爲程序分配一個值在10245000區間的惟一端口。

函數執行成功,會返回0;不然,表示出錯。此時,函數WSAGetLastError能返回的錯誤碼以及相應的描述如表11.6所示:

錯誤碼

描述

WSANOTINITIALISED

必須在成功調用WSAStartup函數以後,才能調用此函數

WSAENETDOWN

網絡子系統出錯或者相關的服務提供者出現故障

WSAEACCES

訪問權限錯誤

WSAEADDRINUSE

地址已經與其餘套接字綁定,並且沒有被設置爲可重用

WSAEADDRNOTAVAIL

地址對當前機器來講不合法或是不可達

WSAEFAULT

參數namenamelen不合法,地址無效

WSAEINPROGRESS

阻塞性的Winsock函數正在被調用,或是服務提供者正在處理回調函數

WSAEINVAL

套接字已經與其餘地址綁定

WSAENOBUFS

鏈接數太多,緩存不足

WSAENOTSOCK

參數s不是一個套接字的句柄

11.6WSAGetLastError能返回的錯誤碼與描述

下面的程序展現如何新建一個套接字,且與當前機器綁定。

// Declare variables

SOCKET ListenSocket;

struct sockaddr_in saServer;

hostent* localHost;

char* localIP;

// Create a listening socket

ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

// Get the local host information

localHost = gethostbyname("");

localIP = inet_ntoa (*(struct in_addr *)*localHost->h_addr_list);

// Set up the sockaddr structure

saServer.sin_family = AF_INET;

saServer.sin_addr.s_addr = inet_addr(localIP);

saServer.sin_port = htons(5150);

// Bind the listening socket using the

// information in the sockaddr structure

bind( ListenSocket,(SOCKADDR*) &saServer, sizeof(saServer) );

11.1.5 監聽套接字

服務器端先建立套接字並與網絡地址(通常爲全部,即ADDR_ANY指定的網絡地址)綁定;而後,進入到監聽狀態,等待客戶端發出鏈接請求。函數listen的原型以下:

int listen(

  SOCKET s,

  int backlog

);

參數s指定了要監聽的套接字句柄。

參數backlog指定了等待鏈接的最大隊列長度。若是backlog被設置爲SOMAXCONN,那麼服務提供者會爲之分配合理範圍內的最大值。這個參數的值決定了服務器能同時鏈接的客戶端的數目。若是請求的客戶端數目超過了backlog,超出的客戶端請求會返回失敗。

函數執行成功,則返回0;不然,返回錯誤SOCKET_ERROR。此時,函數WSAGetLastError能返回的錯誤碼以及相應的描述如表11.7所示:

錯誤碼

描述

WSANOTINITIALISED

必須在成功調用WSAStartup函數以後,才能調用此函數

WSAENETDOWN

網絡子系統出錯或者相關的服務提供者出現故障

WSAEADDRINUSE

地址已經與其餘套接字綁定,並且沒有被設置爲可重用

WSAEINPROGRESS

阻塞性的Winsock函數正在被調用,或是服務提供者正在處理回調函數

WSAEINVAL

套接字沒有調用bind函數進行綁定

WSAEISCONN

套接字已經被鏈接

WSAEMFILE

套接字句柄已達上限

WSAENOBUFS

鏈接數太多,緩存不足

WSAENOTSOCK

參數s不是一個套接字的句柄

WSAEOPNOTSUPP

套接字句柄不支持listen操做

11-7錯誤碼以及描述說明

11.1.6 等待鏈接

服務器端在調用listen函數進入到監聽狀態以後,等待客戶端發出鏈接的請求。服務器端在接收到鏈接請求後,開始接受客戶端的鏈接。函數accept的功能在於服務器端創建與客戶端的鏈接。函數的原型以下:

SOCKET accept(

  SOCKET s,

  struct SOCK_ADDR* addr,

  int FAR* addrlen

);

參數s指定了進入到監聽狀態的套接字句柄。

參數addr返回了創建鏈接的客戶端的網絡地址。

參數addrlen表示參數addr的長度。

若是套接字是阻塞模式,當等待鏈接隊列中沒有鏈接請求時,函數accept將進入到阻塞狀態,直到隊列存在等待鏈接;若是套接字是非阻塞模式,當等待鏈接隊列中存在鏈接請求,函數accept將接受第一個鏈接請求,不然返回INVALID_SOCKET

函數執行成功,則返回一個新的套接字句柄,用於與客戶端進行數據的發送和接收;不然,返回錯誤SOCKET_ERROR。此時,函數WSAGetLastError能返回的錯誤碼以及相應的描述以下:如表11-8所示。

錯誤碼

描述

WSANOTINITIALISED

必須在成功調用WSAStartup函數以後,才能調用此函數

WSAENETDOWN

網絡子系統出錯或者相關的服務提供者出現故障

WSAEFAULT

參數addrlen的值過小,或是參數addr不是合法的地址

WSAEINTR

套接字已經被關閉

WSAEINPROGRESS

阻塞性的Winsock函數正在被調用,或是服務提供者正在處理回調函數

WSAEINVAL

在調用accept函數以前,listen函數沒有被調用

WSAEMFILE

等待隊列爲空,沒有可用的套接字句柄

WSAENOBUFS

鏈接數太多,緩存不足

WSAENOTSOCK

參數s不是一個套接字的句柄

WSAEOPNOTSUPP

套接字句柄不支持面向鏈接的服務

WSAEWOULDBLOCK

套接字的類型是非阻塞型,而當前沒有等待的鏈接請求

11-8錯誤代碼以及描述說明

11.1.7 創建鏈接

客戶端的套接字與服務器端的網絡地址綁定成功之後,就能夠發起與服務器端的鏈接。函數connect的功能在於與服務器端創建一個鏈接。它的原型以下:

int connect(

  SOCKET s,

  const struct SOCK_ADDR* name,

  int namelen

);

參數s指定要鏈接的客戶端的套接字。

參數name指定了要創建鏈接的服務器端的地址和端口號。

參數namelen指定了參數name的長度。

在阻塞模式下,函數的返回值若是是0,表示執行成功;不然,表示出錯,能夠調用WSAGetLastError函數查看具體的錯誤碼。在非阻塞模式下,鏈接請求不能被當即處理。在這種情形下,函數返回SOCKET_ERROR,並且WSAGetLastError函數返回的錯誤碼是WSAEWOULDBLOCK。此時,存在如下三種選擇:

1) 利用select函數來判斷鏈接請求是否被處理,這主要是經過檢查套接字是否可寫來實現的。

2) 若是應用程序使用WSAEventSelect函數來指明鏈接的事件,須要將當前鏈接請求的狀態(是否成功)傳遞給相應的事件。

函數WSAGetLastError能返回的錯誤碼以及相應的描述以下:如表11-9所示

錯誤碼

描述

WSANOTINITIALISED

必須在成功調用WSAStartup函數以後,才能調用此函數

WSAENETDOWN

網絡子系統出錯或者相關的服務提供者出現故障

WSAEADDRINUSE

套接字的本地地址已經被佔用,並且該地址不能被重用

WSAEINTR

套接字已經被關閉

WSAEINPROGRESS

阻塞性的Winsock函數正在被調用,或是服務提供者正在處理回調函數

WSAEALREADY

指定的套接字中存在正在執行的非阻塞的connect函數調用

WSAEADDRNOTAVAIL

要鏈接的地址不合法(例如ADDR_ANY

WSAEAFNOSUPPORT

指定的地址與套接字不匹配

WSAECONNREFUSED

鏈接請求被強制拒絕

WSAEFAULT

參數namenamelen不合法,或是namelen參數的值過小,或者name參數中的地址格式與指定的地址家族的格式不一致

WSAEINVAL

指定的套接字監聽狀態

WSAEISCONN

套接字已經被鏈接

WSAENETUNREACH

服務器端的網絡不可達

WSAENOBUFS

鏈接數太多,緩存不足

WSAENOTSOCK

參數s不是一個套接字的句柄

WSAETIMEDOUT

鏈接超時

WSAEWOULDBLOCK

套接字的類型是非阻塞型,而當前沒有等待的鏈接請求

WSAEACCES

由於套接字的SO_BROADCAST被禁止,將數據包的套接字與廣播地址創建鏈接出錯 

11-9錯誤碼以及對應描述

11.1.8 發送數據

在客戶端經過connect函數,和服務器端經過accept函數創建相互之間的鏈接後,二者就能任意地發送或接收數據。

函數send的功能在於向連通的套接字中發送數據。它的原型以下:

int send(

  SOCKET s,

  const char FAR* buf,

  int len,

  int flags

);

參數s指定了要發送數據的套接字,這個套接字必須是連通的。

參數buf是存儲了待發送數據的緩衝區。

參數len指定了參數buf的長度,也就是待發送數據的大小。

參數flags指定了函數調用的方式。它的值會影響函數的執行行爲。在Windows CE中,它的值只有惟一的MSG_DONTROUTE標誌。標誌MSG_DONTROUTE代表數據不須要路由,不過Winsock的服務提供者能夠選擇忽略這個參數。

發送數據的長度是有限制的,它不能超過底層的服務提供者所規定的最大報的長度。函數getsockopt能夠獲取套接字的SO_MAX_MSG_SIZE屬性,也就是當前服務提供者支持的最大數據包的長度。若是長度超過了最大值,函數會返回WSAEMSGSIZE,並且沒有數據會被髮送。另外,因爲在數據傳輸的過程當中可能發生數據包的丟失,函數send執行成功並不表示數據已經被成功送達。

在阻塞模式下,若是沒有足夠的空間來緩存全部須要傳輸的數據,send函數將進入到阻塞狀態;而在非阻塞模式下,根據緩存空間的大小不一樣,傳輸的數據能夠是1到需傳輸數據的長度。

若是函數執行成功,將返回實際傳輸數據的長度。在非阻塞模式下,這個值可能會小於須要傳輸數據的總長度。若是函數執行錯誤,會返回SOCKET_ERROR。函數WSAGetLastError能返回的錯誤碼以及相應的描述以下:如表11-10所示

錯誤碼

描述

WSANOTINITIALISED

必須在成功調用WSAStartup函數以後,才能調用此函數

WSAENETDOWN

網絡子系統出錯或者相關的服務提供者出現故障

WSAEACCES

由於套接字的SO_BROADCAST被禁止,將數據包的套接字與廣播地址創建鏈接出錯

WSAEINTR

套接字已經被關閉

WSAEINPROGRESS

阻塞性的Winsock函數正在被調用,或是服務提供者正在處理回調函數

WSAEFAULT

參數buf裏面包含了不合法的用戶地址空間的地址

WSAENETRESET

由於檢測到錯誤發生,鏈接被中斷

WSAENOBUFS

鏈接數太多,緩存不足

WSAENOTCONN

套接字沒有連通

WSAENOTSOCK

指定的套接字描述符不是合法的套接字

WSAEOPNOTSUPP

屬性MSG_OOB被指定,可是該套接字不支持帶外數據OOBout of band)的傳輸

WSAESHUTDOWN

套接字已經被關閉

WSAEWOULDBLOCK

套接字的類型是非阻塞型,而當前沒有等待的鏈接請求

WSAEMSGSIZE

傳輸的數據超過了底層協議支持的最大長度

WSAEHOSTUNREACH

遠程主機不可達

WSAEINVAL

指定的套接字沒有處於監聽狀態,參數flag不被識別,或是屬性MSG_OOB在設置了SO_OOBINLINE的套接字中被指定

WSAECONNABORTED

鏈接超時或發生錯誤致使虛擬通訊鏈路被重置

WSAECONNRESET

虛擬通訊鏈路被遠程主機重置

WSAETIMEDOUT

鏈接超時

11-10錯誤碼以及對應描述

在面向無鏈接的套接字中(例如數據報服務),儘管套接字中會綁定到特定的網絡地址,可是在數據傳輸時仍須要指定進行通訊的網絡地址。函數sendto就是無鏈接的套接字發送數據的接口。函數sendto的原型以下:

int sendto(

  SOCKET s,

  const char FAR* buf, 

  int len,

  int flags,

  const struct SOCK_ADDR* to,

  int tolen

);

和函數send相比,函數sendto增長了兩個參數:totolen。參數to是進行通訊的目標地址,參數tolen表示參數to的大小。

在面向無鏈接的套接字中,若是套接字已經指定了特定的網絡地址,函數sendto的參數to會覆蓋這個網絡地址;在面向鏈接的套接字中使用函數sendto發送數據,參數totolen都會被忽略。此時,函數sendto等同於函數send

11.1.9 接收數據

Winsock接收數據的方式也能夠分爲面向鏈接和麪向無鏈接的兩種方式。函數recv的功能在於從鏈接的套接字中接收數據。函數recv的原型以下:

int recv(

  SOCKET s,

  char FAR* buf,

  int len,

  int flags

);

參數s指定了要發送數據的套接字,這個套接字必須是連通的。

參數buf是存儲了待發送數據的緩衝區。

參數len指定了參數buf的長度,也就是待發送數據的大小。

參數flags指定了函數調用的方式。在Windows CE默認支持的Winsock服務提供者中,有兩種常見的網絡標誌不被支持。這兩種標誌以下:如表11-11

錯誤碼

描述

MSG_PEEK

能夠偷窺接收緩衝區中的內容,即數據能夠複製到接收緩衝區,並且也不從輸入隊列中刪除。

MSG_OOB

處理帶外數據(Out Of Band

   表11-11錯誤碼以及描述

函數執行成功的時候,返回接收到數據的字節數;若是鏈接被關閉,則返回0;若是發生錯誤,函數返回SOCKET_ERROR。函數WSAGetLastError能返回的錯誤碼以及相應的描述如表11-12

錯誤碼

描述

WSANOTINITIALISED

必須在成功調用WSAStartup函數以後,才能調用此函數

WSAENETDOWN

網絡子系統出錯或者相關的服務提供者出現故障

WSAEFAULT

參數buf裏面包含了不合法的用戶地址空間的地址

WSAENOTCONN

套接字沒有連通

WSAEINTR

套接字已經被關閉

WSAEINPROGRESS

阻塞性的Winsock函數正在被調用,或是服務提供者正在處理回調函數

WSAENETRESET

由於檢測到錯誤發生,鏈接被中斷

WSAENOTSOCK

指定的套接字描述符不是合法的套接字

WSAESHUTDOWN

套接字已經被關閉

WSAEWOULDBLOCK

套接字的類型是非阻塞型,而當前沒有等待的鏈接請求

WSAEMSGSIZE

傳輸的數據超過了底層協議支持的最大長度

WSAEINVAL

指定的套接字沒有處於監聽狀態,參數flag不被識別,或是屬性MSG_OOB在設置了SO_OOBINLINE的套接字中被指定

WSAECONNABORTED

鏈接超時或發生錯誤致使虛擬通訊鏈路被重置

WSAECONNRESET

虛擬通訊鏈路被遠程主機重置

WSAETIMEDOUT

鏈接超時

11-12錯誤碼以及描述

若是程序使用的是面向鏈接的協議,在調用函數recv以前套接字必須被連通;若是是面向無鏈接的協議,套接字必須被綁定。

與函數sendto相對應的是函數recvfrom。函數recvfrom從面向無鏈接的套接字中接收數據報。它的原型以下:

int recvfrom(

  SOCKET s,

  char FAR* buf,

  int len,

  int flags,

  struct SOCK_ADDR* from,

  int FAR* fromlen

);

函數recvfrom的參數與函數recv的參數相似,只是增長了參數fromfromlen。參數from將返回數據發送方的網絡地址,而參數fromlen指定了參數from的長度。與函數recv不一樣的是,參數flags的值能夠是MSG_PEEKMSG_OOB。函數recvfrom的返回值與函數recv的返回值的意義相同。

11.1.10 設置套接字模式

套接字的工做模式存在阻塞和非阻塞兩種方式。在默認狀況下,建立的套接字處於阻塞的工做方式。阻塞式工做模式,是指在執行相關的函數時,如connect函數,只有在成功和服務器創建鏈接或是鏈接失敗時,函數connect纔會返回。而非阻塞式工做模式,是指在函數在執行相關函數時,如socket函數,函數當即返回而不阻塞主線程。至於如何判斷函數是否執行成功,能夠經過select I/O模型來判斷。

函數ioctlsocket的功能在於控制套接字的I/O模式。函數ioctlsocket的原型以下:

int ioctlsocket(

  SOCKET s,

  long cmd,

  u_long FAR* argp

);

參數s指定要設置的套接字。

參數cmd指定要設置的命令標識。

參數argp對應於參數cmd,指定要執行的命令值。它是指向一個長整數數值的指針。

函數執行成功會返回0;不然,返回SOCKET_ERROR函數WSAGetLastError能返回的錯誤碼以及相應的描述以下:如表11-13

錯誤碼

描述

WSANOTINITIALISED

必須在成功調用WSAStartup函數以後,才能調用此函數

WSAENETDOWN

網絡子系統出錯或者相關的服務提供者出現故障

WSAEINPROGRESS

阻塞性的Winsock函數正在被調用,或是服務提供者正在處理回調函數

WSAENOTSOCK

指定的套接字描述符不是合法的套接字

WSAEFAULT

參數argp不是合法的用戶地址空間的地址

WSAEINVAL

參數不被支持或是不合法

11-13錯誤碼以及描述

這個函數可以用於任何狀態下的任何套接字。它的主要目的是設置或獲取套接字相關的操做參數,並且與協議和通訊子系統無關。參數cmd可以支持的命令以下:

1) FIONBIO用於設置套接字是阻塞式仍是非阻塞式。若是參數argp的值是0,則套接字進入到非阻塞模式;若是參數argp的值非0,套接字進入到阻塞模式。在默認狀況下,新建立的套接字是阻塞模式。

2) FIONREAD用於獲取能夠從套接字上讀取的數據量,也就是網絡的輸入緩衝中能夠等待的數據量的大小。參數argp爲輸出類型,保存了套接字能夠讀取的數據量的大小。若是當前套接字是流式套接字,如SOCK_STREAMFIONREAD返回一次調用過程當中函數recv能讀取的最大數據量;這個數據量未必與套接隊列中的數據長度一致。若是當前套接字是數據包式套接字,FIONREAD返回套接隊列中第一個數據包的長度。

相關文章
相關標籤/搜索