【问题标题】:How to properly maintain a listening port for a long time?如何正确长时间维护一个监听端口?
【发布时间】:2013-03-02 19:30:59
【问题描述】:

我用纯 C 语言编写了这个小型服务器应用程序,它监听给定端口中的传入连接,非常简单。

它使用通常的套接字初始化过程,创建socket() 然后bind() 到端口,说它是listen(),并无限循环通过select() 等待传入连接到accept()

一切都很好,就像一个魅力,除了如果我让这个东西运行几个月,监听端口会关闭,而应用程序服务器会继续运行而不知道它,因为我写它是为了相信监听套接字会如果没有被告知,则不会关闭。

所以问题是:为什么在我的应用程序不关心的情况下关闭端口,我可以做些什么来防止它发生?

这是预期的行为吗?我应该检查某种异常还是在侦听套接字上进行“健康检查”以在必要时重新打开它?

代码:https://gist.github.com/Havenard/e930be035a3bee75c018(是的,我意识到我使用0 作为错误提示,这是不好的做法和东西,但正如我在 cmets 中解释的那样,当我设置套接字时,它与问题无关文件描述符到0是停止循环并关闭应用程序。

【问题讨论】:

  • 不正常。我会怀疑您的应用程序中存在某种资源泄漏,例如文件描述符用完,因为您没有关闭 accept() 返回的新套接字。
  • Select() 将导致报告/错误的文件描述符无效。也许你通过 dup2() 关闭它?
  • 这不正常。显示您的代码。
  • 源代码上的一些(风格)提示:1)main() 函数太大而无法阅读 2) 信号处理程序在 main() 中定义。 C 没有嵌套函数。 3)你测试你的文件描述符为零('if (skt_accept == 0)... )尝试使用-1作为无效的文件描述符(并在无效时将它们设置为-1)4)在你的gobackground()函数中你在关闭filedesctiptors 0,1后执行printf, 2. 5) 你真的应该忽略 select() 的一些 -1 返回,比如 EAGAIN。 6)链表是凌乱和过于复杂,恕我直言。 7) ' ' 比 0x20 更具可读性
  • @Havenard 是的,我不是 taklink 关于在侦听套接字上发送,而是在接受的套接字上发送。您的 30 个客户端中的一个可能会在您仍在与您的 irc/like 服务器通话时突然断开与它的连接。当它发生时,如果您尝试向客户端套接字发送()某些东西,整个程序将使用 SIGPIPE 退出。

标签: c sockets daemon


【解决方案1】:

我会先清理它:

  • 将其分解为更小、可读、可验证、可测试的函数
  • 链表的使用看起来很乱;它可以简化很多,也许通过引入一些通用函数。
  • 将所有愚蠢的 '\x20' 字符常量替换为更易读的 ' ' 等效项
  • 避免像这里if (n_case > 0) memcpy(nick, node->nick, (n_case > 32 ? 32 : n_case));这样的显式魔法常量; sizeof 是你的朋友。
  • 不要使用零作为未使用文件描述符的标记值;请改用 -1。
  • 对大小和索引使用无符号类型;负索引会破坏内存,折叠无符号类型会很快失败。 (failfast 是你的朋友)

这只是几个小时的编辑。

我的猜测是,在清理/重构之后,您的“错误”会神奇地浮出水面。

脚注:不,我不会为你做你的工作。不是为了100分,不是为了1000分。请自己收拾烂摊子。

【讨论】:

  • 这段旧代码只是我知道最终会出现问题的程序的一个例子。我编写了其他有相同问题的类似程序,其中一个在 Windows Server 上运行。我毫不怀疑这不是导致问题的程序,因为如您所见,如果 select() 返回 skt_bind 被某些异常设置为零,它应该退出。这些是它关闭套接字skt_bind 的唯一方法,并且在这两种方法中它都会终止程序,这不会发生,这意味着skt_bind 仍然作为有效的套接字存在,尽管端口已关闭。
  • 我真的怀疑什么可能会在没有我的程序关注的情况下关闭一个开放的端口,以及我应该如何正确处理它。我已经尝试过模拟一些网络事故,例如 IP 更改或网线断开连接,但没有任何东西使端口关闭,也不会因此而停止工作。它在长时间打开后关闭,并且由于这个长时间因素,我很难监控和测试如何继续。
  • 它不会在您的程序不关心的情况下关闭:可以肯定的是,侦听 fd 是由您的程序在您制造的错误中的某个地方关闭的。在 fd 创建后输入 fd 值的 printf(或使用 lsof 查找),然后在 gdb 中对 dup2 进行中断,关闭,然后 fclose 以在代码中找到停止监听的位置。
  • 根据我的经验(30 年),每次程序员都会说“我毫不怀疑不是程序导致了问题" 他们最终证明自己错了。再说一次,你是否有一个间歇性的网络中断,足以让 Windows 声明接口不起作用,可能触发关闭,然后再次声明它起作用?如果不是这样,那么问题很可能出在您的代码中。某处。
【解决方案2】:

这个答案主要是对你打电话给close()的地方的代码审查。

第 330 行:您关闭了套接字,但不像在代码中的其他地方那样立即继续。这可能会导致奇怪的行为。

第 928 行:在大多数地方,您在调用 close() 后将客户端或服务器套接字设置为 0。你不会在这个电话之后。

第 1193 行:与第 928 行相同的注释。

第 1195 行:与第 928 行相同的注释。

第 1218 行:与第 928 行相同的注释。

第 1234 行:与第 928 行相同的注释。

第 1236 行:与第 928 行相同的注释。

当我编译带有完整警告的代码时,我看到许多地方编译器注意到函数声明为返回值,但没有返回值。

x.c:582: warning: no return statement in function returning non-void
x.c:591: warning: no return statement in function returning non-void
x.c:598: warning: no return statement in function returning non-void
x.c:609: warning: no return statement in function returning non-void
x.c:620: warning: no return statement in function returning non-void
x.c:728: warning: no return statement in function returning non-void
x.c:779: warning: no return statement in function returning non-void

还有许多其他问题,如其他帖子中所述。

就调试这个问题而言,如果我怀疑绑定套接字被提前关闭,我会使用我自己的版本拦截 close() 调用,该版本断言正在关闭的描述符不应与绑定套接字匹配。

但是,正如 wildplasser 所指出的,select() 如果已关闭,则会返回有关无效描述符的错误。

【讨论】:

    【解决方案3】:

    错误是您使用 0 作为无效的文件描述符。 0 完全有效,通常是标准输入。然后在信号处理程序中将侦听器设置为 0。然后你使用 0 作为无 fd,并且在某些时候你在某个套接字上执行 close(0),有一些分支在没有检查它的情况下执行 close(fd) 并且有效地关闭了侦听器。 阻止侦听器工作的另一个可能选项是溢出积压。

    还有一个问题 - 对 fds 使用 unsigned int。 系统调用在错误时返回 -1 ...并且不会检测到该错误 如果分配给 unsigned int struct identd_node -> 无符号整数句柄; struct thread_node -> unsigned int skt_clnt, skt_serv;

    【讨论】:

    • 那是我的子弹#6。使用 0 作为 fds 的哨兵值确实是非常错误的。
    • 没关系。如果它被信号设置为那个值,它只检查 0。目前没有其他方法可以改变它的值。
    【解决方案4】:

    看起来您的代码需要有 2 个连续错误才能导致失败。

    如果您从选择中得到错误,为什么不立即打印出原因?

    在第281行,printf errno/perror 找出问题所在?

    【讨论】:

    • 如果它达到那个点,程序就会终止。它没有结束,它只是继续,好像一切都很好。
    • 哦,是的,这种检查两次的想法来自 Linux inetd 源代码。它也会检查两次!
    【解决方案5】:

    虽然系统不应该像描述的那样运行,但它有时会这样做。对于服务器系统,您通常需要在外部(从脚本)或从代码中的特殊线程执行运行状况检查调用。

    因此,如果您检测到连续几次尝试都无法连接到服务器(由于可能的过载情况而需要很少),您可以考虑套接字损坏并重新创建它或重新启动服务器。

    【讨论】:

    • 有时会发生这种情况什么时候?我在网络编程的几十年中从未见过这种行为。
    猜你喜欢
    • 2013-07-22
    • 2016-06-14
    • 1970-01-01
    • 2012-03-27
    • 2012-06-13
    • 2013-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多