如果服务器支持线程,那么对于客户/服务进程的架构我们可以采取一个线程处理一个客户连接的设计方案。也就是每当有新的连接请求到达服务器时,服务器会新开一个子线程来专门处理这个连接的信息传递;这种方法类似于服务器为每个客户连接fork一个子进程,但这相对来说更轻量级。
开始之前在头文件中声明一下变量和函数
static DWORD WINAPI WaitProc(LPVOID lpPar);
static DWORD WINAPI RespondProc(LPVOID lpPar);
HANDLE m_hWaitThread; /* 等待连接线程 */
HANDLE m_hRespondThread; /* 通信线程句柄 */
首先,初始化服务器socket
BOOL CTCPServerDlg::InitSocket()
{
WSADATA wsaData;
WORD sockVersion=MAKEWORD(2,2);
if(WSAStartup(sockVersion,&wsaData))
{
AfxMessageBox(_T("failed to load winsock!"),MB_OK|MB_ICONSTOP);
return 0;
}
listensocketfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
saServer.sin_family=AF_INET;
saServer.sin_addr.S_un.S_addr=inet_addr(m_servip);
saServer.sin_port=htons(m_servport);
bind(listensocketfd,(sockaddr*)&saServer,sizeof(saServer));
listen(listensocketfd,50);
return TRUE;
}
然后,在主线程中启动一个等待连接的线程
BOOL CTCPServerDlg::StartService()
{
/* 启动等待连接线程 */
DWORD dwThreadId = 0;
m_hWaitThread = ::CreateThread(NULL, NULL, CTCPServerDlg::WaitProc, ((LPVOID)this), 0, &dwThreadId);
CloseHandle(m_hWaitThread);
return TRUE;
}
下面是WaitProc线程函数的实现
DWORD WINAPI CTCPServerDlg::WaitProc(LPVOID lpPar)
{
sockaddr_in remoteAddr;
int nAddrLen = sizeof(remoteAddr);
CTCPServerDlg *pThis = (CTCPServerDlg*)lpPar;
CString strNotice; /* 通知消息 */
while(1)
{
Sleep(10);
/* 创建通信套接字 */
SOCKET *pClientSocket = new SOCKET;
*pClientSocket = ::accept(pThis->listensocketfd, (SOCKADDR*)&remoteAddr, &nAddrLen);
if(INVALID_SOCKET == *pClientSocket)
{
strNotice = "accept()失败,再次尝试 ...... ";
::AfxMessageBox(strNotice);
continue;
}
DWORD dwThreadId = 1;
/* 启动相应线程与客户端通信 */
pThis->m_hRespondThread = ::CreateThread(NULL, NULL, CTCPServerDlg::RespondProc, ((LPVOID)pClientSocket), 0, &dwThreadId);
CloseHandle(pThis->m_hRespondThread);
}
return 0;
}
其中,为每一个请求连接的客户端都开辟了一个新的线程来处理客户端和服务器的通信
DWORD WINAPI CTCPServerDlg::RespondProc(LPVOID lpPar)
{
SOCKET *pClientSocket = (SOCKET*)lpPar;
char buff[4096]; /* 接收数据缓冲区 */
struct sockaddr_in sa;
int len = sizeof(sa);
getpeername(*pClientSocket, (struct sockaddr *)&sa, &len);
while(1)
{
Sleep(10);
int nRecv = ::recv(*pClientSocket, buff, 4096, 0);
if(nRecv > 0)
{
buff[nRecv] = '\0';
}
//对接收的数据进行处理
.......
}
}
这样便实现了服务器端的多线程模型