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