繁体   English   中英

当线程A关闭套接字对的末尾时,为什么windows select()并不总是通知线程B的select()?

[英]Why is windows select() not always notifying thread B's select() when thread A closes its end of a socket pair?

我在Windows XP(SP3)下遇到的情况让我疯狂,而且我已经到了我的系绳的末端,所以也许有人可以提供一些灵感。

我有一个C ++网络程序(非GUI)。 该程序用于在Windows,MacOS / X和Linux下编译和运行,因此它使用select()和非阻塞I / O作为其事件循环的基础。

除了网络功能外,该程序还需要从stdin读取文本命令,并在stdin关闭时正常退出。 在Linux和MacOS / X下,这很容易 - 我只是在我的read fd_set中包含STDIN_FILENO来select(),而select()在stdin关闭时返回。 我检查FD_ISSET(STDIN_FILENO,&readSet)是否为真,尝试从stdin读取一些数据,recv()返回0 / EOF,所以我退出进程。

另一方面,在Windows下,您无法选择STDIN_FILE_HANDLE,因为它不是真正的套接字。 您也不能对STDIN_FILE_HANDLE执行非阻塞读取。 这意味着无法从主线程读取stdin,因为ReadFile()可能无限期地阻塞,导致主线程停止提供其网络功能。

没问题,我说,我只会产生一个线程来处理stdin。 该线程将在无限循环中运行,在ReadFile(stdinHandle)中阻塞,并且只要ReadFile()返回数据,stdin线程就会将该数据写入TCP套接字。 该套接字的另一端将由主线程选择(),因此主线程将看到通过连接进入的stdin数据,并以与其他任何操作系统相同的方式处理“stdin”。 如果ReadFile()返回false以指示stdin已关闭,则stdin-thread仅关闭其套接字对的结尾,以便主线程将通过select()通知,如上所述。

当然,Windows不会有一个很好的socketpair()函数,所以我不得不推出自己用听(),连接(),并接受()(如见于CreateConnectedSocketPair()函数在这里 ,但我做到了一般而言,它似乎有效。

问题是它不能100%工作。 特别是,如果stdin在程序启动的几百毫秒内关闭,大约一半时间主线程没有得到任何关于套接字对的stdin-end已经关闭的通知。 我的意思是,我可以看到(通过我的printf() - 调试)stdin线程在其套接字上调用closesocket(),我可以看到主线程是select() - 关联socket(即套接字对的另一端),但是select()永远不会返回......如果它确实返回,由于某些其他套接字选择了ready-for-whatever,FD_ISSET(main_thread_socket_for_socket_pair,&readSet)返回0,好像连接没有关闭。

在这一点上,我唯一的假设是Windows的select()实现中存在一个错误,导致主线程的select()没有注意到套接字对的另一端已被stdin-thread关闭。 有另一种解释吗? (请注意,此问题已在Windows 7下报告,尽管我没有在该平台上亲自查看过它)

仅仅为了记录,这个问题完全是一个完全不同的问题,与线程,Windows或stdin无关。 实际问题是一个进程间死锁,其中父进程被阻塞,等待子进程退出,但有时子进程会被同时阻塞,等待父进程为它们提供一些数据,所以什么都不会前进。

向所有人道歉,浪费你的时间在红鲱鱼身上; 如果有一种标准方法可以将这种情况视为无根据,请告诉我,我会这样做。

-Jeremy

你有可能有竞争条件吗? 例如。 在stdin线程有机会尝试关闭其套接字之前,您是否确保CreateConnectedSocketPair()函数已经确定返回?

我在你的代码中学习。 在CreateConnectedSocketPair()中,socket1用于listen(),newfd用于send / recv数据。 那么,为什么“socket1 = newfd”? 那怎么关闭listenfd呢?

不是一个解决方案,但作为一种解决方法,你不能通过TCP套接字发送一些神奇的“stdin已关闭”消息,让你的接收端断开它的套接字,当它看到并运行任何'stdin has closed'处理程序?

老实说,你的代码太长了,我现在没有时间花在它上面。

最有可能的问题是在某些情况下关闭套接字不会导致正常(FIN)关闭。

检查从您的选择返回的异常可能会捕获其余的情况。 还有(苗条)可能性,实际上没有通知发送到另一端关闭的套接字。 在这种情况下,除端点之间的超时或“保持活动”/ ping消息之外没有办法知道套接字已关闭。

如果你想弄清楚到底发生了什么,请打破wireshark并查找FIN和RST(以及没有任何东西)。 如果您在套接字关闭时看到正确的FIN序列,那么问题必须在您的代码中。 如果你看到RST,它可能会被异常捕获,如果你没有看到任何东西,你需要在你的协议中设计一种方法来“ping”连接的每一面,以确保它们仍然存活,或者设置足够短的超时以获得更多数据。

而不是追逐select()中的感知错误,我将解决你的原始谬论,使你远离简单,可靠的单线程设计。

你说“你也不能对STDIN_FILE_HANDLE进行非阻塞读取。这意味着没有办法从主线程中读取stdin,因为ReadFile()可能会无限期地阻塞”,但这根本不是全部。 查看ReadConsoleInput,WSAEventSelect和WaitForMultipleObjects。 stdin句柄只有在有输入时才会发出信号,ReadConsoleInput会立即返回(在Unix中,select()背后的想法几乎相同)。

或者,使用ReadFileEx和WaitForMultipleObjectsEx让控制台读取APC(这不是异步的,它在主线程上运行,只在WaitForMultipleObjectsEx或其他显式等待函数期间运行)。

如果你想坚持使用第二个线程来获取stdin上的异步I / O,那么你可能会尝试关闭传递给select的句柄而不是执行套接字关闭(通过另一端的closesocket)。 根据我的经验,当其中一个等待的fds关闭时,select()往往会很快返回。

或者,也许你的问题是相反的。 选择文档说“对于面向连接的套接字,可读性还可以指示已从对等方接收到关闭套接字的请求”。 通常,您通过调用shutdown()而不是closesocket()发送“关闭套接字的请求”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM