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不仅支持了对多种传统传输协议如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网络编程概述(上)的更多相关文章
- Windows Embedded Compact 7网络编程概述(下)
11.1.1 Select I/O模型 在Windows CE中,Select模型是唯一被支持的I/O模型.Select I/O模型就是利用select函数对I/O进行管理. 函数select的功能在 ...
- Windows Embedded Compact 2013 安装体验
6月14日,微软正式发布了Windows embedded compact 2013,大家还是习惯称之为Window CE 8,公司也要开始做windows embedded compact 2013 ...
- Android网络编程概述
Android网络编程概述 首先,应该了解的几个问题: 1)Android平台网络相关API接口 a) java.net.*(标准Java接口) java.net.*提供与联网有关的类,包括流.数据包 ...
- Windows Embedded Compact 7 开发环境搭建
第一步,我们会定制一个运行在Vitual PC上的image.要完成这个任务,你要保证你的电脑上安装了:1.Windows Virtual PC 2.Visual Studio 2008+SP1 3. ...
- Windows Embedded Compact 2013升级:VS2013也能编译
IT之家(www.ithome.com):Windows Embedded Compact 2013升级:VS2013也能编译 今天,微软为Windows Embedded Compact 2013送 ...
- Windows Embedded Compact 7新特性
Windows® Embedded Compact 7是Windows Embedded CE的下一代产品,而Windows Embedded CE这款操作系统面向占用资源少的新颖设备.Windows ...
- Windows Embedded Compact 7初体验
Windows Embedded Compact 7初体验 Windows Embedded Compact 7已经出来半年多了,一直没时间搞.最近它又出了Refresh的版本,电脑也换了个1T的硬盘 ...
- windows下的socket网络编程
windows下的socket网络编程 windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了, ...
- windows下的socket网络编程(入门级)
windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先 ...
随机推荐
- LINUX下解决netstat查看TIME_WAIT状态过多问题(转)
原文连接:www.itokit.com/2012/0516/73950.html # netstat -an|awk '/tcp/ {print $6}'|sort|uniq -c 16 CLOSIN ...
- C# 解决窗体闪烁
C# 解决窗体闪烁 在Windows窗体上造成“闪烁”的窗体上有很多控制.造成这种闪烁的原因有两个: 1.当控件需要被绘制时,Windows发送一个控件两个消息.第一个(WM_ERASEBKGND)导 ...
- DroidParts 中文系列教程(基于官方教程)
DroidParts中文系列教程(基于官方教程) (一)DroidParts框架概况 2014年4月18日星期五 11:36 他是一个精心构造的安卓框架,包括下面这些基本功能 DI依赖注入,可以注入V ...
- <<Javascript Patterns>>阅读笔记 -- 第2章 基本技巧(二)
关于for-in循环 循环数据时, 强烈不推荐使用for-in循环.因为当Array对象被扩展后, 再用for-in循环遍历数据会导致逻辑上的错误, 举例说明: var arr = ['a', 'b' ...
- zookeeper的简单使用
前言 最近项目中要使用基于zookeeper的集中配置管理系统,而对于zookeeper仅在当初使用阿里开源分布式服务调用框架dubbo时简单的了解一下.本 文的主要目的,调用zkclient (ma ...
- 【洛谷】P4585 [FJOI2015]火星商店问题
题解 题目太丧,OJ太没有良心,我永远喜欢LOJ! (TLE报成RE,垃圾洛谷,我永远喜欢LOJ) 好的,平复一下我debug了一上午崩溃的心态= =,写一写这道题的题解 把所有限制去掉,给出一个值, ...
- Python基本语法[二]
Python基本语法 1.定义变量: 代码正文: x= y= z=x+y 代码讲解: 2.判断语句: 代码正文: score= : print("你真棒") print(&qu ...
- 洛谷P2680 运输计划 [LCA,树上差分,二分答案]
题目传送门 运输计划 Description 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n?1 条双向航道,每条航道建立在两个星球之间, 这 n?1 条航道连通了 L 国的所 ...
- Android之 广播
(以下内容是阅读郭霖大神的<第一行代码>后自己总结的) 1.概述 广播是Android的四大组件之一. Android的广播机制十分灵活. 2.发送广播 如上图Android的广播主要分为 ...
- Windows 下安装 tensorflow & keras & opencv 的避坑指南!
安装 Anaconda3 关键的一步: conda update pip 下面再去安装各种你需要的包,一般不会再报错. pip install -U tensorflow pip install -U ...