• 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 的调用中断。

    至此,程序正常结束。 

  • 相关阅读:
    js中for..of..和迭代器
    Python与其他语言的区别
    数据结构和算法部分总结
    MVC设计模式及SSH框架的介绍
    块级元素和行内元素以及display中block、inline和inline-block的区别
    线程安全和非线程安全
    Spring MVC拦截器入门
    Java反射的理解
    mybatis
    重定向和转发
  • 原文地址:https://www.cnblogs.com/hgwang/p/6086237.html
Copyright © 2020-2023  润新知