[转载]socket下server端支持多客户端并发访问简单实现
/*Author: wainiwann
*Source: 博客园 http://www.cnblogs.com/wainiwann
*Remarks: 转载请说明出处!!!
*/
感觉很不错,可以学习一下。
socket下server端支持多客户端并发访问简单实现
server端开启之后始终有两个线程在处理连接请求,一个是只负责客户端的请求连接的(这里是只针对TCP协议),当客户端connect的时候 记录当前客户端连接存放到数据组中当中,而这个数组声明为全局成员,其实在线程内处理外部成员的话,也没必要非要用静态或者全局成员,今天听经理说也可以 在创建该线程时,把某类的this指针传递过去,同样好像也可以访问public成员的,具体行不行,还没试不过真的是不错的方法。要知道很多在项目很避 讳使用全局的东西,甚至有的公司直接不让使用全局的东西。这里扯的有点远了。
另外一个同步允许的线程就是对accept记录的数组进行操作,依次处理各个客户端请求通信和状态监控等,当数组内socket成员不为空时记录当前索引然后在create发送或者获取线程处理函数并发响应多个客户端的连接请求通信
加载套接字库:
BOOL TcpServer::InitSocket() { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 1 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { return FALSE; } if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) { WSACleanup( ); return FALSE; } //创建套接字 //SOCKET m_socket=socket(AF_INET,SOCK_STREAM,0); return TRUE; }
开启执行Accept线程处理函数:
BOOL TcpServer::SatartServer() { //创建线程 HANDLE hThread = CreateThread(NULL,0,ThreadProc_Accept,NULL,0,NULL); //关闭该接收线程句柄,释放引用计数 CloseHandle(hThread); return TRUE; }
线程处理函数:
DWORD WINAPI TcpServer::ThreadProc_Accept(LPVOID lpParameter) { int len = sizeof(SOCKADDR); int err; m_socket=socket(AF_INET,SOCK_STREAM,0); if (m_socket == INVALID_SOCKET) { AfxMessageBox(_T("套接字创建失败!")); return FALSE; } SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY); addrSrv.sin_family=AF_INET; addrSrv.sin_port=htons(8099); err = bind(m_socket,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); //绑定本地端口 if (err==SOCKET_ERROR) { closesocket(m_socket); AfxMessageBox(_T("绑定失败!")); return FALSE; } listen(m_socket,10);//开启监听 //创建线程 HANDLE hThread = CreateThread(NULL,0,ThreadProc_Select,NULL,0,NULL); //关闭该接收线程句柄,释放引用计数 CloseHandle(hThread); while (TRUE) { m_CliSocketArr[m_ToolConn++] = accept(m_socket,(SOCKADDR*)&addrSrv,&len); } return 0; }
同时在该线程函数内创建处理客户端数组的线程处理函数:
DWORD WINAPI TcpServer::ThreadProc_Select(LPVOID lpParameter) { int recvflag=0; fd_set fdread; //读集fdread int ret; //查看某个套接字的状态 struct timeval tv = {1,0}; //实例化timeval变量 while (TRUE) { //判断当前连接数是否为 0 if (m_ToolConn == 0) { Sleep(50); continue; } FD_ZERO(&fdread); for (int i = 0;i < m_ToolConn;i++) { FD_SET(m_CliSocketArr[i],&fdread); } ret = select(0,&fdread,NULL,NULL,&tv); if (ret == 0) { continue; } for (int i =0;i<m_ToolConn;i++) { if (FD_ISSET(m_CliSocketArr[i],&fdread)) { ret = recv(m_CliSocketArr[i],(char*)&recvflag,sizeof(int)+1,0); if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)) { closesocket(m_CliSocketArr[i]); if (i < m_ToolConn-1) { m_CliSocketArr[i] = m_CliSocketArr[--m_ToolConn]; }else { --m_ToolConn; } }else { INDEX * inx = new INDEX; inx->flag = recvflag; inx->index = i; //创建线程 HANDLE hThread = CreateThread(NULL,0,ThreadProc_Response,(LPVOID)inx,0,NULL); //关闭该接收线程句柄,释放引用计数 CloseHandle(hThread); } }//if }//for }//while return 0; }
下面就是一次创建线程并发处理客户端请求线程处理函数:
DWORD WINAPI TcpServer::ThreadProc_Response(LPVOID lpParameter) { int ix = ((INDEX*)lpParameter)->index; int flag = ((INDEX*)lpParameter)->flag; delete lpParameter; if (flag == 1) { //............................. unsigned char sendBuffer[1024] = {'a'}; send(m_CliSocketArr[ix],(char*)sendBuffer,sizeof(sendBuffer)+1,0); } return 0; }
线程处理函数在定义时,要设置为static或者是全局函数。