2011-02-14 55 views
3

我是winsock的新手,我嘗試編寫一個接受新連接的服務器套接字,然後調用外部可執行文件。我們如何將外部可執行文件的stdin和stdout重定向到已被接受的客戶端套接字。我用google搜索了下面的代碼,但它不起作用。新進程已成功創建,但客戶端無法從新進程接收任何數據。我正在使用Windows 7和Visual Studio 2008 Express版本。任何幫助和意見表示讚賞。非常感謝!將進程的IO重定向到Windows套接字

服務器

#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(); 

} 

客戶

#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; 
} 

外部程序HELLO.CPP

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    printf("hello world!\n");  
    return 0; 
} 

回答

2

Windows將幾乎所有的HANDLE。套接字不是內核對象,是一個例外,它們不能用於重定向。您將需要使用管道,並且如果需要將數據發送到套接字或從套接字發送數據,則需要輔助進程來在管道和套接字之間複製數據。

看看netcat win32版本的源代碼(如果你能找到它的話),它的確可以完成套接字<-> stdin和stdout轉發。

3

套接字不能直接用於重定向。啓動外部進程時,使用CreatePipe()爲進程的重定向STDIN/OUT/ERR句柄創建匿名管道,然後使用ReadFile()WriteFile()(或equivilents)與send()recv()手動代理套接字和進程之間的數據通過管道。換句話說,當數據到達套接字時,從套接字讀取數據並將其寫入STDIN管道。當進程輸出數據時,從STDOUT/ERR管道讀取並將其寫入套接字。

+0

我的回答很不清楚,需要轉述嗎? – 2011-02-15 01:24:26

+0

我只是在說明涉及的Win32 API函數,因此dien不必去尋找它們,就像你的答案所建議的那樣。 – 2011-02-15 19:04:35

10

其實,你可以將IO重定向到套接字。只要確保使用WSASocket而不是socket()打開套接字,並且不指定WSA_FLAG_OVERLAPPED。

這是有點涉及的原因。您提供給CreateProcess進行I/O重定向的任何「標準句柄」必須不重疊(即不支持重疊的I/O)。 Windows上的套接字在使用套接字()創建時打開,如果使用WSASocket創建,則不重疊。

另外,還要確保你在STARTUPINFO

0

這不是正確的說,套接字句柄不能用於重定向IO子進程設置bInheritHandles爲TRUE。我們一直在我們的Web服務器代碼中使用CGI。我們不使用已命名或未命名的管道或中間進程。

a_mole是正確的。你只需要不重疊的套接字。

您可以使用WSASocket,或者如果你有套接字創建的套接字已經(或你的目標操作系統不能使用WSASocket,如預Windows 7的SP1),您可以使用setsockopt設置當前線程的SO_OPENTYPE到SO_SYNCHRONOUS_NONALERT