这里的Echo服务器就是说客户端发什么,服务器端立刻返回什么。一种常见的实现是基于多线程的,在linux上还很容易就用fork实现一个多进程的服务器。
下面演示一下在Windows平台如何用多进程来实现一个echo服务器。
/*
* @file : TestEchoServerMultiProcess.cpp
* @author: Shilyx
* @date : 2014-04-23 08:43:27.206
* @note : Generated by SlxTemplates, 多进程echo服务器演示
*/
#include <WinSock2.h>
#include <Windows.h>
#include <Shlwapi.h>
#pragma warning(disable: 4786)
#include <iostream>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Shlwapi.lib")
using namespace std;
// 初始化WinSock,未检查返回值
void InitWinSock()
{
WSADATA wd;
WSAStartup(MAKEWORD(2, 2), &wd);
}
void Serve(USHORT port)
{
InitWinSock();
SOCKET sock_base = INVALID_SOCKET;
do
{
sock_base = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock_base == INVALID_SOCKET)
{
cerr<<"socket error "<<WSAGetLastError()<<endl;
break;
}
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(port);
if (SOCKET_ERROR == bind(sock_base, (sockaddr *)&sin, sizeof(sin)))
{
cerr<<"bind error "<<WSAGetLastError()<<endl;
break;
}
if (SOCKET_ERROR == listen(sock_base, 100))
{
cerr<<"listen error "<<WSAGetLastError()<<endl;
break;
}
HANDLE hProcess = NULL;
DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &hProcess, 0, TRUE, DUPLICATE_SAME_ACCESS);
if (NULL == hProcess)
{
cerr<<"DuplicateHandle error "<<GetLastError()<<endl;
break;
}
TCHAR szSelfPath[MAX_PATH];
GetModuleFileName(GetModuleHandle(NULL), szSelfPath, RTL_NUMBER_OF(szSelfPath));
PathQuoteSpaces(szSelfPath);
while (true)
{
int len = sizeof(sin);
SOCKET sock = accept(sock_base, (sockaddr *)&sin, &len);
if (sock == INVALID_SOCKET)
{
cerr<<"accept error "<<WSAGetLastError()<<endl;
break;
}
else
{
TCHAR szCommand[MAX_PATH * 2];
STARTUPINFO si = {sizeof(si)};
PROCESS_INFORMATION pi;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
wnsprintf(szCommand, RTL_NUMBER_OF(szCommand), TEXT("%s %u %u"), szSelfPath, sock, hProcess);
if (CreateProcess(NULL, szCommand, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
else
{
cerr<<"CreateProcess error "<<GetLastError()<<endl;
}
closesocket(sock);
}
}
} while (false);
if (sock_base != INVALID_SOCKET)
{
closesocket(sock_base);
}
}
DWORD CALLBACK WorkProc(LPVOID lpParam)
{
SOCKET sock = (SOCKET)lpParam;
while (TRUE)
{
char szBuffer[4096];
int len = recv(sock, szBuffer, sizeof(szBuffer), 0);
if (len <= 0)
{
break;
}
if (send(sock, szBuffer, len, 0) <= 0)
{
break;
}
}
closesocket(sock);
return 0;
}
void Work(SOCKET sock, HANDLE hParentProcess)
{
InitWinSock();
HANDLE hObjects[] = {hParentProcess, CreateThread(NULL, 0, WorkProc, (LPVOID)sock, 0, NULL)};
WaitForMultipleObjects(RTL_NUMBER_OF(hObjects), hObjects, FALSE, INFINITE);
CloseHandle(hObjects[0]);
CloseHandle(hObjects[1]);
}
int main(int argc, char *argv[])
{
// 加端口参数启动为父进程
// 加套接字句柄参数和进程句柄参数为子进程
// 不加参数显示用法
if (argc == 2)
{
int port = StrToIntA(argv[1]);
if (port < 0 || port > 65535)
{
cerr<<"端口错误:"<<port<<endl;
return 0;
}
// 在端口port处启动echo服务器
Serve((USHORT)port);
}
else if (argc == 3)
{
SOCKET sock = StrToIntA(argv[1]);
HANDLE hParentProcess = (HANDLE)StrToIntA(argv[2]);
// 针对具体tcp连接套接字和父进程句柄开始echo工作
Work(sock, hParentProcess);
}
else
{
cout<<"加端口参数启动为父进程"<<endl
<<"加套接字句柄参数和进程句柄参数为子进程"<<endl
<<"不加参数显示用法"<<endl;
}
return 0;
}