socket编程,简单多线程服务端测试程序
socket编程,简单多线程服务端测试程序
前些天重温了MSDN关于socket编程的WSAStartup、WSACleanup、socket、closesocket、bind、listen、accept、recv、send等函数的介绍,今天写了一个CUI界面的测试程序(依赖MFC)作为补充。程序功能简介如下:
1:一个线程做监听用。
2:监听线程收到客户端连接后,创建新线程接收客户端数据。所有对客户端线程将加入容器,以便管理。
3:服务端打印所有客户端发来的信息。
4:服务端CUI界面输入数字0,将关闭所有连接线程,释放socket,并退出程序。
程序实现依赖类:
1:MFC
2:CSingleton模板,我关于singleton实现的文章中有源码。http://www.cnblogs.com/hgwang/p/6085922.html
3:CThreadLockCs,CRITICAL_SECTION封装类,我关于singleton实现的文章中有源码。http://www.cnblogs.com/hgwang/p/6085922.html。
4:windows socket库,我关于windows socket的文章中有介绍及调用方法,http://www.cnblogs.com/hgwang/p/6074038.html。
下面是该测试程序服务端封装类的源码:
Listen.h头文件
1 #pragma once 2 3 #ifndef WHG_LISTEN 4 #define WHG_LISTEN 5 6 #include "Singleton.h" 7 8 class CListen 9 { 10 public: 11 CListen(void); 12 ~CListen(void); 13 //类入口点,会创建监听线程 14 void SetAddress(unsigned short port,std::string ip=""); 15 //用于线程访问关闭socket,并为CListen记录关闭信息 16 void DeleteLink(SOCKET s); 17 private: 18 //线程和CListen类共享的任务信息 19 struct ThreadSocketInfo 20 { 21 CWinThread* pWt; 22 SOCKET* pS; 23 CListen* pListen; 24 ThreadSocketInfo() 25 { 26 pListen = NULL; 27 pWt = NULL; 28 pS = NULL; 29 } 30 ~ThreadSocketInfo() 31 { 32 pListen = NULL; 33 pWt = NULL; 34 pS = NULL; 35 } 36 }; 37 //监听socket 38 SOCKET listensocket; 39 //监听线程指针 40 CWinThread* m_pListenThread; 41 //任务信息列表 42 std::vector<ThreadSocketInfo> vec_WorkThreads; 43 //当前监听ip和端口 44 unsigned short m_port; 45 std::string m_ip; 46 //线程互斥访问锁 47 CThreadLockCs m_tlcs; 48 49 private: 50 //监听线程工作对象 51 static UINT AFX_CDECL ListenThread(LPVOID); 52 //对客户端线程工作对象 53 static UINT AFX_CDECL WorkThread(LPVOID); 54 //bind、listen、accept实现 55 void Address(unsigned short port,std::string ip=""); 56 }; 57 58 //申明singleton的监听线程访问对象,全局唯一实例 59 typedef CSingleton<CListen> LISTEN; 60 61 #endif
Listen.cpp:
1 #include "StdAfx.h" 2 #include "Listen.h" 3 4 CListen::CListen(void) 5 :m_pListenThread(NULL) 6 { 7 listensocket = 0; 8 WSAData wsa; 9 if (WSAStartup(MAKEWORD(1,1),&wsa) != 0) 10 { 11 cout<<"WSAStartup "<<endl; 12 WSACleanup(); 13 } 14 else 15 { 16 listensocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 17 if (listensocket == INVALID_SOCKET) 18 { 19 cout<<"socket() error,error code "<<WSAGetLastError()<<endl; 20 } 21 } 22 } 23 24 CListen::~CListen(void) 25 { 26 //要先关闭对客户端连接,再关闭监听socket 27 if (vec_WorkThreads.size() > 0) 28 { 29 std::vector<ThreadSocketInfo>::iterator iter = vec_WorkThreads.begin(); 30 for (;iter!=vec_WorkThreads.end();iter++) 31 { 32 SOCKET* s = (*iter).pS; 33 if (s) 34 { 35 closesocket(*s); 36 delete s; 37 (*iter).pS = NULL; 38 } 39 CWinThread* pwt = (*iter).pWt; 40 if (pwt) 41 { 42 WaitForSingleObject(pwt->m_hThread,INFINITE); 43 delete pwt; 44 (*iter).pWt = NULL; 45 } 46 } 47 } 48 if (listensocket) 49 { 50 closesocket(listensocket); 51 } 52 WSACleanup(); 53 cout<<"WSACleanup "<<endl; 54 55 if (m_pListenThread) 56 { 57 WaitForSingleObject(m_pListenThread->m_hThread,INFINITE); 58 delete m_pListenThread; 59 } 60 vec_WorkThreads.clear(); 61 } 62 63 //监听线程工作对象 64 UINT CListen::ListenThread(LPVOID param) 65 { 66 CListen* p = (CListen*)param; 67 if (p) 68 { 69 p->Address(p->m_port,p->m_ip); 70 } 71 return 0; 72 } 73 74 //对客户端线程工作对象 75 UINT CListen::WorkThread(LPVOID param) 76 { 77 ThreadSocketInfo* tsi = (ThreadSocketInfo*)param; 78 SOCKET acp = *(tsi->pS); 79 CListen* pListen = tsi->pListen; 80 delete tsi; 81 82 do 83 { 84 char buf[1024]; 85 int len = recv(acp,buf,1024,0); 86 if (len == 0) 87 { 88 cout<<"connection has been closed "<<endl; 89 break; 90 } 91 else if (len == SOCKET_ERROR) 92 { 93 cout<<"recv error,error code "<<WSAGetLastError()<<endl; 94 break; 95 } 96 else 97 { 98 char* outbuf = new char[len+1]; 99 memcpy(outbuf,buf,len); 100 outbuf[len] = 0; 101 cout<<"recv data,"<<outbuf<<endl; 102 delete outbuf; 103 } 104 } while (1); 105 //删除当前连接记录 106 pListen->DeleteLink(acp); 107 return 0; 108 } 109 110 //用于线程访问关闭socket,并为CListen记录关闭信息 111 void CListen::DeleteLink(SOCKET s) 112 { 113 m_tlcs.lock(); 114 std::vector<ThreadSocketInfo>::iterator iter = vec_WorkThreads.begin(); 115 for (;iter!=vec_WorkThreads.end();iter++) 116 { 117 SOCKET* ss = (*iter).pS; 118 if (ss && *ss == s) 119 { 120 closesocket(s); 121 delete ss; 122 iter->pS = NULL; 123 } 124 } 125 m_tlcs.unlock(); 126 } 127 128 //类入口点,会创建监听线程 129 void CListen::SetAddress(unsigned short port,std::string ip) 130 { 131 //监听线程只允许启动一次 132 if (m_pListenThread == NULL) 133 { 134 m_ip = ip; 135 m_port = port; 136 m_pListenThread = AfxBeginThread(ListenThread,this,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED,NULL); 137 if (m_pListenThread) 138 { 139 m_pListenThread->m_bAutoDelete = FALSE ; 140 m_pListenThread->ResumeThread(); 141 } 142 } 143 } 144 145 //bind、listen、accept实现 146 void CListen::Address(unsigned short port,std::string ip) 147 { 148 sockaddr_in service; 149 service.sin_family = AF_INET; 150 service.sin_port = htons(port); 151 if (ip.empty()) 152 { 153 service.sin_addr.s_addr = INADDR_ANY; 154 } 155 else 156 { 157 service.sin_addr.s_addr = inet_addr(ip.c_str()); 158 } 159 160 if (bind(listensocket,(sockaddr*)&service,sizeof(service)) == SOCKET_ERROR) 161 { 162 cout<<"bind() error,error code "<<WSAGetLastError()<<endl; 163 return; 164 } 165 cout<<"bind "<<endl; 166 167 if (listen(listensocket,SOMAXCONN) == SOCKET_ERROR) 168 { 169 cout<<"listen() error,error code "<<WSAGetLastError()<<endl; 170 return; 171 } 172 cout<<"listen "<<endl; 173 174 while (1) 175 { 176 sockaddr_in recvLinkAddr; 177 int recvAddr = sizeof(recvLinkAddr); 178 SOCKET acp = accept(listensocket,(sockaddr*)&recvLinkAddr,&recvAddr); 179 if (acp == INVALID_SOCKET) 180 { 181 cout<<"accept error,error code "<<WSAGetLastError()<<endl; 182 return; 183 } 184 cout<<"获取新的连接请求,ip:"<<inet_ntoa(recvLinkAddr.sin_addr)<<",port:"<<recvLinkAddr.sin_port<<endl; 185 186 SOCKET* s = new SOCKET(acp); 187 ThreadSocketInfo* tsi = new ThreadSocketInfo; 188 tsi->pListen = this; 189 tsi->pS = s; 190 CWinThread* workthread = AfxBeginThread(WorkThread,tsi,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED,NULL); 191 if (workthread) 192 { 193 workthread->m_bAutoDelete = FALSE; 194 workthread->ResumeThread(); 195 tsi->pWt = workthread; 196 ThreadSocketInfo t = *tsi; 197 m_tlcs.lock(); 198 vec_WorkThreads.push_back(t); 199 m_tlcs.unlock(); 200 } 201 } 202 }
客户端代码:
1 // WinsockClient.cpp : Defines the entry point for the console application. 2 // 3 4 #include "stdafx.h" 5 int _tmain(int argc, _TCHAR* argv[]) 6 { 7 cout<<"input id:"; 8 std::string str; 9 cin>>str; 10 11 WSAData wsa; 12 if (WSAStartup(MAKEWORD(1,1),&wsa) != 0) 13 { 14 WSACleanup(); 15 return 0; 16 } 17 SOCKET cnetsocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 18 do 19 { 20 if (cnetsocket == INVALID_SOCKET) 21 break; 22 sockaddr_in server; 23 server.sin_family = AF_INET; 24 server.sin_port = htons(8828); 25 server.sin_addr.s_addr = inet_addr("127.0.0.1"); 26 if (connect(cnetsocket,(sockaddr*)&server,sizeof(server)) == SOCKET_ERROR) 27 { 28 break; 29 } 30 str += " : windows socket test!"; 31 while (1) 32 { 33 int len = send(cnetsocket,str.c_str(),str.length(),0); 34 cout<<"send data:"<<str.c_str()<<" ,length = "<<str.length()<<endl; 35 if (len < str.length()) 36 { 37 cout<<"data send uncompleted"<<endl; 38 str = str.substr(len+1,str.length()); 39 len = send(cnetsocket,str.c_str(),str.length(),0); 40 cout<<"send data uncomplete,send remaining data :"<<str.c_str()<<" ,length = "<<str.length()<<endl; 41 } 42 else if (len == SOCKET_ERROR) 43 { 44 break; 45 } 46 Sleep(5000); 47 } 48 } while (0); 49 closesocket(cnetsocket); 50 WSACleanup(); 51 52 return 1; 53 }
main函数:
1 // MultithreadServer.cpp : Defines the entry point for the console application. 2 // 3 4 #include "stdafx.h" 5 #include "MultithreadServer.h" 6 7 #include "Listen.h" 8 #include "Singleton.h" 9 #ifdef _DEBUG 10 #define new DEBUG_NEW 11 #endif 12 // The one and only application object 13 CWinApp theApp; 14 using namespace std; 15 16 int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) 17 { 18 int nRetCode = 0; 19 20 // initialize MFC and print and error on failure 21 if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) 22 { 23 // TODO: change error code to suit your needs 24 _tprintf(_T("Fatal Error: MFC initialization failed\n")); 25 nRetCode = 1; 26 } 27 LISTEN::Instance()->SetAddress(8828,"127.0.0.1"); 28 29 int outId; 30 cin>>outId; 31 if (outId == 0) 32 { 33 LISTEN::Close(); 34 } 35 return nRetCode; 36 }
测试结果:
1:4个客户端连接
2:客户端4关闭连接
3:输入0,关闭整个服务端,自动断开1.2.3的客户端
这里面涉及到几个错误代码,中文说明如下:
1:10054,远程主机强迫关闭了一个现有的连接。
2:10053,你的主机中的软件中止了一个已建立的连接。
3: 10004,一个封锁操作被对 WSACancelBlockingCall 的调用中断。
至此,程序正常结束。