Strange things with Winsock2 and system start command

huangapple go评论62阅读模式
英文:

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

> WSASocketsocket 函数创建的套接字句柄默认是可继承的。

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 并退出。

  1. 使用 WSASocketWWSA_FLAG_NO_HANDLE_INHERIT,但不要使用 socket
  2. 不要使用 system。使用 CreateProcessW
  3. 不要调用 cmd /c start cmd。如果需要 cmd,直接执行 cmd
  4. 不要将句柄(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..

  1. use WSASocketW with WSA_FLAG_NO_HANDLE_INHERIT but never socket
  2. never use system. use CreateProcessW.
  3. not call cmd /c start cmd. if you need cmd - just exec cmd
  4. not pass handle ( sock ) to another thread and in parallel close it

huangapple
  • 本文由 发表于 2023年5月21日 01:46:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/76296595.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定