c++ linuxaccept()在套接字关闭后阻塞

发布于 2025-01-07 08:50:35 字数 294 浏览 3 评论 0原文

我有一个线程侦听新连接

new_fd = accept(Listen_fd, (struct sockaddr *) & their_addr, &sin_size);

,另一个线程在需要关闭程序时关闭 Listen_fd 。然而,Listen_fd 关闭后,它仍然阻塞。当我使用 GDB 尝试调试accept() 时,它不会阻塞。我认为这可能是 SO_LINGER 的问题,但默认情况下它不应该打开,并且在使用 GDB 时不应该改变。知道发生了什么,或者有任何其他关闭列表套接字的建议吗?

I have a thread that listens for new connections

new_fd = accept(Listen_fd, (struct sockaddr *) & their_addr, &sin_size);

and another thread that closes Listen_fd when when it's time to close the program. After Listen_fd is closed however, it still blocks. When I use GDB to try and debug accept() doesn't block. I thought that it could be a problem with SO_LINGER, but it shouldn't be on by default, and shouldn't change when using GDB. Any idea whats going on, or any other suggestion to closing the listing socket?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

情愿 2025-01-14 08:50:35

使用:sock.shutdown (socket.SHUT_RD)

然后accept将返回EINVAL。不需要难看的跨线程信号!

来自 Python 文档:
"注意 close() 会释放与连接相关的资源,但不一定立即关闭连接。如果您想及时关闭连接,请调用 shutdown()close() 之前。”

http://docs.python.org/3/library/socket .html#socket.socket.close

几年前,我在用 C 编程时遇到了这个问题。但直到今天,在 Python 中遇到了同样的问题,并思考使用信号之后,我才找到了解决方案(恶心!) ,然后记住有关关闭的注释!

至于评论说你不应该跨线程关闭/使用套接字......在 CPython 中,全局解释器锁应该保护你(假设你使用的是文件对象而不是原始的整数文件描述符)。

这是示例代码:

import socket, threading, time

sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind (('', 8000))
sock.listen (5)

def child ():
  print ('child  accept ...')
  try:  sock.accept ()
  except OSError as exc :  print ('child  exception  %s' % exc)
  print ('child  exit')

threading.Thread ( target = child ).start ()

time.sleep (1)
print ('main   shutdown')
sock.shutdown (socket.SHUT_RD)

time.sleep (1)
print ('main   close')
sock.close ()

time.sleep (1)
print ('main   exit')

Use: sock.shutdown (socket.SHUT_RD)

Then accept will return EINVAL. No ugly cross thread signals required!

From the Python documentation:
"Note close() releases the resource associated with a connection but does not necessarily close the connection immediately. If you want to close the connection in a timely fashion, call shutdown() before close()."

http://docs.python.org/3/library/socket.html#socket.socket.close

I ran into this problem years ago, while programming in C. But I only found the solution today, after running into the same problem in Python, AND pondering using signals (yuck!), AND THEN remembering the note about shutdown!

As for the comments that say you should not close/use sockets across threads... in CPython the global interpreter lock should protect you (assuming you are using file objects rather than raw, integer file descriptors).

Here is example code:

import socket, threading, time

sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind (('', 8000))
sock.listen (5)

def child ():
  print ('child  accept ...')
  try:  sock.accept ()
  except OSError as exc :  print ('child  exception  %s' % exc)
  print ('child  exit')

threading.Thread ( target = child ).start ()

time.sleep (1)
print ('main   shutdown')
sock.shutdown (socket.SHUT_RD)

time.sleep (1)
print ('main   close')
sock.close ()

time.sleep (1)
print ('main   exit')
别挽留 2025-01-14 08:50:35

当调用非有效套接字 FD 时,accept 的行为是未定义的。 “不是有效的套接字FD”包括曾经是有效套接字但后来被关闭的数字。您可能会说“但是 Borealid,它应该返回 EINVAL!”,但这并不能保证 - 例如,相同的 FD 编号可能会在 closeaccept< 之间重新分配给不同的套接字。 /code> 调用。

因此,即使您要隔离并纠正导致程序失败的任何因素,您将来仍然可能再次失败。不要这样做 - 更正导致您尝试接受已关闭套接字上的连接的错误。

如果您的意思是之前进行的accept调用继续close之后阻塞,那么您应该做什么向在 accept 中阻塞的线程发送信号。这将为它提供 EINTR,并且它可以完全脱离 - 然后然后关闭套接字。不要从使用它的线程以外的线程中关闭它。

The behavior of accept when called on something which is not a valid socket FD is undefined. "Not a valid socket FD" includes numbers which were once valid sockets but have since been closed. You might say "but Borealid, it's supposed to return EINVAL!", but that's not guaranteed - for instance, the same FD number might be reassigned to a different socket between your close and accept calls.

So, even if you were to isolate and correct whatever makes your program fail, you could still begin to fail again in the future. Don't do it - correct the error that causes you to attempt to accept a connection on a closed socket.

If you meant that a call which was previously made to accept continues blocking after close, then what you should do is send a signal to the thread which is blocked in accept. This will give it EINTR and it can cleanly disengage - and then close the socket. Don't close it from a thread other than the one using it.

鸢与 2025-01-14 08:50:35

shutdown() 函数可能就是您正在寻找的。调用 shutdown(Listen_fd, SHUT_RDWR) 将导致任何对 accept() 的阻塞调用返回 EINVAL。将调用 shutdown() 与原子标志的使用结合起来可以帮助确定 EINVAL 的原因。

例如,如果您有此标志:

std::atomic<bool> safe_shutdown(false);

那么您可以通过以下方式指示另一个线程停止侦听:

shutdown_handler([&]() {
  safe_shutdown = true;
  shutdown(Listen_fd, SHUT_RDWR);
});

为了完整起见,以下是您的线程如何调用接受:

while (true) {
  sockaddr_in clientAddr = {0};
  socklen_t clientAddrSize = sizeof(clientAddr);
  int connSd = accept(Listen_fd, (sockaddr *)&clientAddr, &clientAddrSize);
  if (connSd < 0) {
    // If shutdown_handler() was called, then exit gracefully
    if (errno == EINVAL && safe_shutdown)
      break;
    // Otherwise, it's an unrecoverable error
    std::terminate();
  }
  char clientname[1024];
  std::cout << "Connected to "
            << inet_ntop(AF_INET, &clientAddr.sin_addr, clientname,
                         sizeof(clientname))
            << std::endl;
  service_connection(connSd);
}

The shutdown() function may be what you are looking for. Calling shutdown(Listen_fd, SHUT_RDWR) will cause any blocked call to accept() to return EINVAL. Coupling a call to shutdown() with the use of an atomic flag can help to determine the reason for the EINVAL.

For example, if you have this flag:

std::atomic<bool> safe_shutdown(false);

Then you can instruct the other thread to stop listening via:

shutdown_handler([&]() {
  safe_shutdown = true;
  shutdown(Listen_fd, SHUT_RDWR);
});

For completeness, here's how your thread could call accept:

while (true) {
  sockaddr_in clientAddr = {0};
  socklen_t clientAddrSize = sizeof(clientAddr);
  int connSd = accept(Listen_fd, (sockaddr *)&clientAddr, &clientAddrSize);
  if (connSd < 0) {
    // If shutdown_handler() was called, then exit gracefully
    if (errno == EINVAL && safe_shutdown)
      break;
    // Otherwise, it's an unrecoverable error
    std::terminate();
  }
  char clientname[1024];
  std::cout << "Connected to "
            << inet_ntop(AF_INET, &clientAddr.sin_addr, clientname,
                         sizeof(clientname))
            << std::endl;
  service_connection(connSd);
}
魄砕の薆 2025-01-14 08:50:35

这是一种解决方法,但您可以在超时的情况下对 Listen_fd 进行 select,如果发生超时,请检查您是否要关闭该程序。如果是,则退出循环,如果不是,则返回步骤 1 并执行下一个 select

It's a workaround, but you could select on Listen_fd with a timeout, and if a timeout occured check that you're about to close the program. If so, exit the loop, if not, go back to step 1 and do the next select.

云朵有点甜 2025-01-14 08:50:35

您是否检查 close 的返回值?
来自Linux手册页,(http://www.kernel.org/doc/man-pages/online/pages/man2/close.2.html)
“当同一进程中的其他线程中的系统调用可能正在使用文件描述符时,关闭它们可能是不明智的。由于文件描述符可能会被重用,因此存在一些模糊的竞争条件,可能会导致意想不到的副作用”。
您可以使用选择而不是接受并等待来自另一个线程的某些事件,然后关闭侦听器线程中的套接字。

Are you checking the return value of close?
From linux manpages, (http://www.kernel.org/doc/man-pages/online/pages/man2/close.2.html)
"It is probably unwise to close file descriptors while they may be in use by system calls in other threads in the same process. Since a file descriptor may be reused, there are some obscure race conditions that may cause unintended side effects".
You can use a select instead of an accept and wait for some event from the other thead, then close the socket in the listener thread.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文