如何产生ioexception_结合实例论述控制过程

如何产生ioexception_结合实例论述控制过程IOCP结合AcceptEx实例

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

在普通IOCP的基础上注意两点:
1.记得把监听socket绑定到端口
2.在Accept处理过程中,抛出接受连接的AcceptEx请求,绑定客户端socket到端口和抛出recv请求

客户端要断开连接时,只需发送一个大小为0的内容即可。我们在服务器处理时,收到0,就销毁该socket

// IOCP_TCPIP_Socket_Server.cpp

#include <WinSock2.h>
#include <Windows.h>
#include <vector>
#include <iostream>
#include <mswsock.h>

using namespace std;

#pragma comment(lib, "Ws2_32.lib") // Socket编程需用的动态链接库
#pragma comment(lib, "Kernel32.lib") // IOCP需要用到的动态链接库


#define SEND 0
#define RECV 1
#define ACCEPT 2


/** * 结构体名称:PER_IO_DATA * 结构体功能:重叠I/O需要用到的结构体,临时记录IO数据 **/
const int DataBuffSize = 2 * 1024;
typedef struct
{
    OVERLAPPED overlapped;
    WSABUF databuff;
    char buffer[DataBuffSize];
    int BufferLen;
    int operationType;
    SOCKET client;
}PER_IO_OPERATEION_DATA, *LPPER_IO_OPERATION_DATA, *LPPER_IO_DATA, PER_IO_DATA;

/** * 结构体名称:PER_HANDLE_DATA * 结构体存储:记录单个套接字的数据,包括了套接字的变量及套接字的对应的客户端的地址。 * 结构体作用:当服务器连接上客户端时,信息存储到该结构体中,知道客户端的地址以便于回访。 **/
typedef struct
{
    SOCKET socket;
    SOCKADDR_STORAGE ClientAddr;
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

// 定义全局变量
const int DefaultPort = 5000;
vector < PER_HANDLE_DATA* > clientGroup;        // 记录客户端的向量组
int g_nThread = 0;//开启线程数量
HANDLE hThread[50];//线程句柄

SOCKET srvSocket = NULL;
DWORD dwBytes = 0;

HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
DWORD WINAPI ServerWorkThread(LPVOID CompletionPortID);
DWORD WINAPI ServerSendThread(LPVOID IpParam);


LPFN_ACCEPTEX lpfnAcceptEx = NULL;//AcceptEx函数指针
GUID guidAcceptEx = WSAID_ACCEPTEX;
GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS;
LPFN_GETACCEPTEXSOCKADDRS lpfnGetAcceptExSockAddrs = NULL;

// 开始主函数
int main()
{
    // 加载socket动态链接库
    WORD wVersionRequested = MAKEWORD(2, 2); // 请求2.2版本的WinSock库
    WSADATA wsaData;    // 接收Windows Socket的结构信息
    DWORD err = WSAStartup(wVersionRequested, &wsaData);

    if (0 != err) { // 检查套接字库是否申请成功
        cerr << "Request Windows Socket Library Error!\n";
        system("pause");
        return -1;
    }
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
  
  // 检查是否申请了所需版本的套接字库
        WSACleanup();
        cerr << "Request Windows Socket Version 2.2 Error!\n";
        system("pause");
        return -1;
    }

    // 创建IOCP的内核对象
    /** * 需要用到的函数的原型: * HANDLE WINAPI CreateIoCompletionPort( * __in HANDLE FileHandle, // 已经打开的文件句柄或者空句柄,一般是客户端的句柄 * __in HANDLE ExistingCompletionPort, // 已经存在的IOCP句柄 * __in ULONG_PTR CompletionKey, // 完成键,包含了指定I/O完成包的指定文件 * __in DWORD NumberOfConcurrentThreads // 真正并发同时执行最大线程数,一般推介是CPU核心数*2 * ); **/
    HANDLE completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    if (NULL == completionPort) {   // 创建IO内核对象失败
        cerr << "CreateIoCompletionPort failed. Error:" << GetLastError() << endl;
        system("pause");
        return -1;
    }

    // 创建IOCP线程--线程里面创建线程池

    // 确定处理器的核心数量
    SYSTEM_INFO mySysInfo;
    GetSystemInfo(&mySysInfo);

    // 基于处理器的核心数量创建线程
    for (DWORD i = 0; i < (mySysInfo.dwNumberOfProcessors * 2); ++i) {
        // 创建服务器工作器线程,并将完成端口传递到该线程
        HANDLE ThreadHandle = CreateThread(NULL, 0, ServerWorkThread, completionPort, 0, NULL);//第一NULL代表默认安全选项,第一个0,代表线程占用资源大小,第二个0,代表线程创建后立即执行
        if (NULL == ThreadHandle) {
            cerr << "Create Thread Handle failed. Error:" << GetLastError() << endl;
            system("pause");
            return -1;
        }
        hThread[i] = ThreadHandle;
        ++g_nThread;
    }

    // 建立流式套接字
    srvSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);


    // Associate SOCKET with IOCP 
    if (NULL == CreateIoCompletionPort((HANDLE)srvSocket, completionPort, NULL, 0))
    {
        cout << "CreateIoCompletionPort failed with error code: " << WSAGetLastError() << endl;
        if (INVALID_SOCKET != srvSocket)
        {
            closesocket(srvSocket);
            srvSocket = INVALID_SOCKET;
        }
        return -1;
    }


    // 绑定SOCKET到本机
    SOCKADDR_IN srvAddr;
    srvAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    srvAddr.sin_family = AF_INET;
    srvAddr.sin_port = htons(DefaultPort);
    int bindResult = bind(srvSocket, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR));
    if (SOCKET_ERROR == bindResult) {
        cerr << "Bind failed. Error:" << GetLastError() << endl;
        system("pause");
        return -1;
    }

    // 将SOCKET设置为监听模式
    int listenResult = listen(srvSocket, 10);
    if (SOCKET_ERROR == listenResult) {
        cerr << "Listen failed. Error: " << GetLastError() << endl;
        system("pause");
        return -1;
    }

    // 开始处理IO数据
    cout << "本服务器已准备就绪,正在等待客户端的接入...\n";

     创建用于发送数据的线程
    //HANDLE sendThread = CreateThread(NULL, 0, ServerSendThread, 0, 0, NULL);//第二个0,代表回掉函数参数为0



    for (int i = 0; i < 10; ++i)
    {

        PER_HANDLE_DATA * PerHandleData = NULL;
        SOCKET acceptSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
        if (INVALID_SOCKET == acceptSocket)
        {
            cerr << "WSASocket failed with error code: %d/n" << WSAGetLastError() << endl;
            return FALSE;
        }

        // 开始在接受套接字上处理I/O使用重叠I/O机制
        // 在新建的套接字上投递一个或多个异步
        // WSARecv或WSASend请求,这些I/O请求完成后,工作者线程会为I/O请求提供服务 
        // 单I/O操作数据(I/O重叠)
        LPPER_IO_OPERATION_DATA PerIoData = NULL;
        PerIoData = (LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_OPERATEION_DATA));
        ZeroMemory(&(PerIoData->overlapped), sizeof(OVERLAPPED));
        PerIoData->databuff.len = 1024;
        PerIoData->databuff.buf = PerIoData->buffer;
        PerIoData->operationType = ACCEPT;  // read
        PerIoData->client = acceptSocket;

        if (SOCKET_ERROR == WSAIoctl(srvSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidAcceptEx, sizeof(guidAcceptEx), &lpfnAcceptEx,
            sizeof(lpfnAcceptEx), &dwBytes, NULL, NULL))
        {
            cerr << "WSAIoctl failed with error code: " << WSAGetLastError() << endl;
            if (INVALID_SOCKET != srvSocket)
            {
                closesocket(srvSocket);
                srvSocket = INVALID_SOCKET;
            }
            //goto EXIT_CODE;
            return -1;
        }

        if (SOCKET_ERROR == WSAIoctl(srvSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidGetAcceptExSockAddrs,
            sizeof(GuidGetAcceptExSockAddrs), &lpfnGetAcceptExSockAddrs, sizeof(lpfnGetAcceptExSockAddrs),
            &dwBytes, NULL, NULL))
        {
            cerr << "WSAIoctl failed with error code: " << WSAGetLastError() << endl;
            if (INVALID_SOCKET != srvSocket)
            {
                closesocket(srvSocket);
                srvSocket = INVALID_SOCKET;
            }
            //goto EXIT_CODE;
            return -1;
        }

        if (FALSE == lpfnAcceptEx(srvSocket, PerIoData->client, PerIoData->databuff.buf, PerIoData->databuff.len - ((sizeof(SOCKADDR_IN) + 16) * 2),
            sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, &dwBytes, &(PerIoData->overlapped)))
        {
            if (WSA_IO_PENDING != WSAGetLastError())
            {
                cerr << "lpfnAcceptEx failed with error code: " << WSAGetLastError() << endl;

                return FALSE;
            }
        }


    }


    Sleep(1000 * 60 * 60);
    PostQueuedCompletionStatus(completionPort, 0, NULL, NULL);
    WaitForMultipleObjects(g_nThread, hThread, TRUE, INFINITE);

    WSACleanup();
    system("pause");
    return 0;
}

// 开始服务工作线程函数
DWORD WINAPI ServerWorkThread(LPVOID IpParam)
{
    HANDLE CompletionPort = (HANDLE)IpParam;
    DWORD BytesTransferred;
    LPOVERLAPPED IpOverlapped;
    LPPER_HANDLE_DATA PerHandleData = NULL;
    LPPER_IO_DATA PerIoData = NULL;
    DWORD RecvBytes = 0;
    DWORD Flags = 0;
    BOOL bRet = false;

    while (true) {
        bRet = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (PULONG_PTR)&PerHandleData, (LPOVERLAPPED*)&IpOverlapped, INFINITE);
        if (bRet == 0) {
            if (WAIT_TIMEOUT == GetLastError())
            {
                continue;
            }
            // Error
            cout << "GetQueuedCompletionStatus failed with error:" << GetLastError() << endl;
            continue;
        }
        PerIoData = (LPPER_IO_DATA)CONTAINING_RECORD(IpOverlapped, PER_IO_DATA, overlapped);
        //这个宏的作用是:根据一个结构体实例中的成员的地址,取到整个结构体实例的地址
        //PER_IO_DATA的成员overlapped的地址为&IpOverlapped,结果就可以获得PER_IO_DATA的地址

        if (NULL == PerIoData)
        {
            // Exit thread 
            break;
        }



        // 检查在套接字上是否有错误发生
        if (0 == BytesTransferred && (PerIoData->operationType == RECV || PerIoData->operationType == SEND))
        {
            closesocket(PerHandleData->socket);
            GlobalFree(PerHandleData);
            GlobalFree(PerIoData);
            continue;
        }

        switch (PerIoData->operationType)
        {
        case ACCEPT:
        {
            SOCKADDR_IN* remote = NULL;
            SOCKADDR_IN* local = NULL;
            int remoteLen = sizeof(SOCKADDR_IN);
            int localLen = sizeof(SOCKADDR_IN);
            lpfnGetAcceptExSockAddrs(PerIoData->databuff.buf, PerIoData->databuff.len - ((sizeof(SOCKADDR_IN) + 16) * 2),
                sizeof(SOCKADDR_IN) + 16, sizeof(SOCKADDR_IN) + 16, (LPSOCKADDR*)&local, &localLen, (LPSOCKADDR*)&remote, &remoteLen);


            //使用GetAcceptExSockaddrs函数 获得具体的各个地址参数.
            if (setsockopt(PerIoData->client, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
                (char*)&(PerHandleData->socket), sizeof(PerHandleData->socket)) == SOCKET_ERROR)
                cout << "setsockopt..." << endl;
            // 创建用来和套接字关联的单句柄数据信息结构
            PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));  // 在堆中为这个PerHandleData申请指定大小的内存
            PerHandleData->socket = PerIoData->client;

            //memcpy(&(perHandleData->clientAddr),raddr,sizeof(raddr));
            //将新的客户套接字与完成端口连接
            CreateIoCompletionPort((HANDLE)PerHandleData->socket,
                CompletionPort, (ULONG_PTR)PerHandleData, 0);

            memset(&(PerIoData->overlapped), 0, sizeof(OVERLAPPED));
            PerIoData->operationType = RECV;        //将状态设置成接收
                                                   //设置WSABUF结构
            PerIoData->databuff.buf = PerIoData->buffer;
            PerIoData->databuff.len = PerIoData->BufferLen = 1024;

            cout << "wait for data arrive(Accept)..." << endl;
            Flags = 0;
            if (WSARecv(PerHandleData->socket, &(PerIoData->databuff), 1,
                &RecvBytes, &Flags, &(PerIoData->overlapped), NULL) == SOCKET_ERROR)
                if (WSAGetLastError() == WSA_IO_PENDING)
                    cout << "WSARecv Pending..." << endl;

            continue;
        }
        break;
        case RECV:
            // 开始数据处理,接收来自客户端的数据
            //WaitForSingleObject(hMutex, INFINITE);
            cout << "A Client says: " << PerIoData->databuff.buf << endl;
            //ReleaseMutex(hMutex);

            // 为下一个重叠调用建立单I/O操作数据
            ZeroMemory(&(PerIoData->overlapped), sizeof(OVERLAPPED)); // 清空内存
            PerIoData->databuff.len = 1024;
            PerIoData->databuff.buf = PerIoData->buffer;//buf是个指针,这一过程会清空buffer的内容
            PerIoData->operationType = RECV;    // read
            WSARecv(PerHandleData->socket, &(PerIoData->databuff), 1, &RecvBytes, &Flags, &(PerIoData->overlapped), NULL);

            continue;
            break;
        default:
            break;
        }
    }

    return 0;
}


// 发送信息的线程执行函数
DWORD WINAPI ServerSendThread(LPVOID IpParam)
{
    while (1) {
        if (clientGroup.empty())
        {
            Sleep(5000);
            continue;
        }

        char talk[200];
        cin.get(talk, 200);
        int len;
        for (len = 0; talk[len] != '\0'; ++len) {
            // 找出这个字符组的长度
        }
        talk[len] = '\n';
        talk[++len] = '\0';
        printf("I Say:");
        cout << talk;
        //WaitForSingleObject(hMutex, INFINITE);
        for (unsigned i = 0; i < clientGroup.size(); ++i) {
            send(clientGroup[i]->socket, talk, 200, 0); // 发送信息
        }
        //ReleaseMutex(hMutex);
    }
    return 0;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/187823.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • [日常办公][Ubuntu]proxy settings

    [日常办公][Ubuntu]proxy settingsbashproxyexporthttp_proxy=XXXexporthttps_proxy=XXXaptproxysudovim/etc/apt/apt.confAcquire::http::proxy”http://XXX”;Acquire::https::proxy”https://XXX”;Acquire::ftp::proxy”ftp://XXX”;Acquire::socks::proxy”socks://XXX”;dockerproxymkdir

    2022年7月21日
    11
  • javascript替换换行符的正确方法

    javascript替换换行符的正确方法js报错(Error:unterminatedstringliteral),原因是字符串中包含换行符,需要用javascript替换换行符,兼容IE和Firefox的正确方法是,使用正则并且把\r和\n分开替换:str.replace(/\r/ig,“”).replace(/\n/ig,“”);需要注意的是:1.javascript的replace只能替换一次…

    2022年5月23日
    41
  • python表白代码-表白python代码

    python表白代码-表白python代码广告关闭2017年12月,云+社区对外发布,从最开始的技术博客到现在拥有多个社区产品。未来,我们一起乘风破浪,创造无限可能。七夕快到了,表白素材赶紧先准备好。。。0、委婉的表白python代码:importstringl=string.ascii_letterss=s.append(l)s.append(l)s.append(l)s.append(l)s.append(l)s.appe…

    2022年6月2日
    31
  • 设备管理器里“SM总线控制器”、“其他PCI桥设备”驱动有问题

    设备管理器里“SM总线控制器”、“其他PCI桥设备”驱动有问题设备管理器里“SM总线控制器”、“其他PCI桥设备”驱动有问题

    2022年6月6日
    51
  • 谷歌地球怎么画路线图_消防路线图怎么画

    谷歌地球怎么画路线图_消防路线图怎么画1.首先需要将GPSDebug.log打开。打开方法 :点我传送。 2.使用NMEA2KMZ程序将LOG中的Nmealog开头的log转换成KMZ文件,会生成一个日期的文件。3.直接将生成的文件放到GoogleEarth中就行了。当两条路线对比的时候,还可以调整路线的颜色。注: KMZ文件是将GPS每秒位置点连成轨迹,比较精确的反映了测试轨迹。

    2022年9月19日
    0
  • 第12章方差分析介绍课_t检验中的第一类错误是指

    第12章方差分析介绍课_t检验中的第一类错误是指方差分析用以比较两组及以上处理之间的平均数差异的情况因素:方差分析中,用于分派组别的变量水平:因素的个别情形或个别值方差分析与t检验比较(为什么使用方差分析而不用t检验多次比较均值差异):检验α水平:做一个单独的假设检验时犯第一类错误的可能性。实验α水平:当一个实验包括多个不同的假设检验时,实验中全部独立的检验所犯第一类错误积累后的犯错总概率。通常,实验α水平比任何一个单独的检验的α值大。方差分析可在一次实验中同时进行三个及以上均值差异的比较,避免了实验α的膨胀。方差分析…

    2022年10月10日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号