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

关于recv函数的资料集

2018年04月11日 ⁄ 综合 ⁄ 共 20105字 ⁄ 字号 评论关闭

1.

Prototypes

#include <sys/types.h>
#include <sys/socket.h>

ssize_t recv(int s, void *buf, size_t len, int flags);
ssize_t recvfrom(int s, void *buf, size_t len, int flags,
                 struct sockaddr *from, socklen_t *fromlen);

Description

Once you have a socket up and connected, you can read incoming data from the remote side using therecv() (for TCPSOCK_STREAM
sockets) and recvfrom() (for UDPSOCK_DGRAM sockets).

Both functions take the socket descriptor s, a pointer to the bufferbuf, the size (in bytes) of the bufferlen, and a set offlags that
control how the functions work.

Additionally, the recvfrom() takes a
struct sockaddr*, from that will tell you where the data came from, and will fill infromlen with the size ofstruct sockaddr.
(You must also initializefromlen to be the size offrom orstruct sockaddr.)

So what wondrous flags can you pass into this function? Here are some of them, but you should check your local man pages for more information and what is actually supported on your system. You bitwise-or these together, or just setflags
to 0 if you want it to be a regular vanillarecv().

MSG_OOB

Receive Out of Band data. This is how to get data that has been sent to you with theMSG_OOB flag insend(). As the receiving side, you will have had signalSIGURG
raised telling you there is urgent data. In your handler for that signal, you could callrecv() with this
MSG_OOB flag.

MSG_PEEK

If you want to call recv() "just for pretend", you can call it with this flag. This will tell you what's waiting in the buffer for when you callrecv() "for real" (i.e.without
theMSG_PEEK flag. It's like a sneak preview into the nextrecv() call.

MSG_WAITALL

Tell recv() to not return until all the data you specified in thelen parameter. It will ignore your wishes in extreme circumstances, however, like if a signal interrupts the call or if
some error occurs or if the remote side closes the connection, etc. Don't be mad with it.

When you call recv(), it will block until there is some data to read. If you want to not block, set the socket to non-blocking or check withselect() orpoll()
to see if there is incoming data before callingrecv() orrecvfrom().

Return Value

Returns the number of bytes actually received (which might be less than you requested in thelen parameter), or-1 on error (anderrno will be set accordingly.)

If the remote side has closed the connection, recv() will return0. This is the normal method for determining if the remote side has closed the connection. Normality is good, rebel!

Example

// stream sockets and recv()

struct addrinfo hints, *res;
int sockfd;
char buf[512];
int byte_count;

// get host info, make socket, and connect it
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;
getaddrinfo("www.example.com", "3490", &hints, &res);
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
connect(sockfd, res->ai_addr, res->ai_addrlen);

// all right!  now that we're connected, we can receive some data!
byte_count = recv(sockfd, buf, sizeof buf, 0);
printf("recv()'d %d bytes of data in buf\n", byte_count);
// datagram sockets and recvfrom()

struct addrinfo hints, *res;
int sockfd;
int byte_count;
socklen_t fromlen;
struct sockaddr_storage addr;
char buf[512];
char ipstr[INET6_ADDRSTRLEN];

// get host info, make socket, bind it to port 4950
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(NULL, "4950", &hints, &res);
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
bind(sockfd, res->ai_addr, res->ai_addrlen);

// no need to accept(), just recvfrom():

fromlen = sizeof addr;
byte_count = recvfrom(sockfd, buf, sizeof buf, 0, &addr, &fromlen);

printf("recv()'d %d bytes of data in buf\n", byte_count);
printf("from IP address %s\n",
    inet_ntop(addr.ss_family,
        addr.ss_family == AF_INET?
            ((struct sockadd_in *)&addr)->sin_addr:
            ((struct sockadd_in6 *)&addr)->sin6_addr,
        ipstr, sizeof ipstr);

2.

嵌入式系统开发时自己写的一个标准例程,很可靠:
参数解释:
sck - socket
buf - 接收缓冲区
size-缓冲区大小
time_out-等待时间(按秒计)如果超时则返回
返回值:收到字节数,0表示超时等错误

int rcv(int sck, void * buf, int size, int time_out)
{
if (sck < 1 || !buf || size < 1) return 0;
timeval tv = { 0, 0}; timeval * ptv = 0;
if (time_out > 0) { tv.tv_sec = time_out; ptv = &tv; }
memset(buf, 0, size);
int r = 0; char * b = (char*) buf; int sz = size;
fd_set rd, er; int total = 0; time_t t0 = time(0); time_t t1 = 0;
do {
  FD_ZERO(&rd); FD_SET(sck, &rd);
  FD_ZERO(&er); FD_SET(sck, &er);
  r = select(sck + 1, &rd, 0, &er, ptv);
  if (r == -1) { nperror("select()"); return -1; }
  if (FD_ISSET(sck, &er)) {
   nperror("socket(shutdown)"); return -1;
  }//end if
  if (FD_ISSET(sck, &rd)) {
   r = recv(sck, b, sz, 0);
   if (r == -1) { nperror("recv()"); return -1; }
   total += r; sz -= r; b+= r;
  }//end if
  if (time_out > 0)
   t1 = time(0) - t0;
  else
   t1 = time_out - 1;
  //end if
}while(sz && t1 < time_out);
return total;
}//end if

 

3.

一个包没有固定长度,以太网限制在46-1500字节,1500就是以太网的MTU,超过这个量,TCP会为IP数据报设置偏移量进行分片传输,现在一般可允许应用层设置8k(NTFS系统)的缓冲区,8k的数据由底层分片,而应用层看来只是一次发送。
        windows的缓冲区经验值是4k。
        Socket本身分为两种,流(TCP)和数据报(UDP),你的问题针对这两种不同使用而结论不一样。甚至还和你是用阻塞、还是非阻塞Socket来编程有关。
       1、通信长度,这个是你自己决定的,没有系统强迫你要发多大的包,实际应该根据需求和网络状 况来决定。对于TCP,这个长度可以大点,但要知道,Socket内部默认的收发缓冲区大小大概是8K,你可以用SetSockOpt来改变。但对于 UDP,就不要太大,一般在1024至10K。注意一点,你无论发多大的包,IP层和链路层都会把你的包进行分片发送,一般局域网就是1500左右,广域 网就只有几十字节。分片后的包将经过不同的路由到达接收方,对于UDP而言,要是其中一个分片丢失,那么接收方的IP层将把整个发送包丢弃,这就形成丢
包。显然,要是一个UDP发包佷大,它被分片后,链路层丢失分片的几率就佷大,你这个UDP包,就佷容易丢失,但是太小又影响效率。最好可以配置这个值, 以根据不同的环境来调整到最佳状态。
        send()函数返回了实际发送的长度,在网络不断的情况下,它绝不会返回(发送失败的)错误,最多就是返回0。对于TCP你可以写一个循环发送。当 send函数返回SOCKET_ERROR时,才标志着有错误。但对于UDP,你不要写循环发送,否则将给你的接收带来极大的麻烦。所以UDP需要用 SetSockOpt来改变Socket内部Buffer的大小,以能容纳你的发包。明确一点,TCP作为流,发包是不会整包到达的,而是源源不断的到, 那接收方就必须组包。而UDP作为消息或数据报,它一定是整包到达接收方。
       2、关于接收,一般的发包都有包边界,首要的就是你这个包的长度要让接收方知道,于是就有个 包头信息,对于TCP,接收方先收这个包头信息,然后再收包数据。一次收齐整个包也可以,可要对结果是否收齐进行验证。这也就完成了组包过程。UDP,那 你只能整包接收了。要是你提供的接收Buffer过小,TCP将返回实际接收的长度,余下的还可以收,而UDP不同的是,余下的数据被丢弃并返回 WSAEMSGSIZE错误。注意TCP,要是你提供的Buffer佷大,那么可能收到的就是多个发包,你必须分离它们,还有就是当Buffer太小,而
一次收不完Socket内部的数据,那么Socket接收事件(OnReceive),可能不会再触发,使用事件方式进行接收时,密切注意这点。这些特性 就是体现了流和数据包的区别。
        补充一点,接收BuffSize >= 发送BuffSize >= 实际发送Size,对于内外部的Buffer都适用,上面讲的主要是Socket内部的Buffer大小关系。
       3、TCP是有多少就收多少,如果没有当然阻塞Socket的recv就会等,直到有数据, 非阻塞Socket不好等,而是返回WSAEWOULDBLOCK。UDP,如果没有数据,阻塞Socket就会等,非阻塞Socket也返回 WSAEWOULDBLOCK。如果有数据,它是会等整个发包到齐,并接收到整个发包,才返回。

4.

int send( SOCKET s, const char FAR *buf, int len, int flags );

不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。

 

该函数的第一个参数指定发送端套接字描述符;

 

第二个参数指明一个存放应用程序要发送数据的缓冲区;

 

第三个参数指明实际要发送的数据的字节数;

 

第四个参数一般置0。

 

这里只描述同步Socket的send函数的执行流程。当调用该函数时,

 

(1)send先比较待发送数据的长度len和套接字s的发送缓冲的长度, 如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;

(2)如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议 还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么send就比较s的发送缓冲区的剩余空间和len

(3)如果len大于剩余空间大小,send就一直等待协议把s的发送缓冲中的数据发送完

(4)如果len小于剩余 空间大小,send就仅仅把buf中的数据copy到剩余空间里(注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里)。

 

 

如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。

 

要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如 果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执 行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回 SOCKET_ERROR)

 

注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

 

通过测试发现,异步socket的send函数在网络刚刚断开时还能发送返回相应的字节数,同时使用select检测也是可写的,但是过几秒钟之后,再send就会出错了,返回-1。select也不能检测出可写了。

 

 

 

2. recv函数

int recv( SOCKET s, char FAR *buf, int len, int flags);

 

不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。该函数的第一个参数指定接收端套接字描述符;

 

第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;

 

第三个参数指明buf的长度;

 

第四个参数一般置0。

 

这里只描述同步Socket的recv函数的执行流程。当应用程序调用recv函数时,

 

(1)recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR,

 

(2)如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数 据,那么recv就一直等待,直到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以 在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),

 

recv函数返回其实际copy的字节数。如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。

 

注意:在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

阻塞就是干不完不准回来,   
非组赛就是你先干,我现看看有其他事没有,完了告诉我一声

我们拿最常用的send和recv两个函数来说吧...
比如你调用send函数发送一定的Byte,在系统内部send做的工作其实只是把数据传输(Copy)到TCP/IP协议栈的输出缓冲区,它执行成功并不代表数据已经成功的发送出去了,如果TCP/IP协议栈没有足够的可用缓冲区来保存你Copy过来的数据的话...这时候就体现出阻塞和非阻塞的不同之处了:对于阻塞模式的socket send函数将不返回直到系统缓冲区有足够的空间把你要发送的数据Copy过去以后才返回,而对于非阻塞的socket来说send会立即返回WSAEWOULDDBLOCK告诉调用者说:"发送操作被阻塞了!!!你想办法处理吧..."
对于recv函数,同样道理,该函数的内部工作机制其实是在等待TCP/IP协议栈的接收缓冲区通知它说:嗨,你的数据来了.对于阻塞模式的socket来说如果TCP/IP协议栈的接收缓冲区没有通知一个结果给它它就一直不返回:耗费着系统资源....对于非阻塞模式的socket该函数会马上返回,然后告诉你:WSAEWOULDDBLOCK---"现在没有数据,回头在来看看"

 

 

读数据的时候需要考虑的是当recv()返回的大小如果等于请求的大小,那么很有可能是缓冲区还有数据未读完,也意味着该次事件还没有处理完,所以还需要再次读取
while(rs)
{
buflen = recv(activeevents[i].data.fd, buf, sizeof(buf), 0);
if(buflen < 0)
{
    // 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读
    // 在这里就当作是该次事件已处理处.
    if(errno == EAGAIN)
     break;
    else
     return;
   }
   else if(buflen == 0)
   {
     // 这里表示对端的socket已正常关闭.
   }
   if(buflen == sizeof(buf)
     rs = 1;   //
需要再次读取
   else
     rs = 0;
}

还有,假如发送端流量大于接收端的流量(意思是epoll所在的程序读比转发的socket要快),由于是非阻塞的socket,那么send()函数虽然返回,但实际缓冲区的数据并未真正发给接收端,这样不断的读和发,当缓冲区满后会产生EAGAIN错误(参考man
send),
同时,不理会这次请求发送的数据.所以,需要封装socket_send()的函数用来处理这种情况,该函数会尽量将数据写完再返回,返回-1表示出错。在socket_send()内部,当写缓冲已满(send()返回-1,errnoEAGAIN),那么会等待后再重试.这种方式并不很完美,在理论上可能会长时间的阻塞在socket_send()内部,但暂没有更好的办法.

ssize_t socket_send(int sockfd, const char* buffer, size_t buflen)
{
ssize_t tmp;
size_t total = buflen;
const char *p = buffer;

while(1)
{
    tmp = send(sockfd, p, total, 0);
    if(tmp < 0)
    {
      // 当send收到信号时,可以继续写,但这里返回-1.
      if(errno == EINTR)
        return -1;

      // 当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,
      // 在这里做延时后再重试.
      if(errno == EAGAIN)
      {
        usleep(1000);
        continue;
      }

      return -1;
    }

    if((size_t)tmp == total)
      return buflen;

    total -= tmp;
    p += tmp;
}

return tmp;
}

6.

The Windows Sockets recv function receives data from a connected socket.

int recv (  SOCKET s,         char FAR* buf,    int len,          int flags       );

Parameters 参数

s
[in] A descriptor identifying a connected socket. socket连接字
buf
[out] A buffer for the incoming data. 收数据包缓冲区
len
[in] The length of buf. 缓冲的长度
flags
[in] A flag specifying the way in which the call is made. 接收方式

Remarks

The recv function is used to read incoming data on connection-oriented sockets, or connectionless sockets. When using a connection-oriented protocol, the sockets must be connected before callingrecv. When using a connectionless
protocol, the sockets must be bound before callingrecv.

recv函数用来读取面向连接的,和无连接的套接字发送过来的数据.当使用一个面向连接的协议,套接字必需先建立连接,当使用一个无连接的协议,套接字必需先绑定.

The local address of the socket must be known. For server applications, use an explicit function or an implicit or function. Explicit binding is discouraged for client applications. For client applications the socket can become bound implicitly to a local
address using , , , , or .

本地套接字的地址必需知道.对于服务器程序,使用显式的绑定函数或者隐式的accept或者WSAAccept接收函数,显式的绑定不适用于客户端程序.对于客户端程序套接字可以变成隐式的绑定到本地的一个地址通过使用connect,WSAConnect,sendto WSASendTo,或者WSAJoinLeaf函数.

For connected or connectionless sockets, this function restricts the addresses from which received messages are accepted. The function only returns messages from the remote address specified in the connection. Messages from other addresses are (silently)
discarded

对于面向连接的或者无连接的套接字,这个函数限定连接被接受的地址.这个函数只返回连接中的远程地址返回的信息.其它连接返回的信息是不显示的.

For connection-oriented sockets (type SOCK_STREAM for example), calling
recv
will return as much information as is currently available—up to the size of the buffer supplied. If the socket has been configured for in-line reception of out-of-band data (socket option SO_OOBINLINE) and out-of-band data is yet unread, only
out-of-band data will be returned. The application can use the ioctlsocket orWSAIoctl SIOCATMARK command to determine whether any more out-of-band data remains to be read.

 

对于面向连接的socket(例如流式),调用recv函数将返回缓冲区能容纳的尽可能最多的信息。如果socket配置了带外数据(选项SO_OOBINLINE),并且带外数据还没有读取,那么将只返回带外数据。应用程序可以使用ioctlsocket或者WSAIoctl SIOCATMARK 方法去获取是否还有带外数据等待读取。

For connectionless sockets (type SOCK_DGRAM or other message-oriented sockets), data is extracted from the first enqueued datagram (message) from the destination address specified by the function.

对于无连接的sockets管道(例如报文格式或者其他基于消息的sockets),获取数据的方式是通过从connect函数描述的目标地址获得的第一个报文(消息)。

If the datagram or message is larger than the buffer supplied, the buffer is filled with the first part of the datagram, andrecv
generates the error . For unreliable protocols (for example, UDP) the excess data is lost; for reliable protocols, the data is retained by the service provider until it is successfully read by callingrecv with a large enough buffer.
For TCP/IP, an application cannot receive from any multicast address until after becoming a group member.

 

如果报文或者消息大于接受缓冲区,缓冲区将被报文的第一部分填充,recv函数返回WSAEMSGSIZE错误,对于不可信协议(例如,UDP)升序的数据会丢失,对于可信协议,数据将被服务提供者保留直到提供一个足够大的缓冲区 使用recv函数读取。对于TCP/IP协议,一个应用程序不能接收任何多播地址的数据包,除非他本身成为多播地址的一员。

If no incoming data is available at the socket, the recv call blocks and waits for data to arrive according to the blocking rules defined for with the MSG_PARTIAL flag not set unless the socket is nonblocking. In this case, a value of SOCKET_ERROR
is returned with the error code set to . The , , or functions can be used to determine when more data arrives.

如果没有数据在socket,当WSARecv方法没有设置的MSG_PARTIAL标志,recv函数会阻塞等待数据取,除非socket不是阻塞的。在这种情况下(非阻塞模式),将返回SOCKET_ERROR,调用WSAGetLastError将返回.对于, , or  函数可以用来获取何时将有数据到达。

If the socket is connection oriented and the remote side has shut down the connection gracefully, and all data has been received, arecv will complete immediately with zero bytes received. If the connection has been reset, arecv
will fail with the error .

The flags parameter can be used to influence the behavior of the function invocation beyond the options specified for the associated socket. The semantics of this function are determined by the socket options and theflags parameter. The
latter is constructed by or'ing the following values:

Value Meaning
MSG_PEEK Peek at the incoming data. The data is copied into the buffer but is not removed from the input queue. The function then returns the number of bytes currently pending to receive.
MSG_OOB Process out-of-band data. (See section for a discussion of this topic.)

如果socket是面向连接的并且远端已经关闭优雅的关闭连接。并且所有的数据都已经收到,recv函数将立即返回0,如果连接被重置,recv将失败返回错误。flags参数可以用来设定函数相关的选项调用的行为。这个函数的语义决定于sokcet的选项和flags参数,后者

Value                                    含义

MSG_PEEK                           收取发送过来的数据,数据拷贝到缓冲区但是不从输入队列移除,函数返回当前还没有接收完阻塞的数据长度

MSG_OOB                            处理带外数据(参考 )

Return Values

If no error occurs, recv returns the number of bytes received. If the connection has been gracefully closed, the return value is zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling
.

Error Codes

WSANOTINITIALISED A successful must occur before using this function.
WSAENETDOWN The network subsystem has failed.
WSAEFAULT The buf parameter is not completely contained in a valid part of the user address space.
WSAENOTCONN The socket is not connected.
WSAEINTR The (blocking) call was canceled through .
WSAEINPROGRESS A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.
WSAENETRESET The connection has been broken due to the keep-alive activity detecting a failure while the operation was in progress.
WSAENOTSOCK The descriptor is not a socket.
WSAEOPNOTSUPP MSG_OOB was specified, but the socket is not stream-style such as type SOCK_STREAM, out-of-band data is not supported in the communication domain associated with this socket, or the socket is unidirectional and supports only send operations.
WSAESHUTDOWN The socket has been shut down; it is not possible to recv on a socket after has been invoked withhow set to SD_RECEIVE or SD_BOTH.
WSAEWOULDBLOCK The socket is marked as nonblocking and the receive operation would block.
WSAEMSGSIZE The message was too large to fit into the specified buffer and was truncated.
WSAEINVAL The socket has not been bound with , or an unknown flag was specified, or MSG_OOB was specified for a socket with SO_OOBINLINE enabled or (for byte stream sockets only)len was zero or negative.
WSAECONNABORTED The virtual circuit was terminated due to a time-out or other failure. The application should close the socket as it is no longer usable.
WSAETIMEDOUT The connection has been dropped because of a network failure or because the peer system failed to respond.
WSAECONNRESET The virtual circuit was reset by the remote side executing a "hard" or "abortive" close. The application should close the socket as it is no longer usable. On a UDP datagram socket this error would indicate that a previous send operation resulted in an
ICMP "Port Unreachable" message.

 7.

int select(
int
 nfds,
fd_set*
 readfds,
fd_set*
 writefds,
fd_set*
 exceptfds,
const struct timeval*
 timeout
);

nfds:本参数忽略,仅起到兼容作用。
   readfds:(可选)指针,指向一组等待可读性检查的套接口。
   writefds:(可选)指针,指向一组等待可写性检查的套接口。
   exceptfds:(可选)指针,指向一组等待错误检查的套接口。
   timeout:select()最多等待时间,对阻塞操作则为NULL。

timeout为结构timeval,用来设置select()的等待时间,其结构定义如下
struct timeval
{
time_t tv_sec; //second 秒
time_t tv_usec; //microsecond 微妙
};

注释:
   本函数用于确定一个或多个套接口的状态。对每一个套接口,调用者可查询它的可读性、可写性及错误状态信息。用fd_set结构来表示一组等待检查的套接口。在调用返回时,这个结构存有满足一定条件的套接口组的子集,并且select()返回满足条件的套接口的数目。有一组宏可用于对fd_set的操作,这些宏与Berkeley Unix软件中的兼容,但内部的表达是完全不同的。
   readfds参数标识等待可读性检查的套接口。如果该套接口正处于监听listen()状态,则若有连接请求到达,该套接口便被标识为可读,这样一个accept()调用保证可以无阻塞完成。对其他套接口而言,可读性意味着有排队数据供读取。或者对于SOCK_STREAM类型套接口来说,相对于该套接口的虚套接口已关闭,于是recv()或recvfrom()操作均能无阻塞完成。如果虚电路被“优雅地”中止,则recv()不读取数据立即返回;如果虚电路被强制复位,则recv()将以WSAECONNRESET错误立即返回。如果SO_OOBINLINE选项被设置,则将检查带外数据是否存在(参见setsockopt())。
   writefds参数标识等待可写性检查的套接口。如果一个套接口正在connect()连接(非阻塞),可写性意味着连接顺利建立。如果套接口并未处于connect()调用中,可写性意味着send()和sendto()调用将无阻塞完成。〔但并未指出这个保证在多长时间内有效,特别是在多线程环境中〕。
   exceptfds参数标识等待带外数据存在性或意味错误条件检查的套接口。请注意如果设置了SO_OOBINLINE选项为假FALSE,则只能用这种方法来检查带外数据的存在与否。对于SO_STREAM类型套接口,远端造成的连接中止和KEEPALIVE错误都将被作为意味出错。如果套接口正在进行连接connect()(非阻塞方式),则连接试图的失败将会表现在exceptfds参数中。
   如果对readfds、writefds或exceptfds中任一个组类不感兴趣,可将它置为空NULL。
   在winsock.h头文件中共定义了四个宏来操作描述字集。FD_SETSIZE变量用于确定一个集合中最多有多少描述字(FD_SETSIZE缺省值为64,可在包含winsock.h前用#define FD_SETSIZE来改变该值)。对于内部表示,fd_set被表示成一个套接口的队列,最后一个有效元素的后续元素为INVAL_SOCKET。宏为:
   FD_CLR(s,*set):从集合set中删除描述字s。
   FD_ISSET(s,*set):若s为集合中一员,非零;否则为零。
   FD_SET(s,*set):向集合添加描述字s。
   FD_ZERO(*set):将set初始化为空集NULL。
   timeout参数控制select()完成的时间。若timeout参数为空指针,则select()将一直阻塞到有一个描述字满足条件。否则的话,timeout指向一个timeval结构,其中指定了select()调用在返回前等待多长时间。如果timeval为{0,0},则select()立即返回,这可用于探询所选套接口的状态。如果处于这种状态,则select()调用可认为是非阻塞的,且一切适用于非阻塞调用的假设都适用于它。举例来说,阻塞钩子函数不应被调用,且WINDOWS套接口实现不应yield。

返回值:
   select()调用返回处于就绪状态并且已经包含在fd_set结构中的描述字总数;如果超时则返回0;否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。

错误代码:
   WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
   WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。
   WSAEINVAL:超时时间值非法。
   WSAEINTR:通过一个WSACancelBlockingCall()来取消一个(阻塞的)调用。
   WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。
   WSAENOTSOCK:描述字集合中包含有非套接口的元素。

范例 :

sock= socket(AF_INET,SOCK_STREAM,0);

 

struct sockaddr_in addr;      //告诉sock 应该再什么地方licence
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(11111);   //端口啦
addr.sin_addr.s_addr=htonl(INADDR_ANY);        //在本机的所有ip上开始监听

bind (sock,(sockaddr *)&addr,sizeof(addr));//bind....

listen(sock,5);                   //最大5个队列

SOCKET socka;                    //这个用来接受一个连接
fd_set rfd;                     // 描述符集 这个将用来测试有没有一个可用的连接
struct timeval timeout;

FD_ZERO(&rfd);                     //总是这样先清空一个描述符集

timeout.tv_sec=60;                //等下select用到这个
timeout.tv_usec=0;

u_long ul=1;

ioctlsocket(sock,FIONBIO,&ul);    //用非阻塞的连接

//现在开始用select
FD_SET(sock,&rfd);    //把sock放入要测试的描述符集 就是说把sock放入了rfd里面 这样下一步调用select对rfd进行测试的时候就会测试sock了(因为我们将sock放入的rdf) 一个描述符集可以包含多个被测试的描述符, 
if(select(sock+1,&rfd,0,0, &timeout)==0) 
{ 
//这个大括号接上面的,返回0那么就超过了timeout预定的时间

//处理....

}

if(FD_ISSET(sock,&rfd))
{      //有一个描述符准备好了

socka=accept(sock,0,0);     //一个用来测试读 一个用来测试写

FD_ZERO(&rfd);

FD_ZERO(&wfd);

FD_SET(socka,&rfd);//把socka放入读描述符集

FD_SET(sockb,&rfd);//把sockb放入读描述符集

FD_SET(socka,&wfd);把socka放入写描述符集

FD_SET(sockb,&wfd);把sockb放入写描述符集

if(SOCKET_ERROR!=select(0,&rfd,&wfd,0,0))      //测试这两个描述符集,永不超时 其中rfd只用来测试读 wfd只用来测试写

{      //没有错误

if(FD_ISSET(socka,&rfd))    //socka可读

{...}

if(FD_ISSET(sockb,&rfd)   //sockb可读

{...}

if(FD_ISSET(socka,&wfd) //socka 可写

{...}

if(FD_ISSET(sockb,&wfd) //sockb可写

{...}

}

 

 

二、linux c中

select(I/O多工机制)


表头文件

#i nclude<sys/time.h>
#i nclude<sys/types.h>
#i nclude<unistd.h>

定义函数

int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);

函数说明

select()用来等待文件描述词状态的改变。参数n代表最大的文件描述词加1,参数readfds、writefds 和exceptfds 称为描述词组,是用来回传该描述词的读,写或例外的状况。底下的宏提供了处理这三种描述词组的方式:
FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位
FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真
FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位
FD_ZERO(fd_set *set); 用来清除描述词组set的全部位

参数

timeout为结构timeval,用来设置select()的等待时间,其结构定义如下
struct timeval
{
time_t tv_sec;
time_t tv_usec;
};

返回值

如果参数timeout设为NULL则表示select()没有timeout。

错误代码

执行成功则返回文件描述词状态已改变的个数,如果返回0代表在描述词状态改变前已超过timeout时间,当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds,exceptfds和timeout的值变成不可预测。
EBADF 文件描述词为无效的或该文件已关闭
EINTR 此调用被信号所中断
EINVAL 参数n 为负值。
ENOMEM 核心内存不足

范例

常见的程序片段:fs_set readset;
FD_ZERO(&readset);
FD_SET(fd,&readset);
select(fd+1,&readset,NULL,NULL,NULL);
if(FD_ISSET(fd,readset){……}

下面是linux环境下select的一个简单用法

#i nclude <sys/time.h>
#i nclude <stdio.h>
#i nclude <sys/types.h>
#i nclude <sys/stat.h>
#i nclude <fcntl.h>
#i nclude <assert.h>

int main ()
{
int keyboard;
int ret,i;
char c;
fd_set readfd;
struct timeval timeout;
keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK);
assert(keyboard>0);
while(1)
    {
timeout.tv_sec=1;
timeout.tv_usec=0;
FD_ZERO(&readfd);
FD_SET(keyboard,&readfd);
ret=select(keyboard+1,&readfd,NULL,NULL,&timeout);
if(FD_ISSET(keyboard,&readfd))
    {
      i=read(keyboard,&c,1);
          if('/n'==c)
          continue;
      printf("hehethe input is %c/n",c);
     
       if ('q'==c)
      break;
      }
}
}

 

抱歉!评论已关闭.