zoukankan      html  css  js  c++  java
  • 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 的调用中断。

    至此,程序正常结束。 

  • 相关阅读:
    [转发]深入理解git,从研究git目录开始
    iOS系统网络抓包方法
    charles抓包工具
    iOS多线程中performSelector: 和dispatch_time的不同
    IOS Core Animation Advanced Techniques的学习笔记(五)
    IOS Core Animation Advanced Techniques的学习笔记(四)
    IOS Core Animation Advanced Techniques的学习笔记(三)
    IOS Core Animation Advanced Techniques的学习笔记(二)
    IOS Core Animation Advanced Techniques的学习笔记(一)
    VirtualBox复制CentOS后提示Device eth0 does not seem to be present的解决方法
  • 原文地址:https://www.cnblogs.com/hgwang/p/6086237.html
Copyright © 2011-2022 走看看