C 启用非阻塞套接字
我有一个用C/C++编写的服务器。 我为连接设置包装器,如下所示:C 启用非阻塞套接字,c,sockets,nonblocking,asyncsocket,C,Sockets,Nonblocking,Asyncsocket,我有一个用C/C++编写的服务器。 我为连接设置包装器,如下所示: //START WRAPPER void Server::init_address(int port) { memset(&(this->serv_addr), 0, sizeof(this->serv_addr)); this->serv_addr.sin_family = AF_INET; this->serv_addr.sin_port = htons(port);
//START WRAPPER
void Server::init_address(int port)
{
memset(&(this->serv_addr), 0, sizeof(this->serv_addr));
this->serv_addr.sin_family = AF_INET;
this->serv_addr.sin_port = htons(port);
this->serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
}
int Server::w_socket()
{
int retv;
retv = socket(PF_INET, SOCK_STREAM, 0);
//FIXME
//fcntl(retv, F_SETFL, O_NONBLOCK);
if(retv == -1)
{
std::string err_msg(strerror(errno));
err_msg = "[socket] " + err_msg;
throw err_msg;
}
else
{
int reuse_opt = 1;
if(setsockopt(retv, SOL_SOCKET, SO_REUSEADDR, &reuse_opt, sizeof(int))==-1)
{
perror("setsockopt error");
exit(1);
}
return retv;
}
}
void Server::w_bind()
{
int retv;
retv = bind(this->sock_fd,
(struct sockaddr*) &(this->serv_addr),
sizeof(this->serv_addr));
if(retv == -1)
{
std::string err_msg(strerror(errno));
err_msg = "[bind] " + err_msg;
throw err_msg;
}
}
void Server::w_listen()
{
int retv;
retv = listen(this->sock_fd, 3);
if (retv == -1)
{
std::string err_msg(strerror(errno));
err_msg = "[listen] " + err_msg;
throw err_msg;
}
}
int Server::w_accept(struct sockaddr_in* client_addr)
{
int retv;
int socklen = sizeof(sockaddr_in);
retv = accept(this->sock_fd, (struct sockaddr*)client_addr, (socklen_t*)&socklen);
if(retv == -1)
{
std::string err_msg(strerror(errno));
err_msg = "[accept] " + err_msg;
throw err_msg;
}
else
{
return retv;
}
}
int Server::recvtimeout(int s, char *buf, int len, int timeout)
{
fd_set fds;
int n;
struct timeval tv;
// set up the file descriptor set
FD_ZERO(&fds);
FD_SET(s, &fds);
// set up the struct timeval for the timeout
tv.tv_sec = timeout;
tv.tv_usec = 0;
// wait until timeout or data received
n = select(s+1, &fds, NULL, NULL, &tv);
if (n == 0){
return -2; // timeout!
}
if (n == -1){
return -1; // error
}
// data must be here, so do a normal recv()
return recv(s, buf, len, 0);
}
// END WRAPPER
我的目标是启用非阻塞套接字模式。
我试过做fcntl(retv,F_SETFL,O_NONBLOCK)如Beej手册所述,但我收到错误:[接受]资源暂时不可用
这个问题的一个解决方案是使用select函数,但我已经在recvtimeout函数中使用了它,就像Beej-guide所说的那样
因此,我不知道如何解决这个问题以启用非阻塞套接字模式。您会收到错误,因为套接字是非阻塞的
要么在出现错误时执行忙循环(当accept
返回-1
时,检查errno
,查看eWoldBlock
或EAGAIN
)。另一种也是推荐的解决方案是使用选择
查看套接字何时可读,然后可以调用接受
编辑:如何使用选择
您需要有一个更高级别的事件循环,用于检查是否可以读取侦听套接字或连接的套接字。如果侦听套接字可读,则可以接受新连接;如果连接的套接字可读,则可以从中读取
比如:
for (;;)
{
fd_set readset;
FD_ZERO(&readset);
FD_SET(listening_socket, &readset);
int maxfd = listening_socket;
if (connected_socket >= 0)
{
FD_SET(connected_socket, &readset);
maxfd = max(maxfd, connected_socket);
}
// NULL timeout (5-th argument) means wait until event
select(maxfd + 1, &readset, NULL, NULL, NULL);
if (FD_ISSET(listening_socket, &readset))
{
accept_new_connection(listening_socket);
}
if (connected_socket >= 0 && FD_ISSET(connected_socket, &readset))
{
if (!read_from_socket(connected_socket))
{
close(connected_socket);
connected_socket = -1;
}
}
}
如果您有多个连接的套接字,请将它们放在一个简单的链接列表中,并在循环中添加/检查它们。当它们关闭时从列表中删除。您会收到错误,因为套接字是非阻塞的
要么在出现错误时执行忙循环(当accept
返回-1
时,检查errno
,查看eWoldBlock
或EAGAIN
)。另一种也是推荐的解决方案是使用选择
查看套接字何时可读,然后可以调用接受
编辑:如何使用选择
您需要有一个更高级别的事件循环,用于检查是否可以读取侦听套接字或连接的套接字。如果侦听套接字可读,则可以接受新连接;如果连接的套接字可读,则可以从中读取
比如:
for (;;)
{
fd_set readset;
FD_ZERO(&readset);
FD_SET(listening_socket, &readset);
int maxfd = listening_socket;
if (connected_socket >= 0)
{
FD_SET(connected_socket, &readset);
maxfd = max(maxfd, connected_socket);
}
// NULL timeout (5-th argument) means wait until event
select(maxfd + 1, &readset, NULL, NULL, NULL);
if (FD_ISSET(listening_socket, &readset))
{
accept_new_connection(listening_socket);
}
if (connected_socket >= 0 && FD_ISSET(connected_socket, &readset))
{
if (!read_from_socket(connected_socket))
{
close(connected_socket);
connected_socket = -1;
}
}
}
如果您有多个连接的套接字,请将它们放在一个简单的链接列表中,并在循环中添加/检查它们。关闭时从列表中删除。在您的解决方案中,您使用的是连接套接字的select
call,而不是listen套接字的
如果已将listen socket
也更改为非阻塞,则在调用accept
之前,必须使用select forlisten socket
在您的解决方案中,您正在为连接套接字使用select
call,但对listen socket
不使用相同的方法
如果已将listen socket
也更改为非阻塞,则在调用accept
之前,必须使用select forlisten socket
我已经在accept函数上用do-while语句检查了EAGAIN的errno,我已经取消了fcntl的注释(retv、F_SETFL、O_NONBLOCK);现在一切似乎都是工作,但真的是非阻塞吗?我已经关闭了服务器,但它仍然崩溃。关于使用select查看套接字何时可读的第二个解决方案,如何使用它?我的select in recvtimeout函数不足以执行此操作?谢谢。好的,最后一个问题。在我的情况下,我可以修改recvtimeout函数还是必须使用另一个select函数执行另一个函数?因为当我调用receive时,这两个函数都是高级别的。我已经在accept函数上用do while语句检查了errno for EAGAIN,我已经取消了fcntl的注释(retv、F_SETFL、O_NONBLOCK);现在一切似乎都是工作,但真的是非阻塞吗?我已经关闭了服务器,但它仍然崩溃。关于使用select查看套接字何时可读的第二个解决方案,如何使用它?我的select in recvtimeout函数不足以执行此操作?谢谢。好的,最后一个问题。在我的情况下,我可以修改recvtimeout函数还是必须使用另一个select函数执行另一个函数?因为当我呼叫接收时,两者都是高电平。