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
"));
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 的调用中断。

至此,程序正常结束。 

原文地址:https://www.cnblogs.com/hgwang/p/6086237.html