基于VC开发epoll/linux 程序指南

2023-05-16

1       概述.... 3

2       背景.... 3

3       总体思路.... 3

4       功能特点.... 4

5       开发工具和操作系统要求.... 4

5.1       Windows/VC.. 4

5.2       linux/g++. 4

6       开发人员要求.... 5

7       Linux针对epoll移植.... 5

8       VC工程源码兼容性调整步骤和方案.... 5

8.1       准备移植文件... 5

8.2       宏定义... 5

8.1       调用初始化函数... 5

8.1       调用socket-recv()函数调整... 6

8.2       调用socket-send()函数调整... 7

8.3       调用socket-closesocket()函数调整... 7

9       本方案的限制.... 8

10         成功案例.... 8

11    epoll移植头文件.... 9

12         实用案例-端口映射源码.... 12

 

  1.  概述

本文是讲解日如何采用VC进行epoll编程,这是基于VC和windows API开发跨平台程序的重要组成部分,阅读本文之前需要先阅读《基于VC开发linux服务器程序指南》,文档网址:

https://blog.csdn.net/freeland008/article/details/107214767

 

  1.  背景

目前在linux下进行socket编程,一般使用select多路复用和epoll模式,

Windows和linux基于select模式开发,调用的函数类似。 windows不支持epoll,而只支持完成端口。要实现海量TCP并发访问必须采用epoll模式,为了实现在VC上进行epoll编程,进而移植到linux/g++,作者提供了相应的解决方案,具体参考下面章节。

 

  1.  总体思路

1、作者基于VC把select相关函数封装成epoll函数,具体参考作者提供的源码

文件。

 

2、开发人员就可以使用VC调用作者提供的epoll函数进行开发,相关epoll函

数的接口参数和返回值、功能兼容linux下的epoll函数。

 

3、因为不能完全兼容linux epoll,需要在VC的代码中加入部分兼容代码,采用

预定义指令,这些兼容代码只对VC有效,当移植到linux上是,这些兼容代

码不需要编译和执行。

 

4、VC工程在window上编译、调试、测试过后,就可以移植到linux/g++,epoll

部分代码可做到100%兼容。

 

说明:

1、作者会提供2个移植源码文件:EpollMng.cpp、EpollMng.h。

2、这2个文件只用于VC工程,移植到linux的源码是不需要这2个文件的。

 

  1.  功能特点

作者提供的epoll封装函数有以下特点

 

  1. 支持epoll的函数有epoll_create()、epoll_ctl()、epoll_wait()
  2. 支持epoll定义的LT和ET模式
  3. 支持listen()、accept()、connect()等socket函数。

4、 作者封装的epoll内部的数据结构采用了数组和avl树数据结构,性能优异。

    开发人员完全可以在windows下用epoll取代select进行开发。

5、 支持多次调用epoll_create(),生成多个epoll句柄,每个poller支持绑定多

个socket。

  1.  开发工具和操作系统要求
  2.  Windows/VC

 

1、建议采用/VC 2008及以上版本,同时安装Visual Assist X开发助手。

2、操作系统

   1)桌面系统采用64位Win7及以上版本。

   2)服务器系统采用Windows 2008x64及以上版本

3、其他

    如果只是开发32位程序,建议安装VC6,同时安装Visual Assist X开发助

    手。

 

  1.  linux/g++

作者开发选用RedHat和CentOS 的linux,如果开发人员希望在其他厂商的linux上开发,需要进行进一步的测试

 

  1.  开发人员要求
  2. 精通Socket编程,尤其是异步编程。
  3. 精通epoll的工作机制,具体可参考epoll的相关文档。

3、 熟悉VC++编程。

 

  1.  Linux针对epoll移植

1、只需去掉VC源码下的宏定义即可。

   例如:#define  PORT_OS_WIN32

 

  1.  VC工程源码兼容性调整步骤和方案

 

  1.  准备移植文件

作者提供了epoll封装源码文件为: EpollMng.cpp、EpollMng.h,开发人员把这2个文件加入到自己VC工程。

 

  1.  宏定义

需要在源码中新增一个宏定义:

 

#define    PORT_OS_WIN32

 

这个宏定义是限制某些epoll的兼容函数只运行在Windows的VC工程,在linux/g++下无效。

 

    1.  调用初始化函数

在调用任何epoll函数前,必须调用且只调用一次epoll的初始化函数,一般代码放置在程序初始化阶段,调用代码如下。

 

#ifdef PORT_OS_WIN32

       epoll_init();

#endif

 

这个函数只对VC工程有效,linux忽略这个函数。

 

    1.  调用socket-recv()函数调整

因为采用epoll编程,socket必须是异步,在调用socket的recv()函数接收数据时,需要做以下调整。示例代码如下:

 

       while(true)

         {                                            

                   char pBuf[1024*8];

                   int nRet;

                   nRet = recv(hSocket, pBuf, 8*1024, 0);

                   if(nRet == 0)

                   {

                            return -1;

                   }

                   else if(nRet == SOCKET_ERROR)

                   {

                            nRet = WSAGetLastError();

                            if(nRet == WSAEWOULDBLOCK)

                            {

#ifdef PORT_OS_WIN32

                                     epoll_setrecvnull(nPollerID, hSocket);

#endif

                                     return 0;

                            }      

                            return -1;

                   }      

         }      

 

其中红色代码部分是必须要加的,也就是说当recv()接收完socket缓冲区中所有的数据时,recv()函数时会返回阻塞(无数据)的错误码,这时必须执行epoll_setrecvnull函数,用来通知epoll封装类对象,已经无数据可读。红色代码只在VC工程有效,在linux/g++中不被编译。

 

以上加入的红色代码部分纯粹是为了让VC支持epoll接口的编程机制。

 

    1.  调用socket-send()函数调整

 

因为采用epoll编程,socket必须是异步,在调用socket的send()函数发送 数据时,需要做以下调整。示例代码如下:

 

       while(true)

         {                                            

                   char pBuf[1024*8];

                   int nRet;

                   nRet = send(hSocket, pBuf, 8*1024, MSG_NOSIGNAL);

                   if(nRet == 0)

                   {

                            return -1;

                   }

                   else if(nRet == SOCKET_ERROR)

                   {

                            nRet = WSAGetLastError();

                            if(nRet == WSAEWOULDBLOCK)

                            {

#ifdef PORT_OS_WIN32

                                     epoll_setsendnull (nPollerID, hSocket);

#endif

                                     return 0;

                            }      

                            return -1;

                   }      

         }      

 

其中红色代码部分是必须要加的,也就是说当socket缓冲区满,再调用send()时,send()函数时会返回阻塞(缓冲满)的错误码,这时必须执行epoll_ setsendnull函数,用来通知epoll封装类对象,socket不可写。红色代码只在VC工程有效,在linux/g++中不被编译。

 

以上加入的红色代码部分纯粹是为了让VC支持epoll接口的编程机制。

 

 

    1. 调用socket-closesocket()函数调整

当在VC工程中调用closesocket函数关闭socket(linux调用close())时,如果该socket之前绑定到某个epoll句柄,需要对closesocket()函数附近的相关代码做调整。示例代码如下:

 

#ifdef PORT_OS_WIN32

                     epoll_event pEvent;                                                       

                     epoll_ctl(nPollerID, EPOLL_CTL_DEL, hSocket, &pEvent);

#endif

                     closesocket(hSocket);

 

其中红色代码部分是必须要加的,当在VC工程中调用closesocket()关闭socket时,需要把socket从epoll句柄中解除绑定。linux不需要执行此函数,因为linux调用close()关闭socket时会自动解除绑定。

 

以上加入的红色代码部分纯粹是为了让VC支持epoll接口的编程机制。

 

 

  1.  本方案的限制

由于作者是采用select函数封装成的epoll接口,在执行性能上稍低于原始的

select模式。如果进行并发测试,TCP连接超过上千个,性能会下降,主要是受限于select本身的限制。如果要进行大量TCP并发连接测试,还是要在linux上进行测试。

 

  1.  成功案例

作者开发的所有服务器程序的网络编程都是采用epoll模式,先是在Windows的VC上进行开发、调试、测试,然后再把工程源码复制到linux上进行编译、运行、测试。

 

1、内存数据库系统

 

2、WebServer产品

 

  1. 短信网关

 

4、MQTT服务器系统

 

5、其他软件。

 

作者提供基于此方案的工程源码:TCP端口映射程序和WebServer程序。

 

  1.  epoll移植头文件

以下文件是在VC源码工程中实现的epoll封装类的头文件(EpollMng.h),部分代码是从linux的头文件复制过来。EpollMng.cpp文件请从相关网址下载,或向作者索取。

 

#ifndef EPOLLMNG_H

#define EPOLLMNG_H

 

#include <winsock2.h>

#include <windows.h>

 

/* Flags to be passed to epoll_create2.  */

enum

{

         EPOLL_CLOEXEC = 02000000,

#define EPOLL_CLOEXEC EPOLL_CLOEXEC

         EPOLL_NONBLOCK = 04000

#define EPOLL_NONBLOCK EPOLL_NONBLOCK

};

 

 

enum EPOLL_EVENTS

{

         EPOLLIN = 0x001,

#define EPOLLIN EPOLLIN

         EPOLLPRI = 0x002,

#define EPOLLPRI EPOLLPRI

         EPOLLOUT = 0x004,

#define EPOLLOUT EPOLLOUT

         EPOLLRDNORM = 0x040,

#define EPOLLRDNORM EPOLLRDNORM

         EPOLLRDBAND = 0x080,

#define EPOLLRDBAND EPOLLRDBAND

         EPOLLWRNORM = 0x100,

#define EPOLLWRNORM EPOLLWRNORM

         EPOLLWRBAND = 0x200,

#define EPOLLWRBAND EPOLLWRBAND

         EPOLLMSG = 0x400,

#define EPOLLMSG EPOLLMSG

         EPOLLERR = 0x008,

#define EPOLLERR EPOLLERR

         EPOLLHUP = 0x010,

#define EPOLLHUP EPOLLHUP

         EPOLLRDHUP = 0x2000,

#define EPOLLRDHUP EPOLLRDHUP

         EPOLLONESHOT = (1 << 30),

#define EPOLLONESHOT EPOLLONESHOT

         EPOLLET = (1 << 31)

#define EPOLLET EPOLLET

};

 

/* Valid opcodes ( "op" parameter ) to issue to epoll_ctl().  */

#define EPOLL_CTL_ADD 1        /* Add a file decriptor to the interface.  */

#define EPOLL_CTL_DEL 2         /* Remove a file decriptor from the interface.  */

#define EPOLL_CTL_MOD 3       /* Change file decriptor epoll_event structure.  */

 

typedef union epoll_data

{

         void *ptr;

         int fd;

         unsigned int/*uint32_t*/ u32;

         unsigned __int64/*uint64_t*/ u64;

} epoll_data_t;

 

struct epoll_event

{

         unsigned int/*uint32_t*/ events;      /* Epoll events */

         epoll_data_t data; /* User data variable */

};

//

struct epoll_event_ex

{

         epoll_event event;        

         bool bCanSend;

         bool bCanRecv;           

};

//

class CEpoller

{

public:

         int m_nPeerHashSize;

         epoll_event_ex** m_pPeerArray;  

         HANDLE m_hPollerTree;

         HANDLE m_hEventTree;

         fd_set m_pReadSet;

         fd_set m_pWriteSet;

         fd_set m_pErrorSet;

public:

         epoll_event* m_pEventArray;

         int m_nEventIndex;

public:

         CEpoller();

         ~CEpoller();

public:

         int Init(int nMaxSize);

         int Destroy();

         int AddEvent(SOCKET hSocket, epoll_event* ev);

         int RemoveEvent(SOCKET hSocket);

         int ModifyEvent(SOCKET hSocket, epoll_event* ev);

         epoll_event_ex* GetEvent(SOCKET hSocket);

         int WaitEvent(epoll_event* pEventArray, int nMaxEvents);

         int FillSocket(int nTimeOut);

         int SetSendNull(SOCKET hSocket);

         int SetRecvNull(SOCKET hSocket);       

};

 

class CEpollMng

{

public:

         CRITICAL_SECTION m_cri;

         int m_nUNID;    

         int m_nMaxPoller;

         CEpoller** m_pPollerArray;

public:

         CEpollMng();

         ~CEpollMng();

public:

         int Init();

         int Destroy();

public:

         int CreatePoller(int nMaxSize);      

         int ClosePoller(int nPollerID);                 

         CEpoller* GetPoller(int nPollerID);

 

         int AddEvent(int nPollerID, SOCKET hSocket, epoll_event* ev);

         int RemoveEvent(int nPollerID, SOCKET hSocket);

         int ModifyEvent(int nPollerID, SOCKET hSocket, epoll_event* ev);

         int WaitEvent(int nPollerID, epoll_event* pEventArray, int nMaxEvents, int nTimeOut);

 

         int SetSendNull(int nPollerID, SOCKET hSocket);

         int SetRecvNull(int nPollerID, SOCKET hSocket);

};

//

int epoll_wininit();

int epoll_setsendnull(int nPollerID, SOCKET hSocket);

int epoll_setrecvnull(int nPollerID, SOCKET hSocket);

//

int epoll_create(int nMaxSize);

int epoll_ctl(int nPollerID, int nActionID, SOCKET hSocket, epoll_event* pEvent);

int epoll_wait(int nPollerID, epoll_event* pEventArray, int nMaxEvents, int nTimeOut);

 

#endif

 

 

  1.  实用案例-端口映射源码

1、下图为端口映射的源码工程,可100%迁移到linux/g++编译运行。

 

端口映射源码工程-VC2008

2、作者可提供windowsx64和CentOSx64源码工程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于VC开发epoll/linux 程序指南 的相关文章

  • C - 公园坐椅子

    题意 xff1a SDUQD 旁边的滨海公园有 x 条长凳 第 i 个长凳上坐着 a i 个人 这时候又有 y 个人将来到公园 xff0c 他们将选择坐在某些公园中的长凳上 xff0c 那么当这 y 个人坐下后 xff0c 记k 61 所有
  • week12 hw 必做题1,2

    题意 xff1a 给出n个数 xff0c zjm想找出出现至少 n 43 1 2次的数 xff0c 现在需要你帮忙找出这个数是多少 xff1f Input 本题包含多组数据 xff1a 每组数据包含两行 第一行一个数字N 1 lt 61 N
  • week13 hw必做1,2

    题意 xff1a 这一天 xff0c TT 遇到了一个神秘人 神秘人给了两个数字 xff0c 分别表示 n 和 k xff0c 并要求 TT 给出 k 个奇偶性相同的正整数 xff0c 使得其和等于 n 例如 n 61 10 xff0c k
  • week14限时模拟 猫睡觉问题 HDU - 3700

    题意 xff1a 众所周知 xff0c TT家里有一只魔法喵 这只喵十分嗜睡 一睡就没有白天黑夜 喵喵一天可以睡多次 xff01 xff01 每次想睡多久就睡多久 喵睡觉的时段是连续的 xff0c 即一旦喵喵开始睡觉了 xff0c 就不能被
  • CSP M4 C 宇宙狗的危机

    题意 xff1a 描述 在瑞神大战宇宙射线中我们了解到了宇宙狗的厉害之处 xff0c 虽然宇宙狗凶神恶煞 xff0c 但是宇宙狗有一个很可爱的女朋友 最近 xff0c 他的女朋友得到了一些数 xff0c 同时 xff0c 她还很喜欢树 xf
  • csp 201809-3 元素选择器

    题意 xff1a 思路 xff1a 这道题的解决应该分为建树 43 查找两部分 关于建树 xff0c 为了方便后代选择器的查找 xff0c 采用儿子记录父节点的方法 xff0c 即每个节点记录自己的父节点位置 采用数组描述这棵树 xff0c
  • csp 201312-4有趣的数

    题意 xff1a 问题描述 我们把一个数称为有趣的 xff0c 当且仅当 xff1a 1 它的数字只包含0 1 2 3 xff0c 且这四个数字都出现过至少一次 2 所有的0都出现在所有的1之前 xff0c 而所有的2都出现在所有的3之前
  • C++20中的协程

    一 协程 在谷歌的Golang中 xff0c 如果大家说他的特点有啥 xff0c 肯定绕不过协程 而在此之前 xff0c 大多数的语言一般是从多进程讲到多线程 xff0c 一般来说 xff0c 对某个语言掌握的深度 xff0c 就看在多线程
  • RUST网络客户端的基本技术说明

    一 客户端的说明 上文中的网络客户端 xff0c 其实就是一个比较简单的TCP通信客户端 xff0c 原来为了实现和服务端的通信 xff0c 增加了相关的通信协议的相关内容 xff0c 在这里分析时 xff0c 可以忽略掉 xff0c 毕竟
  • 跟我学c++中级篇——再谈Concepts

    一 理解Concepts 可能很多的c 43 43 程序员到职业生涯结束 xff0c 都没有真正写过模板程序 xff0c 有一些甚至都没有听说过模板 这个很正常 xff0c 特别是一些参与c开发的c 43 43 程序员更是如此 不过 xff
  • win10软链接

    win10软链接 C gt mklink 创建符号链接 MKLINK D H J Link Target D 创建目录符号链接 默认为文件 符号链接 H 创建硬链接而非符号链接 J 创建目录联接 Link 指定新的符号链接名称 Target
  • Maven项目引用本地jar包依赖打包警告should not point at files within the project directory

    Maven项目引用本地jar包依赖打包警告Some problems were encountered while building the effective model for com xxx xxx xxx xxx jar shoul
  • make collect2: ld terminated with signal 9 错误解决办法

    make collect2 ld terminated with signal 9 错误解决办法 echo579 博客园 原因 xff1a signal 9 错误是由于交换区空间不足导致 xff0c 扩展交换区大小即可 解决方法搬运自Lin
  • 基于BFS的最短路径搜索[C++]

    基于C 43 43 实现BFS的最短路径搜索时 xff0c 可以使用STL中的优先队列priority queue xff0c 优先队列按照小顶堆 xff0c 即路径短的优先取出 其中节点可以用结构体定义 xff0c 结构体中存储节点的位置
  • c++ GUI轻量工具包FLTK介绍 (1)

    c 43 43 有许多gui开发工具 xff0c 比如MFC xff0c QT xff0c 而FLTK Fast Light ToolKit 读音fulltik xff0c 则是一个轻量级的 xff0c 简洁的gui开发库 fltk是跨平台
  • 一道题谈回溯

    CSDN话题挑战赛第1期 活动详情地址 xff1a https marketing csdn net p bb5081d88a77db8d6ef45bb7b6ef3d7f 参赛话题 xff1a Leetcode刷题指南 话题描述 xff1a
  • 戴尔服务器安装Debian11过程

    目录 物理戴尔服务器Debian11安装过程 以下皆为作者实操 转载注明出处 制作Debian ISO 镜像 U盘启动服务器进入启动项设置注意 在开机之前 请一定拔掉服务器网线 否则Debian安装会卡在 39 安装软件 39 这一步安装过
  • (转)Linux文件系统只读Read-only file system的快速解决方法

    xff08 原创地址 xff09 http www ha97 com 5428 html 问题描述 xff1a 上周公司的私有云 xff08 底层架构是Openstack 43 KVM xff0c 目前稳定性还不够好 xff0c 开发团队在
  • C语言中头文件写法

    在实现C语言模块化编程时 xff0c 通常会用到 h式的头文件的编写 xff0c 在此记录下头文件的基本写法 通常我们写C程序时 xff0c 都会直接使用这样的语句 include lt stdlib h gt 这便是我们通常说的头文件 头
  • 测试人员应该知道的Redis知识(六) Set

    一 概述 Redis 的 Set 是 String 类型的无序集合 集合成员是唯一的 xff0c 这就意味着集合中不能出现重复的数据 Redis 中集合是通过哈希表实现的 xff0c 所以添加 xff0c 删除 xff0c 查找的复杂度都是

随机推荐

  • OpenPCDet初级教程【自定义模型、loss】

    最近在研究点云物体检测 xff0c 基于OpenPCDet框架进行算法开发可以节约大量的重复性工作 xff0c 专心集中在核心算法的设计上 xff0c 大量节约时间 同时 xff0c 因为框架由大公司专业团队进行维护 xff0c 代码质量稳
  • NFS

    NFS Server apt install nfs kernel server nfs common y vim etc exports etc exports the access control list for filesystem
  • (深入理解计算机系统) bss段,data段、text段、堆(heap)和栈(stack)

    文章目录 bssdatatextheapstack总结例子 bss bss段 xff08 bss segment xff09 通常是指用来存放程序中未初始化的全局变量的一块内存区域 bss是英文Block Started by Symbol
  • linux学习43-HTTP服务和APACHE2

    HTTP服务和APACHE2 知识点 请求报文响应报文错误码请求重定向编译安装实现httpscurl工具 1 http协议 http协议版本 http 0 9 http 1 0 http 1 1 xff08 较多 xff09 http 2
  • Ubuntu20.4安装QT6

    前言 xff1a 本教程基于Ubuntu20 4 xff0c 在Ubuntu22 4上也测试过 Ubuntu18 04由于GCC版本太低 xff0c 无法正常工作 1 下载QT安装程序 xff1a Open Source Developme
  • sublime安装教程并配置C++环境

    sublime安装教程并配置C 43 43 环境 sublime安装教程并配置C 43 43 环境前言下载sublime配置C 43 43 环境 sublime安装教程并配置C 43 43 环境 前言 最近将电脑重新安装 xff0c 配置s
  • PHP call_user_func_array回调函数 call_user_func_array函数详解

    call user func array PHP官方 call user func array讲解 call user func array PHP 4 gt 61 4 0 4 PHP 5 PHP 7 call user func arra
  • MicroPython 链接WiFi ESP32连WiFi

    这里 gt gt gt gt MicroPython 教程写的非常好强烈推荐 import network import socket import time SSID 61 34 abc 34 修改为你的WiFi名称 PASSWORD 6
  • ESP32 arduino 天气显示 后台可控制 定时消息提示 图片提示

    ESP32 arduino 天气显示 后台可控制 定时消息提示 图片提示 后台操作界面 添加图片显示 添加文字轮播 用到的 H库 include lt WiFi h gt include lt ESPmDNS h gt include lt
  • python subprocess子进程

    import subprocess cmd lx 61 subprocess Popen cmd rtmp encoding 61 34 utf 8 34 shell 61 True sg SystemTray notify 39 开播成功
  • python 获取可用视频列表 和麦克风列表

    import re import subprocess cmd 61 39 ffmpeg 39 39 list devices 39 39 true 39 39 f 39 39 dshow 39 39 i 39 39 dummy 39 de
  • python ffmpeg直播客户端

    import PySimpleGUI as sg import sys import json import os import requests import subprocess import pygame camera import
  • 动物类的继承

    动物类的继承 要求 xff1a 1 在一个名为Test java的文件中定义四个类 xff1a 动物类Animal xff0c 狗类Dog和猫类Cat继承Animal xff0c 测试类Test xff0c 要求编写代码的同时编写简单注释
  • ffmpeg命令操作 合并视频 取图片帧数 获取音频

    ffmpeg安装 点击这里跳转 官方 wins安装的话要添加 环境变量转载点击这里 ffmpeg命令操作 合并视频 取图片帧数 1 获取视频内的图片 ffmpeg i input mp4 r 15 q v 2 f image2 img 04
  • JS 使用 lz-string存储 数据压缩

    浏览器localStorage存储为 5M 然而并不能满足我们的要求 我们可以压缩的是您可以存储的更多数据 好在JS 有lz string 库 引入库 lt script src 61 34 https cdn bootcss com lz
  • USB开发者模式 安卓 adb操作 + 安装

    USB开发者模式 安卓 adb操作 43 安装 这是adb 安装 xff01 xff01 xff01 xff01 xff01 xff01 xff01 xff01 xff01 xff01 xff01 xff01 不是gdb 安装 adb 下载
  • python控制 鼠标移动 pyautogui || PyMouse 自动化

    python控制 鼠标移动 pyautogui PyMouse 自动化 方法1 pyautogui 安装 pip install pyautogui文档基本操作指令 gui PAUSE 61 0 5 每次函数调用后暂停0 5秒 gui FA
  • PHP 图片去除水印 去除logo

    使用插件 ffmpeg 点这里 不使用插件 也可以 lt php function CLEAR ICO filename savename Clear W Clear H Clear X Clear Y filename 61 读取图片名
  • Apache Options指令详解

    Options指令是Apache配置文件中一个比较常见也比较重要的指令 xff0c Options指令可以在Apache服务器核心配置 server config 虚拟主机 配置 virtual host 特定目录配置 directory
  • 基于VC开发epoll/linux 程序指南

    1 概述 3 2 背景 3 3 总体思路 3 4 功能特点 4 5 开发工具和操作系统要求 4 5 1 Windows VC 4 5 2 linux g 43 43 4 6 开发人员要求 5 7 Linux针对epoll移植 5 8 VC工