在C ++中正确写入非阻塞套接字 [英] Properly writing to a nonblocking socket in C++

查看:102
本文介绍了在C ++中正确写入非阻塞套接字的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm having a strange problem while attempting to transform a blocking socket server into a nonblocking one. Though the message was only received once when being sent with blocking sockets, using nonblocking sockets the message seems to be received an infinite number of times. Here is the code that was changed:

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;

编辑:主集的初始化看起来像这样:

The initialization of the master set looks like this:

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



我确认在这两种情况下,代码只被调用一次违规的消息。此外,客户端代码根本没有改变 - 有没有任何建议?

I have confirmed that in both cases, the code is only being called once for the offending message. Also, the client side code hasn't changed at all - does anyone have any recommendations?

推荐答案

我不相信代码在非阻塞版本中真的只调用一次(引号,因为它不是真正的非阻塞的,正如Maister指出的,看看这里),再次检查。如果阻塞版本和非阻塞版本一致,则非阻塞版本应返回total_sent(或size)。使用 return 0 ,而呼叫者可能相信没有发送。这将导致无限发送...是不是发生了什么?

I do not believe that this code is really called only once in the "non blocking" version (quotes because it is not really non-blocking yet as Maister pointed out, look here), check again. If the blocking and non blocking versions are consistent, the non blocking version should return total_sent (or size). With return 0 instead caller is likely to believe nothing was sent. Which would cause infinite sending... is it not what's happening ?

此外,你的非阻塞代码是很奇怪的。你似乎使用选择,使它阻塞反正...好吧,超时1s,但为什么不让它真的非阻塞?即:删除所有选择的东西,并在 write()中测试错误情况,错误是 EWOULDBLOCK 选择 poll 用于多路复用。

Also your "non blocking" code is quite strange. You seem to use select to make it blocking anyway... Ok, with a timeout of 1s, but why don't you make it really non blocking ? ie: remove all the select stuff and test for error case in write() with errno being EWOULDBLOCK. select or poll are for multiplexing.

应检查选择的错误,并使用FD_ISSET检查套接字是否真的准备好了。如果1秒超时真的发生了怎么办?或者如果选择被一些中断停止?如果在写入中出现错误,您还应该写出哪个错误,这比通用消息更有用。但我想这部分代码还远远没有完成。

Also you should check errors for select and use FD_ISSET to check if socket is really ready. What if the 1 s timeout really happen ? Or if select is stopped by some interruption ? And if an error occurs in write, you should also write which error, that is much more useful than your generic message. But I guess this part of code is still far from finished.

据我理解你的代码,它应该看起来像这样(如果代码运行在一个唯一的线程或线程,或接受连接时分叉会改变细节):

As far as I understand your code it should probably look somewhat like that (if the code is running in an unique thread or threaded, or forking when accepting a connection would change details):

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

// Nonblocking socket code
result = ::write(client, &message[total_sent], (size - total_sent));
if (result == -1) {
  if (errno == EWOULDBLOCK){
      return 0;
  }
  std::cerr << "An error has occured while writing to the server."
          << std::endl;
  return result;
}
return size;

这篇关于在C ++中正确写入非阻塞套接字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆