c/c++ TcpServer-IOCP管理模式

IOCP

1: IOCP: 是windows针对高性能服务器做的IO的管理模式,又叫完成端口;
2: IOCP的核心模式:
1>提交请求;
2>等待结果;
3>继续提交请求;
3: 监听:
1>提交一个监听请求,使用完成端口来等待这个请求到来;
2>请求来了后,处理,继续提交请求;
4: 读取数据:
1>提交一个读取数据的请求。
2>请求完成后,处理完后继续提交;
5: 发送数据的请求:
1>提交一个发送数据的请求;
2>请求完成后,继续处理;


IOCP开发

1:配置IOCP开发环境:

    #include <WinSock2.h>
    #include <mswsock.h>
    #include <windows.h> 
    #pragma comment(lib, "WSOCK32.lib ")
    #pragma comment(lib, "ws2_32.lib")
    #pragma comment(lib, "odbc32.lib")
    #pragma comment(lib, "odbccp32.lib")

2: 创建一个IOCP;
3: 提交一个IOCP的监听请求:
4: 提交读请求;


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/*
IOCP 只支持windows平台 Linux epoll
*/
#include <WinSock2.h>
#include <MSWSock.h>
#include <Windows.h>

#pragma comment(lib,"WSOCK32.LIB")
#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"odbc32.lib")
#pragma comment(lib,"odbccp32.lib")

// 非常重要的数据结构;
enum{
    IOCP_ACCEPT = 0, // 监听socket,接入请求;
    IOCP_RECV, // 读请求;
    IOCP_WRITE, // 写请求;
};


// 接收数据的时候最大的buf大小;
#define MAX_RECV_SIZE 8192


struct io_package{
    // 自己定义的, 一定要在第一个就要用WSAOVERLAPPED 结构体;?
    // 所有的请求的等待,都是等在这个结构对象上的,必须是第一个;
    WSAOVERLAPPED overlapped;

    // 操作类型, 监听,读,写, IOCP_ACCPET, IOCP_RECV, IOCP_WRITE
    int opt;

    // 句柄,就是我们提交请求的句柄, accept的句柄或你读写请求的句柄
    int accept_sock;

    // 结构体,配合读写数据的时候用的bufffer;
    WSABUF wsabuffer;// wsabuffer.buf = 内存;, wsabuffer.len = MAX_RECV_SIZE;

    // 定义了一个buf,这个buf就是整正的内存;
    char pkg[MAX_RECV_SIZE];
};

// 投递一个用户的请求;
static void post_accept(SOCKET l_sock){
    // step1: 分配一个io_package 数据结构;
    struct io_package* pkg = malloc(sizeof(struct io_package));
    memset(pkg, 0, sizeof(struct io_package));

    // 初始化好了接受数据的buf --> WSABUF
    pkg->opt = IOCP_ACCEPT; // 请求类型;
    pkg->wsabuffer.buf = pkg->pkg;
    pkg->wsabuffer.len = MAX_RECV_SIZE - 1;

    SOCKET client = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
    int addr_size = (sizeof(struct sockaddr) + 16);
    pkg->accept_sock = client;// 创建一个socket,然后客户端连接进来后,就用这个socket和我们的客户端通讯;

    // 发送一个异步的请求,来接入客户端的连接;
    DWORD dwBytes = 0;
    AcceptEx(l_sock, client, pkg->wsabuffer.buf, 0, 
        addr_size, addr_size, &dwBytes, &pkg->overlapped);

}

static void post_recv(SOCKET client_fd){
    struct io_package* io_data = malloc(sizeof(struct io_package));
    memset(io_data, 0, sizeof(struct io_package));

    io_data->opt = IOCP_RECV;
    io_data->wsabuffer.buf=io_data->pkg;
    io_data->wsabuffer.len = MAX_RECV_SIZE - 1;
    io_data->accept_sock = client_fd;

    DWORD dwRecv = 0;
    DWORD dwFlgs = 0;
    int ret = WSARecv(client_fd, &(io_data->wsabuffer), 1,
        &dwRecv, &dwFlgs,&(io_data->overlapped),NULL );
}

int main(int argc, char** argv){
    // 如果你做socket那么必须要加上;
    WSADATA data;
    WSAStartup(MAKEWORD(2, 2), &data);

    // step1:创建一个完成端口;
    HANDLE  iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    if (iocp == INVALID_HANDLE_VALUE){
        goto failed;
    }

    // 创建我们的监听socket,开始监听
    SOCKET l_sock = INVALID_SOCKET;
    l_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (l_sock == INVALID_SOCKET){
        goto failed;
    }
    struct sockaddr_in s_address;
    memset(&s_address, 0, sizeof(s_address));
    s_address.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    s_address.sin_family = AF_INET;
    s_address.sin_port = htons(6080);
    if (bind(l_sock, (struct sockaddr*)&s_address, sizeof(s_address)) != 0){
        goto failed;
    }
    if (listen(l_sock, SOMAXCONN) != 0){
        goto failed;
    }

    // 让IOCP来管理我们的l_sock
        // 第三个参数,是用户传的自定义数据(一个指针带入进去),后面讲解;
    CreateIoCompletionPort((HANDLE)l_sock, iocp, (DWORD)0, 0);

    // 发送一个监听用户进来的请求;
    post_accept(l_sock);// 投递一个accpet接入请求;

    while (1){
        DWORD dwTrans;
        DWORD udata;
        struct io_package* io_data;
        // 通过完成端口,来获得这个请求的结果;
        // 调用操作系统的API函数,查看,那个请求完成了;
        // 如果没有状态完成了,那么任务会挂起,等待;
        int ret = GetQueuedCompletionStatus(iocp,&dwTrans,
            &udata,(LPOVERLAPPED*)&io_data,WSA_INFINITE);
        if (ret == 0){ //意外
            if (io_data){
                if (io_data->opt == IOCP_RECV){
                    closesocket(io_data->accept_sock);
                    free(io_data);
                }
                else if (io_data->opt == IOCP_ACCEPT){
                    free(io_data);
                    post_accept(l_sock);
                }
            }
            continue;
        }
        if (dwTrans == 0 && io_data->opt == IOCP_RECV){
            // 关闭socket发生了;
            closesocket(io_data->accept_sock);
            free(io_data);
            continue;
        }
        switch (io_data->opt)
        {
        case IOCP_ACCEPT: //接入第一个SOCKET;
        {
            //接入的client_sock
            int client_fd = io_data->accept_sock;
            int addr_size = (sizeof(struct sockaddr_in) + 16);
            struct sockaddr_in* l_addr = NULL;
            int l_len = sizeof(struct sockaddr_in);
            struct sockaddr_in* r_addr = NULL;
            int r_len = sizeof(struct sockaddr_in);

            GetAcceptExSockaddrs(io_data->wsabuffer.buf, 0,/*io_data->wsabuffer.len - addr_size * 2, */
                addr_size, addr_size,
                (struct sockaddr**)&l_addr, &l_len,
                (struct sockaddr**)&r_addr,&r_len);

            // 将新进来的client_fd,也加入完成端口,来帮助管理完成的请求;
            // 第三个参数是用户自定义数据,你可以携带自己的数据结构,client_fd;
            CreateIoCompletionPort((HANDLE)client_fd, iocp, (DWORD)client_fd, 0);
            //投递一个读的请求
            post_recv(client_fd);
            //重新投递一个接入客户端
            free(io_data);
            post_accept(l_sock);
        }
        break;
        case IOCP_RECV:
        {
            //dwTrans 读到数据的大小;
            //soket,io_data->accept_sock;
            //Buf io_data->wsabuffer.buf
            io_data->pkg[dwTrans] = 0;
            printf("IOCP RECV %d %s\n", dwTrans, io_data->pkg);
            send(io_data->accept_sock, io_data->pkg, dwTrans, 0);


            //再来投递下一个请求
            DWORD dwRecv = 0;
            DWORD dwFlgs = 0;
            int ret = WSARecv(io_data->accept_sock, &(io_data->wsabuffer),
                1, &dwRecv, &dwFlgs, &(io_data->overlapped),NULL);
        }
        break;
        default:
            break;
        }
    }

failed:
    if (l_sock){
        goto failed;
    }
    if (iocp != INVALID_HANDLE_VALUE){
        CloseHandle(iocp);//释放关闭端口
    }

    WSACleanup();
    return 0;
}

服务端

客户端

猜你喜欢

转载自blog.csdn.net/qq_36482772/article/details/79312620
今日推荐