• socket通讯IOCP模型


    1.同步与异步模式(Sync/Async)
    在一些IO函数如ReadFile(),socket.recv(),默认使用的是同步模式,即函数执行完成后才返回,如果既没有数据,也没有超时设置,则程序会阻塞在这里。在对话框主程序中,如果使用这种方式会把界面卡死。

    处理这类问题的常见方法是,启动一个线程,将这些IO操作放在线程中执行。如果需要结束线程的阻塞状态,只要在主线程中关闭Handle/Socket即可。

    另一种处理方式为异步模式,IO函数ReadFile()调用后直接返回,通过查询状态来获取其执行结果,如:
    HANDLE hFile = CreateFile(xx, GENERIC_READ, 0, NULL,...,FILE_FLAG_OVERLAPPED); //指定FILE_FLAG_OVERLAPPED
    OVERLAPPED overlap;
    ReadFile(hFile, buf,nsize, &nread, &overlap);//立即返回false,err=IO_PENDING
    while(i<100 && !HasOverlappedIoCompleted(&overlap)); // 等待完成……
    { DoSomething();
      i++;Sleep(1000);//CancelIO(hFile)...
    }
    这种方式可以对阻塞过程进行控制,如超时退出或做其他处理。

    2.同步模式的多任务
    以上两种模式,在单个IO操作时区别不大,异步模式更零活一些。在多任务的情况下,网络服务器面向上万的客户端,同步模式则需要开启上万个线程来通讯
    sockServer = socket(...);
    _beginThread(Thread_Recving, ...);
    Thread_Accepting()
    {
      while(bRunning)
      {
        sockClient = accept(sockServer, ...); //blocking
        _beginThread(Thread_Recving, ...);
      }
    }
    Thread_Recving() // 10000+个线程
    {
      while(bRunning)
      {
        recv(sockClient, ...); //blocking
        DoSomthing();
      }
    }
    这种方式非常消耗CPU和内存资源,系统在各个线程之间切换,效率很低。

    3.异步模式的多任务——IOCP
    在上面的例子中,如果以异步模式建立连接,以轮询的方式来检查各个连接是否收到数据
    sockServer = socket(...);
    _beginthread(Thread_Accepting, ...);
    _beginthread(Thread_Recving, ...);
    Thread_Accepting()
    {
      while(bRunning)
      {
        sockClient[x] = accept(sockServer, ...); //non-blocking
        recv(sockClient[x], &overlap[x]);
      }
    }
    Thread_Recving()
    {
      while(bRunning)
      {
        for(i=0; i<10000; i++)
        {
          if(HasOverlappedIoCompleted(&overlap[i]))
            DoSomthing(i);
        }
      }
    }
    这种方式显然不可行,并发的时候,单个线程处理,如果DoSomthing(i)处理时间长,则很容易造成数据丢失未处理。
    这种情况下,比较好的方法是IOCP(Complete Port).IIS,FTP等许多网络服务器软件,都是采用这种模式。首先根据CPU数量来建立线程连接池,可以利用网卡DMA等特性,直接将网络数据写入内存,再动态调用线程来处理数据。

    WSAStartup(MAKEWORD(2, 2), &wsaData); // WinSock 2.2
    hCompPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

    sockSvr = socket(AF_INET, SOCK_STREAM, 0);
    bind(m_sockSvr, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR));
    listen(m_sockSvr, 10);

    CreateThread(Thread_Accepting, ...);
    GetSystemInfo(&sysInfo);
    for(i=0; i<sysInfo.dwNumberOfProcessors*2+2; i++)
      hThread[i] = CreateThread(NULL, 0, Thread_Recving, param, 0, NULL);

    Thread_Accepting()
    {
      while(bRunning)
      {
        sockClient[x] = accept(sockServer, ...); //non-blocking
        CreateIoCompletionPort(sockClient[x], hCompPort, param[x], 0); // 将IO对象sockClient[x]与hCompPort关联
        WSARecv(sockClient, wsaBuf, 1, &dwSize, &dwFlag, &overLapped, NULL); //non-blocking
      }
    }
    Thread_Recving() // 4*2+2=10个线程
    {
      while(bRunning)
      {
        GetQueuedCompletionStatus(hCompPort, &dwRecv, param, &pOverlap, INFINITE); //blocking
        DoSomthing(wsaBuf); // 数据已经接收进了内存
        WSARecv(sock, &wsaBuf, 1, &dwRecv, &dwFlag, &overLap, NULL); // continue recving
      }
    }
    这种方式的好处是,恰好利用了全部CPU的资源,因为启动太多线程没有意义,反而耗费资源进行线程切换。

  • 相关阅读:
    dojo学习
    在WindowsPhone中使用现有的Sqlite
    ArcGIS Runtime SDK for Windows Phone 入门教程
    HTML 相关技巧
    NetBeans 7安装Python 插件
    Tomcat
    MongoDB
    Ubuntu学习
    ArcGIS Server 10.1动态图层 以及Windows Phone/Silverlight客户端实现
    android应用崩溃的调试方法
  • 原文地址:https://www.cnblogs.com/chaos77/p/5544374.html
Copyright © 2020-2023  润新知