将进程的 IO 重定向到 Windows 套接字
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4993119/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Redirect IO of process to Windows socket
提问by Dien Nguyen
I am new to winsock, I tried to write a server socket that accepts new connection, then it calls an external executable file. How can we redirect the stdin and stdout of the external executable file to the client socket which has been accepted. I googled and found the code below but it does not work. The new process was created successfully but the client could not receive any data from the new process. I am using Windows 7 and Visual Studio 2008 Express edition. Any helps and comments are appreciated. Thank a lot!
我是 winsock 的新手,我尝试编写一个接受新连接的服务器套接字,然后它调用一个外部可执行文件。我们如何将外部可执行文件的 stdin 和 stdout 重定向到已接受的客户端套接字。我用谷歌搜索并找到了下面的代码,但它不起作用。新进程已成功创建,但客户端无法从新进程接收任何数据。我使用的是 Windows 7 和 Visual Studio 2008 Express 版。任何帮助和评论表示赞赏。非常感谢!
the server
服务器
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_PORT "27015"
#define DEFAULT_BUFLEN 512
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
int iResult;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
struct addrinfo *result = NULL, *ptr = NULL, hints;
ZeroMemory(&hints, sizeof (hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Resolve the local address and port to be used by the server
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
SOCKET ListenSocket = INVALID_SOCKET;
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
// Setup the TCP listening socket
iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);
freeaddrinfo(result);
if ( listen( ListenSocket, SOMAXCONN ) == SOCKET_ERROR ) {
printf( "Listen failed with error: %ld\n", WSAGetLastError() );
closesocket(ListenSocket);
WSACleanup();
return 1;
}
SOCKET ClientSocket;
ClientSocket = INVALID_SOCKET;
// Accept a client socket
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
STARTUPINFO si;
memset( &si, 0, sizeof( si ) );
si.cb = sizeof( si );
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdInput = (HANDLE)ClientSocket;
si.hStdOutput = (HANDLE)ClientSocket;
si.hStdError = (HANDLE)ClientSocket;
PROCESS_INFORMATION pi;
TCHAR cmd[] = TEXT("C:\Users\dell\Desktop\hello.exe");
if (CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
printf("create process successfully\n");
DWORD i = WaitForSingleObject( pi.hProcess, INFINITE );
printf("%8x\n", i);
}
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
closesocket( ClientSocket );
WSACleanup();
}
the client
客户端
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
char recvbuf[DEFAULT_BUFLEN];
int iResult;
int recvbuflen = DEFAULT_BUFLEN;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
ZeroMemory( &hints, sizeof(hints) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Resolve the server address and port
iResult = getaddrinfo("localhost", DEFAULT_PORT, &hints, &result);
// Attempt to connect to an address until one succeeds
for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) {
// Create a SOCKET for connecting to server
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
// Connect to server.
iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
continue;
}
break;
}
freeaddrinfo(result);
if (ConnectSocket == INVALID_SOCKET) {
printf("Unable to connect to server!\n");
WSACleanup();
return 1;
}
// Receive until the peer closes the connection
do {
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if ( iResult > 0 )
printf("Bytes received: %d\n", iResult);
else if ( iResult == 0 )
printf("Connection closed\n");
else
printf("recv failed with error: %d\n", WSAGetLastError());
} while( iResult > 0 );
// cleanup
closesocket(ConnectSocket);
WSACleanup();
return 0;
}
external program hello.cpp
外部程序hello.cpp
int _tmain(int argc, _TCHAR* argv[])
{
printf("hello world!\n");
return 0;
}
采纳答案by Ben Voigt
Windows treats almost everything as a HANDLE
. Sockets, which aren't kernel objects, are an exception, and they can't be used for redirection. You'll need to use a pipe, and if you need to send data to/from a socket, you'll need a helper process to copy data between the pipe and socket.
Windows 将几乎所有内容都视为HANDLE
. 不是内核对象的套接字是一个例外,它们不能用于重定向。您将需要使用管道,并且如果您需要向/从套接字发送数据,您将需要一个辅助进程来在管道和套接字之间复制数据。
Have a look at netcat
source code for the win32 version (if you can find it), it does pretty much exactly socket <-> stdin and stdout forwarding.
看看netcat
win32 版本的源代码(如果你能找到的话),它几乎完全正确地执行 socket <-> stdin 和 stdout 转发。
回答by a_mole
Actually, you can redirect IO to sockets. Just make sure you open the socket with WSASocket instead of socket(), and do NOT specify the WSA_FLAG_OVERLAPPED.
实际上,您可以将 IO 重定向到套接字。只要确保使用 WSASocket 而不是 socket() 打开套接字,并且不要指定 WSA_FLAG_OVERLAPPED。
The reason for this is a bit involved. Any "Standard Handles" that you supply to CreateProcess for I/O redirection must be non-overlapped (i.e, do not support overlapped I/O). sockets on Windows are opened overlapped when created with socket() and non-overlapped if created as above with WSASocket.
这样做的原因有点牵强。您为 I/O 重定向提供给 CreateProcess 的任何“标准句柄”必须是非重叠的(即,不支持重叠 I/O)。Windows 上的套接字在使用 socket() 创建时重叠打开,如果使用 WSASocket 创建如上,则不重叠。
Also make sure you set bInheritHandles to TRUE in the StartupInfo
还要确保在 StartupInfo 中将 bInheritHandles 设置为 TRUE
回答by Remy Lebeau
Sockets cannot be used directly for redirection. When launching the external process, use CreatePipe()
to create anonymous pipes for the redirected STDIN/OUT/ERR handles of the process, then use ReadFile()
and WriteFile()
(or equivilents) with send()
and recv()
to manually proxy the data between the socket and the process yourself via the pipes. In other words, when data arrives to the socket, read the data from the socket and write it to the STDIN pipe. When the process outputs data, read from the STDOUT/ERR pipes and write it to the socket.
套接字不能直接用于重定向。启动外部进程时,使用CreatePipe()
为进程重定向的 STDIN/OUT/ERR 句柄创建匿名管道,然后使用ReadFile()
and WriteFile()
(或等效项)和send()
和recv()
通过管道手动代理套接字和进程之间的数据。换句话说,当数据到达套接字时,从套接字读取数据并将其写入 STDIN 管道。当进程输出数据时,从 STDOUT/ERR 管道中读取并将其写入套接字。
回答by Adrien
It's not correct to say that socket handles cannot be used for redirected IO to child processes. We do it all the time for CGI in our web server code. We don't use named or unnamed pipes, or intermediary processes.
说套接字句柄不能用于将 IO 重定向到子进程是不正确的。我们一直在我们的 Web 服务器代码中为 CGI 做这件事。我们不使用命名或未命名管道,或中间进程。
a_mole is correct. You just need non-overlapped sockets.
a_mole 是正确的。您只需要非重叠的套接字。
You can either use WSASocket, or if you created the socket already with socket (or can't use WSASocket on your target OS, e.g. Pre Windows 7 SP1) you can use setsockopt to set the current thread's SO_OPENTYPE to SO_SYNCHRONOUS_NONALERT
您可以使用 WSASocket,或者如果您已经使用套接字创建了套接字(或者不能在您的目标操作系统上使用 WSASocket,例如 Windows 7 SP1 之前),您可以使用 setsockopt 将当前线程的 SO_OPENTYPE 设置为 SO_SYNCHRONOUS_NONALERT