【问题标题】:How to interrupt accept() in a TCP/IP server?如何中断 TCP/IP 服务器中的 accept()?
【发布时间】:2017-05-30 10:15:48
【问题描述】:

我正在开发一个视觉应用程序,它有两种模式: 1) 参数设置 2) 自动 问题出在 2) 中,当我的应用程序通过 TCP/IP 等待信号时。程序在调用accept()-methode 时冻结。我想在 GUI 上提供更改模式的可能性。所以如果模式正在改变,它是由另一个信号(message_queue)提供的。所以我想中断接受状态。 是否有简单的可能性来中断接受?

std::cout << "TCPIP " << std::endl;

client = accept(slisten, (struct sockaddr*)&clientinfo, &clientinfolen);

if (client != SOCKET_ERROR)
    cout << "client accepted: " << inet_ntoa(clientinfo.sin_addr) << ":"
         << ntohs(clientinfo.sin_port) << endl;

//receive the message from client
//recv returns the number of bytes received!!
//buf contains the data received
int rec = recv(client, buf, sizeof(buf), 0);
cout << "Message: " << rec << " bytes and the message   " << buf << endl;

我读到了select(),但我不知道如何使用它。谁能给我一个提示,例如如何在我的代码中实现select()

谢谢。

最好的问候,

T

【问题讨论】:

  • 多线程(将渲染与实际进程分开或让线程渲染窗口的不同部分)不是可接受的解决方案?
  • 问题是,我无法离开由accept()引起的冻结状态。我必须中断它以再次检查模式。
  • GUI 和网络 io 之间的交互可能取决于操作系统。什么是你的,或者你真的需要一种完全便携的方式(可能更难......)?

标签: c++


【解决方案1】:

解决方案是仅在有传入连接请求时才调用accept()。您可以通过轮询侦听套接字来做到这一点,您还可以在其中添加其他文件描述符、使用超时等。

您没有提及您的平台。 Linux上看epoll(),UNIX看poll()/select(),Windows我不知道。

【讨论】:

    【解决方案2】:

    一般的方法是使用本地 TCP 连接,UI 线程可以通过该连接中断 select 调用。一般架构将使用:

    • slisten 和本地 TCP 连接上等待 select 的专用线程
    • UI 线程和等待线程之间的 TCP 连接(Unix 或类 Unix 系统上的 Unix 域套接字,或 Windows 上的 127.0.0.1)
    • 根据需要在两个线程之间进行各种同步/消息

    只需声明select 应该读取slisten 和本地套接字。它会在一个准备好后立即返回,您将能够知道哪个准备好了。

    【讨论】:

      【解决方案3】:

      由于您没有指定您的平台,并且网络,尤其是异步,是特定于平台的,我想您需要一个跨平台的解决方案。 Boost.Asio 非常适合这里:http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/reference/basic_socket_acceptor/async_accept/overload1.html

      链接示例:

      void accept_handler(const boost::system::error_code& error)
      {
        if (!error)
        {
          // Accept succeeded.
        }
      }
      
      ...
      
      boost::asio::ip::tcp::acceptor acceptor(io_service);
      ...
      boost::asio::ip::tcp::socket socket(io_service);
      acceptor.async_accept(socket, accept_handler);
      

      如果Boost 是个问题,Asio 可以是仅标头库并在没有 Boost 的情况下使用:http://think-async.com/Asio/AsioAndBoostAsio

      【讨论】:

      • 我相信每个平台都支持select syscall。 OP 对此很感兴趣。
      • Boost 很棒..但是天哪,这个例子让我想起了为什么我不使用它。这么多冒号!
      【解决方案4】:

      一种方法是在超时的循环中运行select

      slisten 置于非阻塞模式(这不是绝对必要的,但有时accept 会阻塞,即使select 另有说明)然后:

      fd_set read_fds;
      FD_ZERO(&read_fds);
      FD_SET(slisten, &read_fds);
      
      struct timeval timeout;
      timeout.tv_sec = 1;  // 1s timeout
      timeout.tv_usec = 0;
      
      int select_status;
      while (true) {
          select_status = select(slisten+1, &read_fds, NULL, NULL, &timeout);
          if (select_status == -1) {
              // ERROR: do something
          } else if (select_status > 0) {
              break;  // we have data, we can accept now
          }
          // otherwise (i.e. select_status==0) timeout, continue
      }
      
      client = accept(slisten, ...);
      

      这将允许您每秒捕获一次信号。更多信息在这里:

      http://man7.org/linux/man-pages/man2/select.2.html

      和Windows版本(差不多):

      https://msdn.microsoft.com/pl-pl/library/windows/desktop/ms740141(v=vs.85).aspx

      【讨论】:

      • @T_F 它确实适用于 Windows。您只需要适当的标题(我无法帮助您,请尝试谷歌搜索)。
      • 似乎没有回答 Op 的问题。 accept() 创建一个新的套接字 fd,而 select() 不这样做。它只扫描现有的套接字。与大多数手册页一样,该手册页非常无用。
      • @Cerin 我认为您误解了解决方案。在我的代码中,我仍然使用接受。不同的是,用select我知道accept不会阻塞,因为有一个挂起的socket。 Select 仅用于超时。
      猜你喜欢
      • 2017-07-15
      • 2012-05-23
      • 2020-08-02
      • 2016-12-30
      • 2016-02-17
      • 1970-01-01
      • 2020-04-15
      • 2010-10-22
      • 1970-01-01
      相关资源
      最近更新 更多