正确地用C ++写入非阻塞套接字

时间:2010-06-30 22:42:45

标签: c++ sockets

尝试将阻塞套接字服务器转换为非阻塞套接字服务器时,我遇到了一个奇怪的问题。虽然消息仅在使用阻塞套接字发送时才收到一次,但使用非阻塞套接字时,消息似乎被无限次接收。 以下是更改的代码:

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);
}

我已经确认,在这两种情况下,只会针对违规邮件调用一次代码。此外,客户端代码根本没有变化 - 有人有任何建议吗?

2 个答案:

答案 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);
}