windows完成端口原理

该文展示了一个Windows下的TCP服务器实现,使用C++编写。服务器初始化涉及WSAStartup,通过CNetServer基类定义接口,CTCPServer类实现网络服务,包括初始化、运行、停止和清理。服务器使用IO完成端口进行多线程处理,接收和发送数据。
摘要由CSDN通过智能技术生成

代码实现:

INetServer.h

#pragma once
struct INetServer
{
    virtual bool init() = 0;
    virtual void run() = 0;
    virtual void stop() = 0;
    virtual bool uninit() = 0;
};

NetServer.h

#pragma once

#include "INetServer.h"

class CNetServer : public INetServer
{
public:
    CNetServer();
    virtual ~CNetServer();

    virtual bool init() override;

    virtual bool uninit() override;
};

NetServer.cpp

#include "NetServer.h"

#include <WinSock2.h>
#include <iostream>

using namespace std;
#pragma comment(lib, "ws2_32.lib")

CNetServer::CNetServer()
{
}


CNetServer::~CNetServer()
{
}

bool CNetServer::init()
{
    // 初始化网络库
    WSADATA wsaData;
    if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData)) {
        cerr << "WSAStartup() Failed" << endl;
        return false;
    }

    return true;
}

bool CNetServer::uninit()
{
    if (0 != WSACleanup()) {
        cerr << "WSACleanup Failed()" << endl;
        return false;
    }

    return true;
}

TCPServer.h

#pragma once

#include "NetServer.h"

#include <atomic>

class CTCPServerImp;
class CTCPServer final
    : public CNetServer
{
public:
    CTCPServer(char* pszIp, int nPort);
    ~CTCPServer();

    virtual bool init() override;

    virtual void run() override;

    virtual void stop() override;

private:
    CTCPServerImp*      m_pImp;
};

TCPServer.cpp

#include "TCPServer.h"
#include "Macro.h"

#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
#include <queue>
#include <map>
#include <thread>

using namespace std;

#define MESSAGESIZE 1024

HANDLE CreateNewIOCompletionPort(DWORD dwNumberOfConcurrentThreads)
{
    return CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, dwNumberOfConcurrentThreads);
}

bool AssociateDeviceWithCompletionPort(HANDLE hDevice, HANDLE hCompletionPort, DWORD dwCompletionKey)
{
    HANDLE h = CreateIoCompletionPort(hDevice, hCompletionPort, dwCompletionKey, 0);
    return h == hCompletionPort;
}

enum emSocketOperate
{
    SOCKET_OPERATE_RECV
};

struct socketData
{
    WSAOVERLAPPED        overlapped;
    WSABUF                buf;
    char                sMessage[MESSAGESIZE];
    DWORD                dwBytes;
    DWORD                flag;
    emSocketOperate      socketOpType;
    void Clear(emSocketOperate type)
    {
        memset(this, 0, sizeof(socketData));
        buf.buf = sMessage;
        buf.len = MESSAGESIZE;
        socketOpType = type;
    }
};

class CTCPServerImp
{
public:
    CTCPServerImp(char* pszIp, int nPort)
        : m_pszIP(nullptr)
        , m_nPort(8888)
        , m_svrSocket(INVALID_SOCKET)
        , m_bStop(false)
        , m_hIOCP(INVALID_HANDLE_VALUE)
    {
        if (pszIp != nullptr) {
            int nLen = strlen(pszIp);
            if (nLen > 8) {
                m_pszIP = new char[nLen];
                memcpy_s(m_pszIP, nLen, pszIp, nLen);
            }
        }

        if (nPort > 0) {
            m_nPort = nPort;
        }
    }

    ~CTCPServerImp()
    {
        if (m_svrSocket != INVALID_SOCKET) {
            closesocket(m_svrSocket);
            m_svrSocket = INVALID_SOCKET;
        }

        while (!m_queProcThread.empty()) {
            auto pThread = m_queProcThread.front();
            m_queProcThread.pop();
            pThread->join();
            delete pThread;
        }

        auto itor = m_mSock.begin();
        for (; itor != m_mSock.end(); ++itor) {
            closesocket(itor->first);
            delete itor->second;
            itor = m_mSock.erase(itor);
        }

        SAFE_DELETE(m_pszIP);
    }

private:
    bool init()
    {
        // 1、创建SOCKET
        m_svrSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, NULL, WSA_FLAG_OVERLAPPED);
        if (m_svrSocket == INVALID_SOCKET) {
            cerr << "WSASocket()" << endl;
            return false;
        }

        // 2、绑定端口
        SOCKADDR_IN addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(m_nPort);
        if (m_pszIP == nullptr || strlen(m_pszIP) < 8) {
            addr.sin_addr.s_addr = htonl(INADDR_ANY);
        }
        else {
            inet_pton(AF_INET, m_pszIP, &addr.sin_addr);
        }
        // 此处std::bind函数和套接字bind函数冲突,需要通过作用域区分,::bind是匿名作用域
        int nRet = ::bind(m_svrSocket, (SOCKADDR*)&addr, sizeof(addr));
        if (0 != nRet) {
            cerr << "bind()" << endl;
            return false;
        }

        // 3、监听
        nRet = listen(m_svrSocket, 10);
        if (0 != nRet) {
            cerr << "listen()" << endl;
            return false;
        }
    }

    void socketProcess()
    {
        DWORD dwBytes = -1;
        SOCKET cSocket;
        socketData *lpSocketData = nullptr;
        DWORD dwMilliseconds = 1000;

        while (!m_bStop) {
            BOOL bOK = GetQueuedCompletionStatus(m_hIOCP, &dwBytes, (PULONG_PTR)&cSocket, (LPOVERLAPPED*)&lpSocketData, dwMilliseconds);
            DWORD dwError = GetLastError();
            if (bOK) {
                // processs a successfully completed IO request
                if (lpSocketData != nullptr && lpSocketData->socketOpType == SOCKET_OPERATE_RECV) {
                    if (dwBytes == 0) {
                        closesocket(cSocket);
                        delete lpSocketData;
                        m_mSock.erase(cSocket);

                        printf("%x\t:closed\n", (DWORD)cSocket);
                    }
                    else {
                        lpSocketData->sMessage[dwBytes] = '\0';
                        printf("%x\t:%s\n", (DWORD)cSocket, lpSocketData->sMessage);
                        lpSocketData->Clear(SOCKET_OPERATE_RECV);
                        WSARecv(cSocket, &lpSocketData->buf, 1, &lpSocketData->dwBytes, &lpSocketData->flag, &lpSocketData->overlapped, NULL);
                    }
                }
            }
            else {
                if (lpSocketData != nullptr) {
                    // process a failed completed IO request
                    // dwError contains the reason for failure
                }
                else {
                    if (dwError == WAIT_TIMEOUT) {
                        // Time-out while waiting for completed IO entry
                    }
                    else {
                        // Bad call to GetQueuedCompletionStatus
                        // dwError contains the reason for the bad call
                    }

                }
            }
        }
    }

    void run()
    {
        if (m_svrSocket == INVALID_SOCKET) {
            return;
        }

        // 创建完成端口
        m_hIOCP = CreateNewIOCompletionPort(0);
        // 创建完成端口处理线程池
        SYSTEM_INFO sysInfo;
        GetSystemInfo(&sysInfo);
        for (int i = 0; i < sysInfo.dwNumberOfProcessors * 2; ++i) {
            thread* pThread = new thread(&CTCPServerImp::socketProcess, this);
            m_queProcThread.push(pThread);
        }

        // 接受连接及接收数据启动
        SOCKADDR_IN clientAddr;
        int nAddrLen = sizeof(clientAddr);
        while (!m_bStop)
        {
            SOCKET cSocket = accept(m_svrSocket, (SOCKADDR*)&clientAddr, &nAddrLen);
            if (cSocket != INVALID_SOCKET) {
                char pszIP[20] = { 0 };
                inet_ntop(AF_INET, &clientAddr.sin_addr, pszIP, 20);
                printf("accept ip:%s port:%d\n", pszIP, ntohs(clientAddr.sin_port));

                AssociateDeviceWithCompletionPort((HANDLE)cSocket, m_hIOCP, (DWORD)cSocket);

                socketData* pData = new socketData;
                pData->Clear(SOCKET_OPERATE_RECV);
                WSARecv(cSocket, &pData->buf, 1, &pData->dwBytes, &pData->flag, &pData->overlapped, nullptr);

                m_mSock[cSocket] = pData;
            }
        }
    }

private:
    // SOCKET
    char*               m_pszIP;
    int                 m_nPort;
    SOCKET              m_svrSocket;
    map<SOCKET, socketData*> m_mSock;

    // 完成端口句柄
    HANDLE              m_hIOCP;
    queue<thread*>      m_queProcThread;

    // 线程控制
    std::atomic<bool>   m_bStop;

    friend class CTCPServer;
};

CTCPServer::CTCPServer(char* pszIp, int nPort)
{
    m_pImp = new CTCPServerImp(pszIp, nPort);
}

CTCPServer::~CTCPServer()
{
    SAFE_DELETE(m_pImp)
}

bool CTCPServer::init()
{
    if (!CNetServer::init()) {
        return false;
    }

    if (m_pImp != nullptr) {
        m_pImp->init();
    }
}

void CTCPServer::run()
{
    thread tAccept(&CTCPServerImp::run, m_pImp);
    tAccept.join();
}

void CTCPServer::stop()
{
    m_pImp->m_bStop = true;
}

main.cpp

#include "TCPServer.h"

#include <iostream>
#include <windows.h>

int main(int argc, char** argv)
{
    CTCPServer tcp(nullptr, 1234);
    tcp.init();
    tcp.run();
    tcp.uninit();
    return 0;
}

Macro.h

#ifndef __MACRO_H__
#define __MACRO_H__

#define SAFE_DELETE(ptr) if (ptr != nullptr) { delete ptr; ptr = nullptr; }
#define SAFE_DELETE_ARR(pArr) if (pArr != nullptr) { delete [] pArr; pArr = nullptr; }

#endif __MACRO_H__

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值