現在,不管是嵌入式設備、PDA仍是智能手機,網絡都是必不可少的模塊。網絡令人們更方便地共享設備上的信息和資源。並且,利用智能手機瀏覽互聯網,也逐漸成爲生活中的常見手段。物聯網所倡導的物物相聯,也離不開設備中的網絡。所以,熟練掌握網絡編程技術,是Windows CE開發的基本技能。跟以前Windows CE的版本以及其餘的Windows系統同樣,Windows Embedded CE 7的網絡編程也是基於套接字來實現的。程序員
本章首先將介紹套接字的相關原理和編程基礎,而後介紹幾種套接字的實際應用,包括了Ping編程、RAS編程,以及最經常使用的UDP編程和TCP編程。編程
Windows Socket (Winsock)是Windows CE網絡編程的基礎。Winsock是基於U.C. Berkeley大學開發的套接字接口,定義的一套Windows環境下通用的網絡編程接口。Winsock不只支持了對多種傳統傳輸協議如TCP、UDP等協議的訪問,也已經可以支持IPv6等新的協議。相應地,應用程序能夠建立多種類型的套接字,知足不一樣網絡環境和特定需求下的網絡鏈接。另外,Winsock包含了一組針對Windows的擴展庫函數,便於程序員利用Windows的消息驅動機制。緩存
Winsock提供的不是協議,而是與協議無關的交互規範或是接口。這個接口能充分發揮底層傳輸協議的通訊特性。因爲Winsock不是協議,它不會改變物理傳輸線路上的信號。服務器
在Windows的開發系統架構框架(WOSA)下,Winsock在API和協議棧之間定義了一套標準的服務提供接口(SPI)。程序員或是網絡軟件供應商能夠利用SPI實現一個分層服務提供商(LSP),來建立新的傳輸服務提供商或是擴展示有的傳輸服務提供商。網絡
Winsock接口的目的在於爲程序員提供一套簡單的API,並讓各網絡軟件供應商共同遵照。此外,Winsock還定義了一個二進制接口(ABI),保證利用了Winsock API的應用程序可以在全部符合Winsock規範但屬於不一樣網絡軟件供應商的平臺上運行。架構
從代碼的角度上看,Winsock就是實現了一套庫函數調用,以及相關的語義。從功能層次上看,Winsock向上爲應用程序提供了能夠調用的API,實現不一樣網絡中應用程序間的通信;向下經過操控網絡傳輸協議,完成網絡間數據的傳輸和通訊。它們的關係如圖11.1所示。框架
Winsock在不一樣的Windows系統中,提供的API稍有差別。下面將具體介紹Windows Embedded CE環境下提供的Winsock API。異步
在應用程序調用Winsock提供的API以前,相應版本的Winsock動態庫必須加載進來。若是在沒有初始化Winsock的狀況下而直接調用Winsock中的函數,將會返回錯誤SOCKET_ERROR。socket
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 |
表11.2函數錯誤碼返回以及對應描述
下列示例程序的功能是應用程序只加載版本號爲2.2的Winsock動態庫。
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;
}
函數socket用於爲程序建立一個套接字。函數的原型以下:
SOCKET socket(
int af,
int type,
int protocol
);
參數af指定了通信家庭地址,常見的是AF_INET。
參數type指定了套接字的類型。在Winsock 1.1版本中,只有SOCK_STREAM和SOCK_DGRAM兩種類型。SOCK_STREAM類型的套接字支持有序的、可靠的、雙向的、基於鏈接的,且支持帶外數據的數據傳輸機制。它利用TCP協議完成數據的傳輸。SOCK_DGRAM類型的套接字利用數據包進行傳輸,是無序的、不可靠的協議。它利用UDP協議傳輸數據。Winsock 2.2版本增長了許多新的套接字協議。程序可以經過函數WSAEnumProtocols動態發現全部能被支持的套接字類型。參數protocol指定了協議的類型,包括IP、ICMP、TCP和UDP等協議。
函數若是執行成功,則返回此套接字的句柄。不然,返回值爲INVALID_SOCKET。經過調用WSAGetLastError函數,程序員能夠查看對應的錯誤碼。可能錯誤碼的描述以下:如表11.3所示。
描述 |
|
WSANOTINITIALISED |
必須在成功調用WSAStartup函數以後,才能調用此函數 |
WSAENETDOWN |
網絡子系統出錯或者相關的服務提供者出現故障 |
WSAEAFNOSUPPORT |
指定的通信家庭地址不被支持 |
WSAEINPROGRESS |
阻塞性的Winsock函數正在被調用,或是服務提供者正在處理回調函數 |
WSAEMFILE |
沒有套接字描述符可用 |
WSAENOBUFS |
沒有緩存空間供套接字使用 |
WSAEPROTONOSUPPORT |
指定的協議不被支持 |
WSAEPROTOTYPE |
指定的協議與套接字類型不兼容 |
WSAESOCKTNOSUPPORT |
通信家庭地址不支持指定的套接字類型 |
表11.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能返回的錯誤碼與描述
函數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) 端口號
結構體sockaddr和sockaddr_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域指定了通信的端口。在底層協議的實現中,有一部分端口有特定的用途,例如FTP的22號端口,以及HTTP的80號端口。這些具備特定用途的端口,是由「互聯網端口分配認證(IANA)」控制和分配的。從本質上說,端口號可分爲「已知」端口、已註冊端口、動態和私用端口三類。這三類的端口號分佈以下:
l 0~1023由IANA控制,爲固定服務保留;
l 1024~49151是IANA列出的已註冊端口,供應用程序使用。
l 49152~65535是動態和私用端口。
對於TCP/IP協議來講,若是程序指定的端口是0,則服務提供者會爲程序分配一個值在1024到5000區間的惟一端口。
函數執行成功,會返回0;不然,表示出錯。此時,函數WSAGetLastError能返回的錯誤碼以及相應的描述如表11.6所示:
錯誤碼 |
描述 |
WSANOTINITIALISED |
必須在成功調用WSAStartup函數以後,才能調用此函數 |
WSAENETDOWN |
網絡子系統出錯或者相關的服務提供者出現故障 |
WSAEACCES |
訪問權限錯誤 |
WSAEADDRINUSE |
地址已經與其餘套接字綁定,並且沒有被設置爲可重用 |
WSAEADDRNOTAVAIL |
地址對當前機器來講不合法或是不可達 |
WSAEFAULT |
參數name或namelen不合法,地址無效 |
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) );
服務器端先建立套接字並與網絡地址(通常爲全部,即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錯誤碼以及描述說明
服務器端在調用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錯誤代碼以及描述說明
客戶端的套接字與服務器端的網絡地址綁定成功之後,就能夠發起與服務器端的鏈接。函數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 |
參數name或namelen不合法,或是namelen參數的值過小,或者name參數中的地址格式與指定的地址家族的格式不一致 |
WSAEINVAL |
指定的套接字監聽狀態 |
WSAEISCONN |
套接字已經被鏈接 |
WSAENETUNREACH |
服務器端的網絡不可達 |
WSAENOBUFS |
鏈接數太多,緩存不足 |
WSAENOTSOCK |
參數s不是一個套接字的句柄 |
WSAETIMEDOUT |
鏈接超時 |
WSAEWOULDBLOCK |
套接字的類型是非阻塞型,而當前沒有等待的鏈接請求 |
WSAEACCES |
由於套接字的SO_BROADCAST被禁止,將數據包的套接字與廣播地址創建鏈接出錯 |
表11-9錯誤碼以及對應描述
在客戶端經過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被指定,可是該套接字不支持帶外數據OOB(out 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增長了兩個參數:to和tolen。參數to是進行通訊的目標地址,參數tolen表示參數to的大小。
在面向無鏈接的套接字中,若是套接字已經指定了特定的網絡地址,函數sendto的參數to會覆蓋這個網絡地址;在面向鏈接的套接字中使用函數sendto發送數據,參數to和tolen都會被忽略。此時,函數sendto等同於函數send。
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的參數相似,只是增長了參數from和fromlen。參數from將返回數據發送方的網絡地址,而參數fromlen指定了參數from的長度。與函數recv不一樣的是,參數flags的值能夠是MSG_PEEK和MSG_OOB。函數recvfrom的返回值與函數recv的返回值的意義相同。
套接字的工做模式存在阻塞和非阻塞兩種方式。在默認狀況下,建立的套接字處於阻塞的工做方式。阻塞式工做模式,是指在執行相關的函數時,如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_STREAM,FIONREAD返回一次調用過程當中函數recv能讀取的最大數據量;這個數據量未必與套接隊列中的數據長度一致。若是當前套接字是數據包式套接字,FIONREAD返回套接隊列中第一個數據包的長度。