SOCKET
对于一个socket 是阻塞模式还是非阻塞模式的处理方法::
方法:fcntl 设置;
即先用fcntl的F_GETFL获取flags,
用F_SETFL设置flags|O_NONBLOCK;
(注意,取消非阻塞的方式是F_SETFL 设置flags&~O_NONBLOCK)
并在recv,send 时,将flag参数设置为MSG_DONTWAIT。
实现
fcntl 函数可以将一个socket 句柄设置成非阻塞模式:
| O_NONBLOCK); //设置成非阻塞模式;
flags = fcntl(sockfd,F_GETFL,0);
&~O_NONBLOCK); //设置成阻塞模式;
并在recv,recvfrom和send,sendto数据时,将flag设置为MSG_DONTWAIT
即:recv, send 函数的最后有一个flag 参数可以设置成MSG_DONTWAIT
recv(sockfd, buff, buff_size,MSG_DONTWAIT); //非阻塞模式的消息发送
send(scokfd, buff, buff_size, MSG_DONTWAIT); //非阻塞模式的消息接受
设置之后每次的对于sockfd 的操作都是非阻塞的。
(
connect 当返回0时,表示立即创建了socket链接,
当返回-1时,需要判断errno是否是EINPROGRESS(表示当前进程正在处理),否则失败
( 下面会有select或epoll监听fd是否建立链接,
select的例子:
int ret = ::connect(_socket_fd, add.addr(), add.length());
if(ret == 0)
{
//建立链接成功
}
else if(ret < 0 && errno == EINPROGRESS) //errno == EINPROGRESS表示正在建立链接
{
// 等待连接完成,errno == EINPROGRESS表示正在建立链接
fd_set set;
FD_ZERO(&set);
FD_SET(_socket_fd,&set);
time_t = 10; //(超时时间设置为10秒)
struct timeval timeo;
timeo.tv_sec = timeout / 1000;
timeo.tv_usec = (timeout % 1000) * 1000; int retval = select(_socket_fd + 1, NULL, &set, NULL, &timeo); //事件监听
if(retval < 0)
{
//建立链接错误close(_socket_fd)
}
else if(retval == 0) // 超时
{
//超时链接没有建立close(_socket_fd)
} //将检测到_socket_fd读事件或写时间,并不能说明connect成功
if(FD_ISSET(_socket_fd,&set))
{
int error = 0;
socklen_t len = sizeof(error);
if(getsockopt(_socket_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
{
//建立简介失败close(_socket_fd)
}
if(error != 0) // 失败
{
//建立链接失败close(_socket_fd)
}
else
{
//建立链接成功
}
}
}
else
{
//出现错误 close(_sock_fd)
}epoll的例子:
(//待续)
当epoll或select监听到sockfd上有EPOLL_IN或EPOLL_OUT时,即读写事件时,
并不能说明链接已经建立。
int error = 0;
socklen_t ilen = sizeof(error);
ret = getsockopt(fd,SOL_SOCKET,SO_ERROR,&error,&ilen);
if(ret < 0)
{
//说明链接建立失败,close(fd);
}
else if(error != 0 )
{ //说明链接建立失败,close(fd);
} else
{
//说明链接建立成功。即可以向fd上写数据。
}
);
当返回值为0时,表示对端已经关闭了这个链接,我们应该自己关闭这个链接,
即close(sockfd)。(因为异步操作会用select或epoll做事件触发,所以:)
如果使用select,应该将sockfd清除掉,不再监听。
如果使用epoll,系统会自己将sockfd清除掉,不再进行监听。
当返回值大于0 且 小于sizeof(buffer)时,表示数据肯定读完。(如果等于sizeof(buffer),可能有数据还没读,应该继续读,不可能有大于)
当返回值小于0,即等于-1时:
如果 errno 为 EAGAINE 或 EWOULDBLOCK
表示暂时无数据可读,可以继续读,或者等待epoll或select的后续通知。
如果 errno 为 EINTR
表示被中断了,可以继续读,或者等待epoll或select后续的通知。
。(此时应该close(sockfd))
(
发生EAGAINE,EWOULDBLOCK,EINTR错误时,
表明socket没有问题,即不用close(sockfd);
产生的原因:
EAGAINE 和 EWOULDBLOCK 可能是多进程读同一个sockfd,可能一个进程读
到数据,其他进程就读取不到数据,当然单个进程也可能出现这种情况。
对于这种错误,不需用close(sockfd)。
可以等待select或epoll的下一次触发,继续读。
)
(recvfrom的判断也是一样的)
send 返回值是实际发送的字符数。
因为我们知道要发送的总长度,所以,如果没有发送完,我们可以继续发送。
当返回值为 -1 时, 我们需要判断 errno:
如果errno为 EAGAINE 或 EWOULDBLOCK ,表示当前缓冲区写满,可以继续写,或者等待epoll或select的后续通知。
如果errno为EINTR ,表示被终端了,可以继续写,或者等待epoll或select的后续通知。
(
发生EAGAINE,EWOULDBLOCK,EINTR错误时,
表明socket没有问题,即不用close(sockfd);
产生的原因:
EAGAINE 和 EWOULDBLOCK 可能是多进程写同一个sockfd,可能一个进程写
了数据,其他进程就不能写数据,当然单个进程也可能出现这种情况。
对于这种错误,不需用close(sockfd)。
可以等待select或epoll的触发。
)
否则真的出错了,即errno不为EAGAINE或EWOULDBLOCK或EINTR,此时应该close(sockfd)
(sendto的判断也是一样)
)