尝试将阻塞套接字服务器转换为非阻塞套接字服务器时,我遇到了一个奇怪的问题。虽然消息仅在使用阻塞套接字发送时才收到一次,但使用非阻塞套接字时,消息似乎被无限次接收。 以下是更改的代码:
return ::write(client, message, size);
到
// Nonblocking socket code
int total_sent = 0, result = -1;
while( total_sent < size ) {
// Create a temporary set of flags for use with the select function
fd_set working_set;
memcpy(&working_set, &master_set, sizeof(master_set));
// Check if data is available for the socket - wait 1 second for timeout
timeout.tv_sec = 1;
timeout.tv_usec = 0;
result = select(client + 1, NULL, &working_set, NULL, &timeout);
// We are able to write - do so
result = ::write(client, &message[total_sent], (size - total_sent));
if (result == -1) {
std::cerr << "An error has occured while writing to the server."
<< std::endl;
return result;
}
total_sent += result;
}
return 0;
编辑:主集的初始化如下所示:
// Private member variables in header file
fd_set master_set;
int sock;
...
// Creation of socket in class constructor
sock = ::socket(PF_INET, socket_type, 0);
// Makes the socket nonblocking
fcntl(sock,F_GETFL,0);
FD_ZERO(&master_set);
FD_SET(sock, &master_set);
...
// And then when accept is called on the socket
result = ::accept(sock, NULL, NULL);
if (result > 0) {
// A connection was made with a client - change the master file
// descriptor to note that
FD_SET(result, &master_set);
}
我已经确认,在这两种情况下,只会针对违规邮件调用一次代码。此外,客户端代码根本没有变化 - 有人有任何建议吗?
答案 0 :(得分:4)
fcntl(sock,F_GETFL,0);
如何使套接字无阻塞?
fcntl(sock, F_SETFL, O_NONBLOCK);
此外,您没有检查是否可以使用
实际写入套接字非阻塞样式FD_ISSET(client, &working_set);
答案 1 :(得分:2)
我不相信这段代码在“非阻塞”版本中只被调用一次(引用因为它不像Maister所指出的那样真的没有阻塞,look here),再次检查。如果阻止和非阻止版本一致,则非阻塞版本应返回total_sent(或大小)。使用return 0
代替调用者可能会相信没有发送任何内容。哪会导致无限发送...是不是发生了什么?
你的“非阻塞”代码也很奇怪。你似乎使用select来阻止它...好吧,超时为1秒,但你为什么不让它真的不阻塞? ie:删除所有select
内容并在write()
中测试错误大小写,错误为EWOULDBLOCK
。 <{1}}或select
用于多路复用。
此外,您应检查选择的错误并使用FD_ISSET检查套接字是否已准备就绪。如果1秒超时真的发生怎么办?或者如果某些中断停止了选择?如果在写入时发生错误,您还应该编写哪个错误,这比您的通用消息更有用。但我想这部分代码还远没有完成。
据我了解你的代码,它应该看起来有点像(如果代码在一个独特的线程或线程中运行,或者在接受连接时分叉会改变细节):
poll
// Creation of socket in class constructor
sock = ::socket(PF_INET, socket_type, 0);
fcntl(sock, F_SETFL, O_NONBLOCK);
// And then when accept is called on the socket
result = ::accept(sock, NULL, NULL);
if (result > 0) {
// A connection was made with a client
client = result;
fcntl(client, F_SETFL, O_NONBLOCK);
}