如今,不论是嵌入式设备、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不仅支持了对多种传统传输协议如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。

11.1.1 Winsock初始化和释放

在应用程序调用Winsock提供的API之前,相应版本的Winsock动态库必须加载进来。如果在没有初始化Winsock的情况下而直接调用Winsock中的函数,将会返回错误SOCKET_ERROR。

WSAStartup函数是初始化Winsock的函数,它的原型如下:

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.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;

}

11.1.2 创建套接字

函数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错误码以及描述

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) 端口号

结构体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) );

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

参数name或namelen不合法,或是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被指定,但是该套接字不支持带外数据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。

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的参数类似,只是增加了参数from和fromlen。参数from将返回数据发送方的网络地址,而参数fromlen指定了参数from的长度。与函数recv不同的是,参数flags的值可以是MSG_PEEK和MSG_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_STREAM,FIONREAD返回一次调用过程中函数recv能读取的最大数据量;这个数据量未必与套接队列中的数据长度一致。如果当前套接字是数据包式套接字,FIONREAD返回套接队列中第一个数据包的长度。

Windows Embedded Compact 7网络编程概述(上)的更多相关文章

  1. Windows Embedded Compact 7网络编程概述(下)

    11.1.1 Select I/O模型 在Windows CE中,Select模型是唯一被支持的I/O模型.Select I/O模型就是利用select函数对I/O进行管理. 函数select的功能在 ...

  2. Windows Embedded Compact 2013 安装体验

    6月14日,微软正式发布了Windows embedded compact 2013,大家还是习惯称之为Window CE 8,公司也要开始做windows embedded compact 2013 ...

  3. Android网络编程概述

    Android网络编程概述 首先,应该了解的几个问题: 1)Android平台网络相关API接口 a) java.net.*(标准Java接口) java.net.*提供与联网有关的类,包括流.数据包 ...

  4. Windows Embedded Compact 7 开发环境搭建

    第一步,我们会定制一个运行在Vitual PC上的image.要完成这个任务,你要保证你的电脑上安装了:1.Windows Virtual PC 2.Visual Studio 2008+SP1 3. ...

  5. Windows Embedded Compact 2013升级:VS2013也能编译

    IT之家(www.ithome.com):Windows Embedded Compact 2013升级:VS2013也能编译 今天,微软为Windows Embedded Compact 2013送 ...

  6. Windows Embedded Compact 7新特性

    Windows® Embedded Compact 7是Windows Embedded CE的下一代产品,而Windows Embedded CE这款操作系统面向占用资源少的新颖设备.Windows ...

  7. Windows Embedded Compact 7初体验

    Windows Embedded Compact 7初体验 Windows Embedded Compact 7已经出来半年多了,一直没时间搞.最近它又出了Refresh的版本,电脑也换了个1T的硬盘 ...

  8. windows下的socket网络编程

    windows下的socket网络编程 windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了, ...

  9. windows下的socket网络编程(入门级)

    windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先 ...

随机推荐

  1. LINUX下解决netstat查看TIME_WAIT状态过多问题(转)

    原文连接:www.itokit.com/2012/0516/73950.html # netstat -an|awk '/tcp/ {print $6}'|sort|uniq -c 16 CLOSIN ...

  2. C# 解决窗体闪烁

    C# 解决窗体闪烁 在Windows窗体上造成“闪烁”的窗体上有很多控制.造成这种闪烁的原因有两个: 1.当控件需要被绘制时,Windows发送一个控件两个消息.第一个(WM_ERASEBKGND)导 ...

  3. DroidParts 中文系列教程(基于官方教程)

    DroidParts中文系列教程(基于官方教程) (一)DroidParts框架概况 2014年4月18日星期五 11:36 他是一个精心构造的安卓框架,包括下面这些基本功能 DI依赖注入,可以注入V ...

  4. <<Javascript Patterns>>阅读笔记 -- 第2章 基本技巧(二)

    关于for-in循环 循环数据时, 强烈不推荐使用for-in循环.因为当Array对象被扩展后, 再用for-in循环遍历数据会导致逻辑上的错误, 举例说明: var arr = ['a', 'b' ...

  5. zookeeper的简单使用

    前言 最近项目中要使用基于zookeeper的集中配置管理系统,而对于zookeeper仅在当初使用阿里开源分布式服务调用框架dubbo时简单的了解一下.本 文的主要目的,调用zkclient (ma ...

  6. 【洛谷】P4585 [FJOI2015]火星商店问题

    题解 题目太丧,OJ太没有良心,我永远喜欢LOJ! (TLE报成RE,垃圾洛谷,我永远喜欢LOJ) 好的,平复一下我debug了一上午崩溃的心态= =,写一写这道题的题解 把所有限制去掉,给出一个值, ...

  7. Python基本语法[二]

    Python基本语法 1.定义变量:  代码正文: x= y= z=x+y 代码讲解: 2.判断语句:  代码正文: score= : print("你真棒") print(&qu ...

  8. 洛谷P2680 运输计划 [LCA,树上差分,二分答案]

    题目传送门 运输计划 Description 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n?1 条双向航道,每条航道建立在两个星球之间, 这 n?1 条航道连通了 L 国的所 ...

  9. Android之 广播

    (以下内容是阅读郭霖大神的<第一行代码>后自己总结的) 1.概述 广播是Android的四大组件之一. Android的广播机制十分灵活. 2.发送广播 如上图Android的广播主要分为 ...

  10. Windows 下安装 tensorflow & keras & opencv 的避坑指南!

    安装 Anaconda3 关键的一步: conda update pip 下面再去安装各种你需要的包,一般不会再报错. pip install -U tensorflow pip install -U ...