现在的位置: 首页 > 综合 > 正文

《ASCE1885的网络编程》—Winsock APIの套接口I/O处理函数

2013年08月04日 ⁄ 综合 ⁄ 共 3419字 ⁄ 字号 评论关闭

Windows环境下,套接口的通信方式分为两种:阻塞方式和非阻塞方式。阻塞方式下工作的套接口在进行I/O操作时,函数要等待到相关操作完成以后才能返回;非阻塞方式下工作的套接口在进行I/O操作时,无论操作是否成功,调用都会立即返回。

阻塞方式和非阻塞方式各有优缺点,阻塞方式的套接口编程简单,易于实现。因此,一个套接口默认操作模式被设置为阻塞方式。如果要使套接口工作在非阻塞方式下,就要使用ioctlsocket()函数进行设置。阻塞方式的套接口在下面几种情况下显得难于管理:

1)当有多个已建立连接的套接口需要进行管理的;

2)当发送的数据量不均匀或接收的数据量不均匀时;

3)当发送或接收的数据时间不确定时。

在进行程序设计时,应该尽量使用非阻塞方式的操作,但非阻塞方式的套接口较为复杂,并且由于操作常常失败,因此在程序中就要考虑操作失败时应该如何处理了。、

 

1)设置套接口的工作方式---ioctlsocket()WSAIoctl()

int ioctlsocket(

  __in     SOCKET s, //套接字描述字

  __in     long cmd, //预定义好的标志,表示对套接口s的操作控制命令

  __inout  u_long *argp //指向cmd命令所待参数的指针

);

 

Winsock2中引入一个新的功能更强大的函数是WSAIoctl()

int WSAIoctl(

  __in   SOCKET s, //套接字描述字

  __in   DWORD dwIoControlCode, //指示将要进行的操作的控制代码

  __in   LPVOID lpvInBuffer, //指向函数的输入参数(用于描述函数输入参数缓冲区的地址)

  __in   DWORD cbInBuffer, //用于描述输入缓冲区的大小

  __out  LPVOID lpvOutBuffer, //指向函数的返回参数(用于描述函数返回数据缓冲区的地址)

  __in   DWORD cbOutBuffer, //用于描述返回参数缓冲区的大小

  __out  LPDWORD lpcbBytesReturned, //指向函数实际返回的字节数的地址

  __in   LPWSAOVERLAPPED lpOverlapped, //WSAOVERLAPPED结构的地址

  __in   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine //一个指向操作结束后

                                     //调用的例程指针,该参数和lpOverlapped使用在重叠I/O

);

 

Ioctlsocket()WSAIoctl()函数支持的标准I/O命令有FIONBIOFIONREADSIOCATMARK

1FIONBIO该命令用于在套接口s上允许或禁止非阻塞模式。一个套接口在创建好以后,默认的通信模式是阻塞模式,如果要在程序中对一个创建好的套接口通行模式进行设置,那么ioctlsocket()函数的第二个参数使用命令FIONBIO,第三个参数argp设为0时,表示禁止非阻塞模式;设为非0值,表示允许非阻塞模式。

要注意:如果使用WSAAsyncSelect()函数或WSAEventSelect()函数,则套接口将会自动设置为非阻塞模式。如果已对一个套接口进行了WSAAsyncSelect()操作,则任何用ioctlsocket()调用来套接口重新设置成阻塞模式的操作都将失败,并返回WSAEINVAL错误。为了把套接口重新设置成阻塞模式,应用程序必须把IEvent参数置为0,调用WSAAsyncSelect()函数,以禁止WSAAsyncSelect()调用;或者令INetworkEvents参数等于0,调用WSAEventSelect()函数,从而禁止WSAEventSelect()

 

2FIONREAD该命令用于确定可从套接口s上自动读入的数据量。参数argp指向一个无符号长整型量,其中存有打算读入的字节数。如果使用WSAIoctl()函数,那么无符号整数是通过lpvOutBuffer返回的。如果套接口s面向数据流(SOCK_STREAM类型)的套接口,则FIONREAD返回一次recv()调用中所接收的数据总量,这通常与套接口中排队的数据总量相同;如果套接口s是面向数据报(SOCK_DGRAM类型)的套接口,则FIONREAD返回套接口上排队的第一个数据报的大小。

 

3SIOCATMARK该命令用于确定是否所有的带外数据都已被读入。这个命令仅适用于SOCK_STREAM类型的套接口,且该套接口已被设置为可以在线接收带外数据(SO_OOBINLINE)。如无带外数据等待读入,则该操作返回TRUE,否则返回FALSE,下一个recv()revcfrom()操作将检索“标记”前的一些或所有数据。应用程序可用SIOCATMARK操作来确定是否有数据剩余。如果在“紧急”带外数据前有常规数据,则按序接收这些数据(注意:recv()recvfrom()操作不会在一次调用中混淆常规数据和带外数据)。

对于ioctlsocket()函数来说,argp指向一个BOOL值,在其中存入返回值;而对于WSAIoctl()函数来说,会在lpvOutBuffer中返回指向布尔变量的指针。

 

Ioctlsocket函数使用实例如下:

//-------------------------

// Initialize Winsock

WSAData wsaData;

int iResult = WSAStartup(NAKEWORD(2,2), &wsaData);

if(iResult != NO_ERROR)

    printf("Error at WSAStartup/n");

 

//-------------------------

// Create a SOCKET object.

SOCKET m_socket;

m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if(m_socket == INVALID_SOCKET)

{

    printf("Error at socket():%d/n", WSAGetLastError());

    WSACleanup();

    return;           

}

 

//-------------------------

// Set the socket I/O mode: In this case FIONBIO

// enables or disables the blocking mode for the

// socket based on the numerical value of iMode.

// If iMode = 0, blocking is enabled;

// If iMode != 0, non-blocking mode is enabled.

u_long iMode = 0;

ioctlsocket(m_socket, FIONBIO, &iMode);

 

2)套接口I/O状态查询---select()

使用select()函数的好处是在进行I/O操作之前,可以首先判断能否向一个套接口写入数据,或者套接口上是否存在可读的数据。这样就可以防止应用程序在套接口处于阻塞模式时,对它进行的I/O操作被迫进入等待状态;同时也可以防止在套接口处于非阻塞模式时,产生WSAEWOULDBLOCK错误。

select函数原型如下:

int select(

  __in     int nfds, //本参数被忽略,仅起到与Berkeley API套接口兼容的作用

  __inout  fd_set *readfds, //检查可读性

  __inout  fd_set *writefds, //检查可写性

  __inout  fd_set *exceptfds, //检查例外数据

  __in     const struct timeval *timeout //本次select()调用最长等待时间

);

 

fd_set是一个结构类型说明符,代表这一系列特定套接口的集合:

typedef struct fd_set {

  u_int  fd_count; //套接口的数目

  SOCKET fd_array[FD_SETSIZE]; //表示数组中存放的套接口号,FD_SETSIZE是常量,定义为

抱歉!评论已关闭.