zoukankan      html  css  js  c++  java
  • socket模型处理多个客户端

    最近学完了简单的socket编程,发现其实socket的网络编程其实并没有什么难度,只是简单的函数调用,记住客户端与服务端的步骤,写起来基本没有什么问题。
    在服务器程序的设计中,一个服务器不可能只相应一个客户端的链接,为了响应多个客户端的链接,需要使用多线程的方式,每当有一个客户端连接进来,我们就开辟一个线程,用来处理双方的交互(主要是利用recv或者recvfrom用于收发信息),由于但是在网络中可能出现这样一种情况:由于处理比较复杂,下一条信息到来之后,上一条信息的处理还没有完成,这样信息太多了之后系统的缓冲占满之后可能会发生丢包的现象,所以为了解决这个问题,需要另外再开一个线程,专门用来处理接收到的数据,这样总共至少有3个线程,主线程,收发信息的线程,处理线程;这样可能也不完整,处理的操作种类多了的话可能需要根据不同的请求来开辟不同的线程用来处理这一类请求,下面是实现这一思路的部分代码:
    全局变量:

    DWORD WINAPI AcceptThread(LPVOID lpParameter);
    DWORD WINAPI RecvThread(LPVOID lpParameter);
    
    DWORD g_nAcceptID = 123;
    DWORD g_nRecvID = 234;
    
    HANDLE g_hAccpetThread;
    HANDLE g_hRecvThread;

    主线程函数:

    int _tmain(int argc, _TCHAR* argv[])
    {
        WSADATA wd;
        WSAStartup(MAKEWORD(2, 2), &wd);
    
        SOCKET sockListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
        if (INVALID_SOCKET == sockListen)
        {
            cout << "创建侦听套接字失败,错误码为:" << WSAGetLastError() << endl;
            return -1;
        }
    
        SOCKADDR_IN srvAddr = { 0 };
        srvAddr.sin_family = AF_INET;
        srvAddr.sin_port = htons(6666);
        srvAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
        if (SOCKET_ERROR == bind(sockListen, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR)))
        {
            cout << "绑定失败,错误码为:" << WSAGetLastError() << endl;
            WSACleanup();
            return -1;
        }
    
        if (SOCKET_ERROR == listen(sockListen, 5))
        {
            cout << "侦听失败,错误码为:" << WSAGetLastError() << endl;
            WSACleanup();
            return -1;
        }
    
        while (true)
        {
            SOCKET sockConn = accept(sockListen, NULL, 0);
            if (INVALID_SOCKET == sockConn)
            {
                cout << "本次连接失败,即将进入下一次连接,错误码为:" << WSAGetLastError() << endl;
                closesocket(sockConn);
                closesocket(sockListen);
                WSACleanup();
                continue;
            }
    
            g_hAccpetThread = CreateThread(NULL, 0, AcceptThread, &sockConn, 0, &g_nAcceptID);
        }
    
        WaitForSingleObject(g_hAccpetThread, INFINITE);
        WSACleanup();
        return 0;
    }

    收发数据函数:

    DWORD WINAPI AcceptThread(LPVOID lpParameter)
    {
        cout << "有客户端连接进来" << endl;
        SOCKET sockConn = *(SOCKET*)lpParameter;
        while (true)
        {
            char *pszBuf = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_NO_SERIALIZE, 255);
            if (SOCKET_ERROR == recv(sockConn, pszBuf, 255, 0))
            {
                cout << "接受数据失败,错误码为:" << WSAGetLastError() << endl;
                cout << "准备进行下一次接受数据....." << endl;
                continue;
            }
    
            g_hRecvThread = CreateThread(NULL, 0, RecvThread, pszBuf, 0, &g_nRecvID);
            WaitForSingleObject(g_hRecvThread, INFINITE);
            if (0 == strcmp("exit", pszBuf))
            {
                cout << "正在断开与该客户端的连接" << endl;
                HeapFree(GetProcessHeap(), 255, pszBuf);
                return 0;
            }
        }
    
        return 0;
    }

    信息处理子线程:
    DWORD WINAPI RecvThread(LPVOID lpParameter)
    {
    cout << “接受到客户端的数据:” << (char*)lpParameter << endl;
    return 0;
    }
    虽说这个解决了多个客户端与服务器通信的问题,但是这样写确定也很明显:所有的与客户端通信的socket都有程序员自己管理,无疑加重了程序员的负担;每有一个连接都需要创建一个线程,当有大量的客户端连接进来开辟的线程数是非常多的,线程是非常耗资源的,所以为了解决这些问题就提出了异步的I/O模型,它们解决了这些问题,由系统管理套接字,不要要人为的一个个管理,同时不需要开辟多个线程来处理与客户端的连接,我们可以将线程主要用于处理客户端的请求上;

  • 相关阅读:
    Github开始强制使用PAT(Personal Access Token)了
    STM32F401的外部中断EXTI
    STM32F401的PWM输出
    STM32F103和STM32F401的ADC多通道采集DMA输出
    nRF24L01无线模块笔记
    51单片机(STC89C52)在Ubuntu下的开发
    51单片机(STC89C52)的中断和定时器
    51单片机的软件和硬件PCA/PWM输出
    Zadig 云原生持续交付 面向开发者设计的开源、高可用 CI/CD
    人生 乐观 悲观 英雄
  • 原文地址:https://www.cnblogs.com/lanuage/p/7725762.html
Copyright © 2011-2022 走看看