简体   繁体   English

WinSock 非阻塞 I/O

[英]WinSock Non-Blocking I/O

I have a WSASocket which when connected calls CreateProcess and servers cmd.exe, I wanted to implement a pipe in-between the processes hStdInput and the Socket Handle to parse commands that get sent over the socket, everything seems to run smoothly except for when I run a command like "ping 127.0.0.1" and have to wait for output, nothing shows up until I send more data over the socket, it seems my ReadFile call is blocking hStdOut handler from sending anything.我有一个 WSASocket,当连接调用 CreateProcess 和服务器 cmd.exe 时,我想在进程 hStdInput 和套接字句柄之间实现一个管道来解析通过套接字发送的命令,除了当我运行像“ping 127.0.0.1”这样的命令并且必须等待输出,在我通过套接字发送更多数据之前没有任何显示,似乎我的 ReadFile 调用阻止了 hStdOut 处理程序发送任何内容。 Is there any way to fix this?有没有什么办法解决这一问题? Please don't be offended by my code I'm writing this project as a learning exercise, any help would be appreciated.请不要被我的代码冒犯我正在编写这个项目作为学习练习,任何帮助将不胜感激。

int syncShell(SOCKET *ConnectSocket) {
    int iResult = 0;
    printf("Spawning process\n");
    char Process[] = "C:\\Windows\\System32\\cmd.exe";
    STARTUPINFO sinfo;
    PROCESS_INFORMATION pinfo;
    memset(&sinfo, 0, sizeof(sinfo));
    sinfo.cb = sizeof(sinfo);
    sinfo.dwFlags = (STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW);

    // create pipe for external commands
    SECURITY_ATTRIBUTES  saAttr;
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
    saAttr.bInheritHandle = TRUE; 
    saAttr.lpSecurityDescriptor = NULL;
    HANDLE hReadPipe = NULL, hWritePipe = NULL;
    iResult = CreatePipe(&hReadPipe, &hWritePipe, &saAttr, DEFAULT_BUFLEN);
    if (iResult == 0) {
        printf("Pipe Error");
    }

    sinfo.hStdOutput = sinfo.hStdError = (HANDLE) *ConnectSocket;
    sinfo.hStdInput = hReadPipe;

    if (!CreateProcessA(NULL, Process, NULL, NULL, TRUE, 0, NULL, NULL, &sinfo, &pinfo)) {
        printf("CreateProcess failed (%d).\n", GetLastError());
    }

    // implement pipe logic
    char buf[DEFAULT_BUFLEN];
    DWORD len = 0;
    WSABUF DataBuf;

    while (1) {
        // causing the block?
        iResult = ReadFile((HANDLE) *ConnectSocket, buf, DEFAULT_BUFLEN, &len, NULL);
        if (iResult == 0) {
            printf("File Error or non-blocking");
        }
        else {
            printf("%d: %.*s\n", len, len, buf);
            WriteFile(hWritePipe, buf, len, NULL, NULL);
        }
        Sleep(1000);
    }

    WaitForSingleObject(pinfo.hProcess, INFINITE); // waits till proc finishes
    CloseHandle(pinfo.hProcess);
    CloseHandle(pinfo.hThread);
    printf("Process exited\n");

    return 0;
}

First, according to the [ ReadFile ] document:首先,根据【 ReadFile 】文档:

For asynchronous read operations, hFile can be any handle that is opened with the FILE_FLAG_OVERLAPPED flag by the CreateFile function, or a socket handle returned by the socket or accept function.对于异步读取操作,hFile 可以是CreateFile函数使用FILE_FLAG_OVERLAPPED标志打开的任何句柄,或者是socketaccept函数返回的套接字句柄。

socket create a socket handle with WSA_FLAG_OVERLAPPED by default. socket默认使用WSA_FLAG_OVERLAPPED创建一个套接字句柄。 You will get error code 87( ERROR_INVALID_PARAMETER ) if you pass a Overlapped handle and set the last parameter of ReadFile as NULL .如果传递 Overlapped 句柄并将ReadFile的最后一个参数设置为NULL则会得到错误代码 87( ERROR_INVALID_PARAMETER )。 Sample to use Overlapped:使用重叠的示例:

OVERLAPPED oRead = { 0 };
oRead.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);  
iResult = ReadFile((HANDLE)ConnectSocket, buf, DEFAULT_BUFLEN, &len, &oRead);
if (!iResult && GetLastError() == ERROR_IO_PENDING)
{
    WaitForSingleObject(oRead.hEvent, INFINITE);
}
buf[oRead.InternalHigh] = 0;  //set string terminator for printf
printf("%s\n", buf);
WriteFile(hWritePipe1, buf, oRead.InternalHigh, NULL, NULL);

And it's better to use recv() directly:而且最好直接使用recv()

iResult = recv(ConnectSocket, buf, DEFAULT_BUFLEN, 0);
buf[iResult] = 0;  //set string terminator for printf
printf("%s\n", buf);
WriteFile(hWritePipe1, buf, iResult, NULL, NULL);

In addition, the overlapped socket could be used for redirected IO to child processes You could create 2 pipes to communicate with child process:此外,重叠套接字可用于将 IO 重定向到子进程您可以创建 2 个管道与子进程通信:

iResult = CreatePipe(&hReadPipe1, &hWritePipe1, &saAttr, DEFAULT_BUFLEN);
if (iResult == 0) {
    printf("Pipe Error");
}
iResult = CreatePipe(&hReadPipe2, &hWritePipe2, &saAttr, DEFAULT_BUFLEN);
if (iResult == 0) {
    printf("Pipe Error");
} 

Read from child process( cmd.exe ), and send to client.从子进程( cmd.exe )读取,并发送到客户端。

Or,或者,

Just use WSASocket instead of socket , and do NOT specify the WSA_FLAG_OVERLAPPED .(recommended)只需使用WSASocket而不是socket ,并且不要指定WSA_FLAG_OVERLAPPED 。(推荐)

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

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