A. recv是阻塞还是非阻塞的
网络编程函数如recv是阻塞(同步)还是非阻塞(异步)取决于在调用recv函数前创建的套接字socket是阻塞还是非阻塞。socket默认创建时设定为阻塞模式;若要将socket设定为非阻塞模式,可以在socket创建时设定为非阻塞模式,那么函数recv就是非阻塞的。
可以通过一下几种方法设定socket为非阻塞:
1.linux平台可以在利用socket()函数创建socket时指定socket是异步(非阻塞)的:
int socket(int domain, int type, int protocol);
在参数type中设置SOCK_NONBLOCK标志即可,例如:
int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
2.windows和linux平台accept()函数返回的socekt也是阻塞的,linux另外提供了一个accept4()函数,可以直接将socket设置为非阻塞模式:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
只要将accept4()最后一个参数flags设置成SOCK_NONBLOCK即可。
3.除了在创建socket时,将socket设置为非阻塞模式,还可以通过以下函数来设置:
linux平台可以调用fcntl()或ioctl()函数,例如:
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
ioctl(sockfd, FIONBIO, 1); //1:非阻塞 0:阻塞
windows平台可调用ioctlsocket函数:
int ioctlsocket(
_In_ SOCKET s,
_In_ long cmd,
_Inout_ u_long *argp
);
将cmd参数设置为FIONBIO,*argp=0即设置成阻塞模式,而*argp非0即可设置成非阻塞模式。但windows平台一个地方需要注意,如果对一个socket调用了WSAAsyncSelect()或WSAEventSelect()函数后,你再调用ioctlsocket()函数将该socket设置为阻塞模式,则会失败,必须先调用WSAAsyncSelect()设置lEvent参数为0或调用WSAEventSelect()设置lNetworkEvents参数为0来分别禁用WSAAsyncSelect()或WSAEventSelect(),再次调用ioctlsocket()将该socket设置成阻塞模式才会成功。因为调用WSAAsyncSelect()或WSAEventSelect()函数会自动将socket设置成非阻塞模式。
B. c语言的recv()非阻塞方法怎么弄哦
需要将recv设置超时,Linux下设置超时如下:
//设置发送超时
struct timeval timeout={3,0};//3s
setsockopt(socket,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(struct timeval));
//设置接收超时
setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval));
windows下设置超时如下:
int timeout = 3000; //3s
int ret=setsockopt(sock_fd,SOL_SOCKET,SO_SNDTIMEO,&timeout,sizeof(timeout));
int ret=setsockopt(sock_fd,SOL_SOCKET,SO_RCVTIMEO,&timeout,sizeof(timeout));
C. socket关闭后为什么recv不返回
recv是socket编程中最常用的函数之一,在阻塞状态的recv有时候会返回不同的值,而对于错误值也有相应的错误码,分别对应不同的状态,下面是我针对常见的几种网络状态的简单总结。
首先阻塞接收的recv有时候会返回0,这仅在对端已经关闭TCP连接时才会发生。
而当拔掉设备网线的时候,recv并不会发生变化,仍然阻塞,如果在这个拔网线阶段,socket被关掉了,后果可能就是recv永久的阻塞了。
所以一般对于阻塞的socket都会用setsockopt来设置recv超时。
当超时时间到达后,recv会返回错误,也就是-1,而此时的错误码是EAGAIN或者EWOULDBLOCK,POSIX.1-2001上允许两个任意一个出现都行,所以建议在判断错误码上两个都写上。
如果socket是被对方用linger为0的形式关掉,也就是直接发RST的方式关闭的时候,recv也会返回错误,错误码是ENOENT
还有一种经常在代码中常见的错误码,那就是EINTER,意思是系统在接收的时候因为收到其他中断信号而被迫返回,不算socket故障,应该继续接收。但是这种情况非常难再现,我尝试过一边一直在不停的发信号,一边用recv接收数据,也没有出现过。这种异常错误我附近只有一个朋友在用write的时候见到过一次,但是总是会有概率出现的,所以作为完善的程序必须对此错误进行特殊处理。
一般设置超时的阻塞recv常用的方法都如下:
while(1)
{
cnt = (int)recv(m_socket, pBuf,RECVSIZE, 0);
if( cnt >0 )
{
//正常处理数据
}
else
{
if((cnt<0) &&(errno == EAGAIN||errno == EWOULDBLOCK||errno == EINTR)) //这几种错误码,认为连接是正常的,继续接收
{
continue;//继续接收数据
}
break;//跳出接收循环
}
}
阻塞与非阻塞recv返回值没有区分,都是 <0 出错 =0 连接关闭 >0 接收到数据大小。
Linux环境下,须如下定义:struct timeval timeout = {3,0};
//设置发送超时
setsockopt(socket,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(struct timeval));
//设置接收超时
setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval));
D. recv函数返回什么值
recv函数返回其实际的字节数,如果recv在时出错,那么它返回SOCKET_ERROR。如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
扩展阅读,linux recv函数详解:
1 #include <sys/socket.h>
2 ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags);
recv 的前3个参数等同于read函数。
flags参数值为0或:
flags
说明
recv
send
MSG_DONTWAIT
仅本操作非阻塞
MSG_OOB 发送或接收带外数据
MSG_PEEK
窥看外来消息
MSG_WAITALL
等待所有数据
recv函数解析:
sockfd: 接收端套接字描述符
buff: 用来存放recv函数接收到的数据的缓冲区
nbytes: 指明buff的长度
flags: 一般置为0
1) recv先等待s的发送缓冲区的数据被协议传送完毕,如果协议在传送sock的发送缓冲区中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR
2)
如果套接字sockfd的发送缓冲区中没有数据或者数据被协议成功发送完毕后,recv先检查套接字sockfd的接收缓冲区,如果sockfd的接收缓
冲区中没有数据或者协议正在接收数据,那么recv就一起等待,直到把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲区中的数据
到buff中(注意协议接收到的数据可能大于buff的长度,所以在这种情况下要调用几次recv函数才能把sockfd的接收缓冲区中的数据
完。recv函数仅仅是数据,真正的接收数据是协议来完成的)
3) recv函数返回其实际的字节数,如果recv在时出错,那么它返回SOCKET_ERROR。如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
4) 在unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用 recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。