英文:
Strange things with Winsock2 and system start command
问题
程序的预期行为是:程序在一个单独的窗口中运行 cmd,而程序自身继续执行。实际行为是:程序直到新的 cmd 窗口关闭之前都不会结束。在定义 CORRECT_SEQUENCE 时,它能够正常运行。但是在没有定义它时,即使 sock 被关闭,accept() 函数也不会返回。调试器显示我们卡在 join() 函数上。
这种依赖的原因是:在没有定义 CORRECT_SEQUENCE 的情况下,程序在执行 system("start cmd") 后立即启动了监听线程,而此时主线程继续执行并关闭了 sock。然而,在某些情况下,accept() 函数可能仍然在等待连接,导致主线程无法继续执行,从而导致程序不会结束。
(请注意,由于您要求只返回翻译好的部分,我已经省略了一些可能的解释和上下文。如果需要更详细的解释,请随时询问。)
英文:
There is a program
#include <Winsock2.h>
#include <stdint.h>
#include <thread>
const uint16_t ANY_VALID_PORT = 12345;
SOCKET sock = INVALID_SOCKET;
void listenPort()
{
sockaddr_in addr {};
int addrSize = sizeof(addr);
accept(sock, (SOCKADDR*)&addr, &addrSize);
}
// #define CORRECT_SEQUENCE
int main()
{
WSADATA wsaData = { 0 };
WSAStartup(MAKEWORD(2, 2), &wsaData);
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in clientService {};
clientService.sin_family = AF_INET;
clientService.sin_port = htons(ANY_VALID_PORT);
clientService.sin_addr.s_addr = INADDR_ANY;
bind(sock, (SOCKADDR*)&clientService, sizeof(clientService));
listen(sock, 2);
#ifdef CORRECT_SEQUENCE
system("start cmd");
std::thread listening { listenPort };
#else
std::thread listening { listenPort };
system("start cmd"); //could be virtually any program
#endif
closesocket(sock);
listening.join();
WSACleanup();
}
Expected behavior: the program finishes while cmd continues to run in a separate window.
Real behavior: the program doesn't finish until the new cmd is closed.
When CORRECT_SEQUENCE is defined, it works just fine. But without it, accept() doesn't return even if sock is closed. The debugger tells us that we are stuck up on join().
What is the reason for this dependency? cmd (or even empty program with only while(1); in the main()) have nothing with our port.
答案1
得分: 1
> WSASocket
或 socket
函数创建的套接字句柄默认是可继承的。
system
使用 CreateProcessA
启动进程,bInheritHandles = TRUE
。结果,sock
被复制到子进程。
accept
返回控制权,当对 sock
的 最后 句柄关闭后,但在 system("start cmd");
之后,在 cmd
中存在另一个 sock
句柄。只有在关闭 cmd 之后,accept 返回错误(可能是 WSAEINTR
)。
同时你的代码两种变体都是错误的,都不起作用。你也可以在调用 accept
前调用 closesocket(sock);
。所以都是错误的。
此外,system("start cmd");
在效率上不高。这会为 cmd.exe 调用 CreateProcessA
,命令行为 /c start cmd - 因此第一个 cmd 运行第二个 cmd 并退出。
- 使用
WSASocketW
和WSA_FLAG_NO_HANDLE_INHERIT
,但不要使用socket
。 - 不要使用
system
。使用CreateProcessW
。 - 不要调用
cmd /c start cmd
。如果需要 cmd,直接执行cmd
。 - 不要将句柄(
sock
)传递给另一个线程,并在同时关闭它。
英文:
> A socket handle created by the WSASocket or the socket
> function is inheritable by default.
the system
start process with CreateProcessA
with bInheritHandles = TRUE
. as result sock
is duplicated to child process.
accept
return control, when last handle to sock
closed, but after system("start cmd");
exist another sock
handle in cmd
. only after you close cmd - accept return with error (probably WSAEINTR
)
also the the both variants of your code is wrong. and both not work. you also can call closesocket(sock);
before call accept
. so all is wrong.
also system("start cmd");
is extermally not efficient. this call CreateProcessA
for cmd.exe with command line /c start cmd - so first cmd run second cmd and exit..
- use
WSASocketW
withWSA_FLAG_NO_HANDLE_INHERIT
but neversocket
- never use
system
. useCreateProcessW
. - not call
cmd /c start cmd
. if you need cmd - just execcmd
- not pass handle (
sock
) to another thread and in parallel close it
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论