经历了两三年的沉寂之后,最新版IOCP_API网络编程库终于要来了。它沿袭了1.X版本的简洁设计和简单接口,并且提供更高的效率和更多的扩展功能。
最为关键的是——开源,我会毫无保留的奉送全部源码,coder们可以任意修改发布,而不需要支付一毛钱的版权。
你还在等什么,赶快来下载试用吧。
最新版IOCP_API为2.4版,相比较前面的版本(1.X版本),新版带来了如下好处:
1. 最新的API,数量只有十几个,涵盖了所有应用;也就是说,你仅仅通过这几个API,即可完成整个网络编程框架的搭建。简洁,方便是它与众不同之处。
2. 更高的效率;2.4版IOCP_API库经过优良的设计,使用一个IOCP核心(工作者线程组+内存池+业务线程池)即可完成所有的网络传输应用。
你可以再这个核心上,跑任意数量的TCP服务器,跑任意数量数量的TCP客户端,还可以跑任意数量的UDP服务,甚至你还可以将他们混合来使用。
这些都是在一个核心上完成的。不要羡慕,你已经拥有它了。
3. 更多的扩展功能; 除了传统的TCP和UDP外,2.4版IOCP_API库更是提供了Channel服务,让你无需担心大数据的拆包和组包业务,它完美的帮您处理了。
你认为只有这些?不!还有更多的扩展功能——P2P、UDT和RPC,你是不是已经迫不及待了?
4. 一定的可扩展性;相比1.X版,2.4版提供可一个可编程的内存池接口,您如果觉得我提供的缺省内存池不够完美,您大可以自己实现一个,是不是很棒?!
5. 更简单的数据处理接口;所有的数据都是通过一个接口通知您来处理,不管是传统的TCP和UDP数据包,还是扩展的Channel数据包,您只要关心您注册
的数据处理接口即可,其他的都交给2.4吧。去喝杯咖啡如何?
6. 更人性化和实用化的设计;您一定有过这样的经历,在一个异步网络架构的应用中,您因为一些特殊原因,需要一个同步接口,那是一件多么烦人的事情啊!
现在好了,有了2.4,这一切都解决了,它提供了RPC接口,可以满足您的这个要求。同时由于它是用的异步模拟的同步的方式,所以也无需担心它的效率问题。
7. 更多的实用工具;除了提供便捷的网络编程接口外,2.4还提供很多实用工具,比如一个DBPool(基于ADO组件)——让你随心所欲的使用数据库;
一个zlib压缩组件,一个FIFO队列等等,都是您编写网络程序必不可少的。
是不是很期待了?下面,我们来解开庐山真面路,走进IOCP_API。
网络通讯的原理,通过这些接口,您只要关心您的业务逻辑即可,其他的,都交给2.4吧。
与这些API相对应,在ISocket.h文件中,定义了相关的接口类,把相关功能都进行了封装,完全面向对象的编程方式,更适合传统的C++ coder们。
为了使您能够更加准确的了解此编程库的用法,在我提供的源码工程中,我还提供了一个Demo工程,它包含了这个编程库的所有主要功能,
当您需要问题时,可以查看并阅读此Demo,以便了解API的用法。
写在最后:
虽然IOCP_API经历了很多版本的演化,功能和稳定性都有了很不错的表现,但是我一个人的力量毕竟是有限的,设计场景也相对固定,测试环境也很简单,
难免会存在很多BUG,甚至在核心系统中。所以,在此我希望也恳请所有的coder们,在发现和(或者)修改一个BUG后,
能及时的通知我(email:shenyi0106@163.com, qq:52851771),以便让我能够及时更新最新到最新的代码中,感谢各位童鞋们。
下载地址: 点这里
最为关键的是——开源,我会毫无保留的奉送全部源码,coder们可以任意修改发布,而不需要支付一毛钱的版权。
你还在等什么,赶快来下载试用吧。
最新版IOCP_API为2.4版,相比较前面的版本(1.X版本),新版带来了如下好处:
1. 最新的API,数量只有十几个,涵盖了所有应用;也就是说,你仅仅通过这几个API,即可完成整个网络编程框架的搭建。简洁,方便是它与众不同之处。
2. 更高的效率;2.4版IOCP_API库经过优良的设计,使用一个IOCP核心(工作者线程组+内存池+业务线程池)即可完成所有的网络传输应用。
你可以再这个核心上,跑任意数量的TCP服务器,跑任意数量数量的TCP客户端,还可以跑任意数量的UDP服务,甚至你还可以将他们混合来使用。
这些都是在一个核心上完成的。不要羡慕,你已经拥有它了。
3. 更多的扩展功能; 除了传统的TCP和UDP外,2.4版IOCP_API库更是提供了Channel服务,让你无需担心大数据的拆包和组包业务,它完美的帮您处理了。
你认为只有这些?不!还有更多的扩展功能——P2P、UDT和RPC,你是不是已经迫不及待了?
4. 一定的可扩展性;相比1.X版,2.4版提供可一个可编程的内存池接口,您如果觉得我提供的缺省内存池不够完美,您大可以自己实现一个,是不是很棒?!
5. 更简单的数据处理接口;所有的数据都是通过一个接口通知您来处理,不管是传统的TCP和UDP数据包,还是扩展的Channel数据包,您只要关心您注册
的数据处理接口即可,其他的都交给2.4吧。去喝杯咖啡如何?
6. 更人性化和实用化的设计;您一定有过这样的经历,在一个异步网络架构的应用中,您因为一些特殊原因,需要一个同步接口,那是一件多么烦人的事情啊!
现在好了,有了2.4,这一切都解决了,它提供了RPC接口,可以满足您的这个要求。同时由于它是用的异步模拟的同步的方式,所以也无需担心它的效率问题。
7. 更多的实用工具;除了提供便捷的网络编程接口外,2.4还提供很多实用工具,比如一个DBPool(基于ADO组件)——让你随心所欲的使用数据库;
一个zlib压缩组件,一个FIFO队列等等,都是您编写网络程序必不可少的。
是不是很期待了?下面,我们来解开庐山真面路,走进IOCP_API。
在IOCP_API.h中定义的所有的编程库所提供接口API,下面是接口API列表:
IOCP_API HANDLE IOCPStartup(INT iWorkThread, INT iThreadPool);
IOCP_API RetCode IOCPCleanup(HANDLE hIocp);
IOCP_API ICache* IOCPCreateCache(INT iMinNum, INT iMaxNum);
IOCP_API RetCode IOCPDestroyCache(ICache *pCache);
IOCP_API TCPListener* IOCPCreateListener(HANDLE hIocp, INT iBindPort);
IOCP_API TCPSocket* IOCPCreateClient(HANDLE hIocp, INT iBindPort);
IOCP_API UDPSocket* IOCPCreateSocket(HANDLE hIocp, INT iBindPort);
IOCP_API RetCode IOCPDestorySocket(Socket* pSocket, SocketType sockType);
IOCP_API ADOPool* IOCPCreatePool(INT nMinPoolSize, INT nMaxPoolSize, const CHAR *pszConnStr);
IOCP_API RetCode IOCPDestoryPool(ADOPool *pAdoPool);
IOCP_API RetCode IOCPUnhandledException(IExceptionHandle *exception);
IOCP_API ILogger* IOCPCreateLogger(const CHAR *pszLogPath);
IOCP_API RetCode IOCPDestoryLogger(ILogger *pLogger);
IOCP_API IBuffer* IOCPCreateQueue(INT nCapacity);
IOCP_API RetCode IOCPDestoryQueue(IBuffer *pBuffer);
看到这些接口,是不是觉得如此高效的网络编程接口竟然如此简单?答案却是如此,就是如此简单。您不需要了解IOCP的原理,甚至都不需要了解
网络通讯的原理,通过这些接口,您只要关心您的业务逻辑即可,其他的,都交给2.4吧。
与这些API相对应,在ISocket.h文件中,定义了相关的接口类,把相关功能都进行了封装,完全面向对象的编程方式,更适合传统的C++ coder们。
//
// 返回值
//
enum RetCode
{
RET_ESUC = 0, //成功
RET_EFAILED = -1, //失败
RET_EEXIST = -2, //已存在
RET_ENOEXIST = -3, //不存在
RET_EARGS = -4, //参数错误
RET_ECALLAPI = -5, //函数调用错误
RET_ENOINIT = -6, //未初始化
RET_ENOMEM = -7, //内存不足
RET_ENOBUF = -8, //缓冲区不足
RET_ECHKSUM = -9, //效验和错误
RET_EUNFIN = -10, //未完成
RET_ECLOSE = -11, //已关闭
RET_ETIMEOUT = -12, //超时
RET_EDISCONN = -13, //断线
};
//
// Socket类型
//
enum SocketType
{
ST_NONE = 0,
ST_TCP = 1, //SOCK_STREAM
ST_UDP = 2, //SOCK_DGRAM
ST_LISTEN = 3, //TCP SERVER
};
//
// Socket基础类,TCPListener,TCPSocket和UDPSocket的的父类
//
class Socket
{
public:
Socket()
{
m_pSocketListener = NULL;
m_pCache = NULL;
m_hIocp = NULL;
m_socketType = ST_NONE;
m_hSocket = INVALID_SOCKET;
m_pLogger = NULL;
}
~Socket()
{
CloseSocket();
}
public:
//
// 根据指定类型创建TCP或者UDP Socket
//
RetCode CreateSocket(SocketType socketType)
{
INT opt = 0;
if (m_hSocket != INVALID_SOCKET)
{
return RET_EEXIST;
}
m_socketType = socketType;
if (m_socketType == ST_NONE)
{
return RET_EARGS;
}
if (m_socketType == ST_TCP)
{
opt = 0;
m_hSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
setsockopt(m_hSocket, SOL_SOCKET, SO_SNDBUF, (CHAR*)&opt, sizeof(INT));
setsockopt(m_hSocket, SOL_SOCKET, SO_RCVBUF, (CHAR*)&opt, sizeof(INT));
}
else
{
m_hSocket = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED);
opt = 1;
setsockopt(m_hSocket, SOL_SOCKET, SO_BROADCAST, (CHAR*)&opt, sizeof(INT));
opt = 64 * 1024;
setsockopt(m_hSocket, SOL_SOCKET, SO_SNDBUF, (CHAR*)&opt, sizeof(INT));
setsockopt(m_hSocket, SOL_SOCKET, SO_RCVBUF, (CHAR*)&opt, sizeof(INT));
}
return (m_hSocket != INVALID_SOCKET) ? RET_ESUC : RET_EFAILED;
}
//
//关闭Socket
//
RetCode CloseSocket(VOID)
{
if (m_hSocket != INVALID_SOCKET)
{
CancelIo((HANDLE)m_hSocket);
Sleep(500);
closesocket(m_hSocket);
Sleep(500);
m_socketType = ST_NONE;
m_hSocket = INVALID_SOCKET;
}
return RET_ESUC;
}
//
//将Socket绑定到指定端口
//
RetCode Bind(INT iPort)
{
SOCKADDR_IN bindAddr;
if (m_hSocket == INVALID_SOCKET)
{
return RET_ENOINIT;
}
//绑定本地端口
memset(&bindAddr,0,sizeof(SOCKADDR_IN));
bindAddr.sin_family = AF_INET;
bindAddr.sin_addr.s_addr = htonl(INADDR_ANY);
bindAddr.sin_port = htons(iPort);
if (bind(m_hSocket, (PSOCKADDR)&bindAddr, sizeof(bindAddr)) == SOCKET_ERROR)
{
CloseSocket();
return RET_ECALLAPI;
}
return RET_ESUC;
}
//
//开始监听客户端连接
//
RetCode Listen(VOID)
{
if (IsValidSocket() == FALSE)
{
return RET_ENOINIT;
}
if (m_socketType != ST_TCP)
{
return RET_EARGS;
}
if (listen(m_hSocket, SOMAXCONN) == SOCKET_ERROR)
{
CloseSocket();
return RET_ECALLAPI;
}
return RET_ESUC;
}
//
// 设置端口重用
//
RetCode ReUseAddr(BOOL bReUse)
{
if (IsValidSocket() == FALSE)
{
return RET_ENOINIT;
}
if (m_socketType != ST_TCP)
{
return RET_EARGS;
}
return
(setsockopt(Handle(), SOL_SOCKET, SO_REUSEADDR, (const char*)&bReUse, sizeof(BOOL)) != SOCKET_ERROR) ? RET_ESUC : RET_ECALLAPI;
}
//
// 设置Socket属性
//
RetCode SetSockOpt(INT nLevel, INT nOptName, const CHAR *pszOptVal, INT nOptLen)
{
INT nRet = 0;
if (IsValidSocket() == FALSE)
{
return RET_ENOINIT;
}
nRet = setsockopt(Handle(), nLevel, nOptName, pszOptVal, nOptLen);
return (nRet != SOCKET_ERROR) ? RET_ESUC : RET_ECALLAPI;
}
//
//获得Socket的句柄
//
SOCKET Handle(VOID) { return m_hSocket; }
//
//判断Socket是否有效
//
BOOL IsValidSocket(VOID) { return (m_hSocket != INVALID_SOCKET); }
//
//设置完成端口句柄
//
VOID IOCP(HANDLE hIocp) { m_hIocp = hIocp;}
//
//获取完成端口句柄
//
HANDLE IOCP() { return m_hIocp; }
//
//获取当前Socket的类型(TCP或者UDP)
//
SocketType ClientType(VOID) { return m_socketType;}
//
//设置内存池对象,用来为Socket内部分配内存
//
VOID Cache(ICache *pCache) { m_pCache = pCache; }
//
//获取内存池对象
//
ICache* Cache() {return m_pCache; }
//
//设置事件监听器
//
VOID SocketListener(ISocketListener *pSocketListener) { m_pSocketListener = pSocketListener; }
//
//获取事件监听器
//
ISocketListener* SocketListener(VOID) { return m_pSocketListener; }
//
// 设置日志对象
//
VOID Logger(ILogger *pLogger) { m_pLogger = pLogger; }
//
// 获取日志对象
//
ILogger* Logger(VOID) { return m_pLogger; }
protected:
ISocketListener *m_pSocketListener; //事件监听器对象
ICache *m_pCache; //内存池对象
HANDLE m_hIocp; //完成端口句柄
SocketType m_socketType; //当前Socket的类型
SOCKET m_hSocket; //当前Socket的句柄
ILogger *m_pLogger; //日志输出接口
};
//
// Socket操作选项
// 在ISocket的OnError时,提供当前Socket的什么操作
// 导致的错误发生
//
enum SocketOption
{
SO_ACCEPT = 1, // Acccept
SO_CONNECT = 2, // Connnect
SO_SEND = 3, // Send
SO_SENDTO = 4, // SendTo
SO_RECEIVE = 5, // Receive
SO_RECVFROM = 6, // ReceiveFrom
SO_UNKNOWN = 7, // Unknown
};
//
// 事件监听器接口类
//
class ISocketListener
{
public:
virtual ~ISocketListener() {}
public:
//
//当发生错误后,它会被调用;pClient是发生错的的Socket对象
//
//注意:
//底层框架会自动管理这个对象,并处理对应错误,
//这里通知调用者的目的是希望调用者去处理自己业务逻辑相关的变更
//
virtual RetCode OnError(Socket *pClient, SocketOption SockOpt, DWORD iErrorCode) = 0;
//
//当连接关闭后,它会被调用;pClient是关闭的Socket对象
//
//注意:
//底层框架会自动管理这个对象,
//这里通知调用者的目的是希望调用者去处理自己业务逻辑相关的变更
//
virtual RetCode OnClose(Socket *pClient) = 0;
//
//当连接成功后,它会被调用;pClient是连接成功的Socket对象
//
//注意:
//底层框架会自动管理这个对象,
//这里通知调用者的目的是希望调用者去处理自己业务逻辑相关的变更
//
virtual RetCode OnConnect(Socket *pClient) = 0;
//
//当数据发送成功后(TCP),它会被调用;pClient是发送数据的Socket对象
//iTotalLen要发送的长度, iLeftLen还剩下多少数据没发送
//
//注意:
//底层框架会自动管理这个对象,
//这里通知调用者的目的是希望调用者去处理自己业务逻辑相关的变更
//
//当连接上服务器(客户端)或者有客户端连接成功(服务器)时,此函数会被调用,
//或者每次发送完成后,此函数也会被触发;它表示缓冲区已空,可以发送数据;
//您可以忽略此函数的调用,直接调用Send去发送数据
//
virtual RetCode OnSend(Socket *pClient) = 0;
//
//当收到数据后(TCP),它会被调用;pClient是收到数据的Socket对象
//pData是收到的数据缓冲区, iLength收到的数据长度
//
//注意:
//底层框架会自动管理这个对象,
//这里通知调用者的目的是希望调用者去处理自己业务逻辑相关的变更
//
virtual RetCode OnReceive(Socket *pClient, const BYTE* pData, INT iLength) = 0;
//
//当数据发送成功后(UDP),它会被调用;pClient是发送数据的Socket对象
//iTotalLen要发送的长度, iLeftLen还剩下多少数据没发送, pToHost接收端地址
//
//注意:
//底层框架会自动管理这个对象,
//这里通知调用者的目的是希望调用者去处理自己业务逻辑相关的变更
//
//当UDP Socket创建成功后,此函数会被调用;或者SendTo成功后,也会调用此函数。
//它表示缓冲区已空,可以发送数据;
//您可以忽略此函数的调用,直接调用SendTo去发送数据
//
virtual RetCode OnSendTo(Socket *pClient) = 0;
//
//当收到数据后(UDP),它会被调用;pClient是收到数据的Socket对象
//pData是收到的数据缓冲区, iLength收到的数据长度, pFromHost数据的来源地址
//
//注意:
//底层框架会自动管理这个对象,
//这里通知调用者的目的是希望调用者去处理自己业务逻辑相关的变更
//
virtual RetCode OnReceiveFrom(Socket *pClient, const BYTE* pData, INT iLength, SOCKADDR *pFromHost) = 0;
};
//
//服务器事件监听器接口
//
class IServerListener : public ISocketListener
{
public:
virtual ~IServerListener() {}
public:
//
//有连接发生时,它会被调用;pListener是TCPListener对象,pAcceptSocket是连接的客户端对象
//
//注意:
//当返回值是RET_ESUC时,表示允许底层框架接受此连接,否则表示不允许.
//
//注意:
//底层框架会自动管理这个对象,
//这里通知调用者的目的是希望调用者去处理自己业务逻辑相关的变更
//
virtual RetCode OnAccept(Socket *pListener, Socket *pAcceptSocket) = 0;
public:
//针对于TcpListener,它是负责Accept客户端的
virtual RetCode OnConnect(Socket *pClient) { return RET_ESUC; }
virtual RetCode OnReceive(Socket *pClient, const BYTE* pData, INT iLength) { return RET_ESUC; }
virtual RetCode OnSend(Socket *pClient) { return RET_ESUC; }
virtual RetCode OnSendTo(Socket *pClient) { return RET_ESUC; }
virtual RetCode OnReceiveFrom(Socket *pClient, const BYTE* pData, INT iLength, SOCKADDR *pFromHost) { return RET_ESUC; }
};
//
// 内存池接口类
//
class ICache
{
public:
//
//创建内存池
//
virtual RetCode CreateCache(INT iMinVal, INT iMaxVal) = 0;
//
//销毁内存池
//
virtual RetCode DestoryCache(VOID) = 0;
//
//从内存池中分配内存
//iSize 要分配的内存大小(此参数保留,没有使用)
//
virtual PVOID CacheAlloc(INT iSize) = 0;
//
//将内存释放到内存池中
//pCache 内存指针, iSize 要分配的内存大小(此参数保留,没有使用)
//
virtual VOID CacheFree(PVOID pCache, INT iSize) = 0;
};
//
// TCP服务器类接口
//
class TCPListener : public Socket
{
public:
//
//启动TCP服务器对象
//
virtual RetCode StartListener(VOID) = 0;
//
//停止TCP服务器对象
//
virtual RetCode StopListener(VOID) = 0;
//
//获得当前所有活动客户端数量
//
virtual INT GetClientCount(VOID) = 0;
//
//枚举所有当前获得客户端对象
//
virtual RetCode EnumSockets(IEnumSocket *pEnumSocket, VOID *pArg) = 0;
public:
//
//设置服务器事件监听器
//
VOID ServerListener(IServerListener *pServerListener){ m_pServerListener = pServerListener; }
//
//获取服务器事件监听器
//
IServerListener* ServerListener(VOID) { return m_pServerListener;}
//
//设置最大可接入客户端数
//
VOID MaxAccept(INT iMaxAccept){ m_iMaxAccept = iMaxAccept; }
//
//获得当前最大可接入客户端数
//
INT MaxAccept(VOID) { return m_iMaxAccept; }
//
//设置客户端超时检测机制的检查间隔时间
//
VOID Timeout(INT iTimeout) { m_iTimeout = iTimeout; }
//
//获取客户端超时检测机制的检查间隔时间
INT Timeout(VOID) { return m_iTimeout; }
protected:
IServerListener *m_pServerListener; //服务器事件监听器
INT m_iMaxAccept, m_iTimeout; //可接入最大客户端数量和超时检查时间间隔
};
//
// TCPSocket类接口
//
class TCPSocket : public Socket
{
public:
//
//发送数据
//
virtual INT Send(const BYTE* pData, INT iLength) = 0;
//
//获取本地套接字信息
//pszIp为输出参数, 格式: IP:端口
//
virtual RetCode GetSocketName(CHAR *pszIp, INT nSize) = 0;
//
//获取对端套接字信息
//pszIp为输出参数, 格式: IP:端口
//
virtual RetCode GetPeerName(CHAR *pszIp, INT nSize) = 0;
//
//设置连接扩展(自定义)数据
//
virtual VOID SetExtData(PVOID pExtData) = 0;
//
//获取连接扩展(自定义)数据
//
virtual PVOID GetExtData(VOID) = 0;
//
//关闭连接(可重用关闭)
//
virtual RetCode Disconnect(VOID) = 0;
//
//连接服务器
//
virtual RetCode Connect(const CHAR *pszIp, INT iPort) = 0;
//
//获得会话数据指针
//
virtual PVOID GetSession(VOID) = 0;
//
//获取会话通道
//
virtual TCPChannel* GetChannel(VOID) = 0;
//
//获得链接关联的管理对象
//
virtual TCPListener* GetListener(VOID) = 0;
};
//
// 缓冲区
//
class DataBuf
{
public:
DataBuf(){ m_pData = NULL; m_nLen = 0; m_nSize = 0; }
public:
BYTE *m_pData; // 数据缓冲区指针
INT m_nLen; // 数据大小
INT m_nSize; // m_pData缓冲区大小
};
//
// IChannel接口(需要用户实现)
//
class IChannel
{
public:
//
// 创建一个IChannel缓冲区
//
virtual DataBuf* AllocBuffer(INT nSize) = 0;
//
// 销毁IChannel缓冲区
// RPC处理结束后,将自动调用此函数来释放使用的缓冲区
//
virtual VOID OnFreeBuffer(DataBuf *pData) = 0;
//
// 处理IChannel请求数据
// 当系统接收到RPC请求时,系统会自动调用此接口通知你去处理,pData是接收到的RPC数据, iLength是接收到的数据长度。
// 在此接口的实现中,完成你的处理逻辑;
// 1. 若你需要返回数据给IChannel请求者,请使用AllocBuffer函数来创建数据缓冲区,并且给你的缓冲区填充上数据,然后返回此缓冲区,系统会帮你完成发送数据的请求;
// 2. 若你仅仅是希望处理这个请求,但是没有要返回的数据,那么请直接返回NULL,系统会认为你不需要返回数据。
//
virtual DataBuf* OnHandle(const BYTE* pData, INT iLength) = 0;
};
//
// TCPChannel类是TCPSocket的扩展类,
// 提供TCP通道的扩展功能;
//
class TCPChannel
{
public:
//
// 扩展发送数据接口:可以发送带扩展头的数据包,最大可以一次性发送4M字节的数据
//
virtual INT SendEx(const BYTE* pData, INT iLength) = 0;
//
// 绑定IChannel接口
// 例如:客户端希望通过CallEx函数获取数据的话,那么服务器端的处理对象就需要实现IChannel接口,并且绑定到TCPChannel对象上
//
virtual BOOL BindEx(IChannel *pCallBack) = 0;
//
// RPC调用接口
// 该接口将同步等待对方返回或者超时;处理此调用的对象必须实现IChannel接口,并且通过Bind函数绑定到TCPChannel对象上
// iTimeout 等待超时时间,单位为毫秒;pOutData 输出缓冲区,需要用户指定;iOutLen 输入输出参数,表示缓冲区大小和接收到的数据大小
//
virtual INT CallEx(const BYTE* pInData, INT iInLen, INT iTimeout, BYTE *pOutData, INT *iOutLen) = 0;
};
//
// UDPSocket类接口
//
class UDPSocket : public Socket
{
public:
//
//初始化UDP套接字,并关联完成端口
//
virtual RetCode BindIo(VOID) = 0;
//
//发送数据
//
virtual INT SendTo(const BYTE* pData, INT iLength, const CHAR *pszIp, INT iPort) = 0;
virtual INT SendTo(const BYTE* pData, INT iLength, SOCKADDR_IN *pSockAddr) = 0;
//
//获取会话通道
//
virtual UDPChannel* GetChannel(VOID) = 0;
};
//
// UDT句柄
//
typedef UINT64 UDT_HANDLE;
//
// UDT无效句柄
//
#define UDT_INVALID_HANDLE ((UINT64)-1)
//
// UDPChannel类是UDPSocket的扩展类,
// 提供UDP通道的扩展功能;
// 该扩展类实现了UDT的部分功能,通过它你可以将UDP变身为可靠传输工具,
// 该扩展类和本通讯库中的的数据处理高度统一,收到可靠的数据后,仍将通过
// UDP相关的数据回调来通知调用者。
class UDPChannel
{
public:
//
// 阻塞发送一个P2P连接请求,直到对方响应或者超时
// 如果成功连接到对端,则返回一个UDTSocket句柄,否则返回-1
// iTimeout 单位是毫秒
//
virtual UDT_HANDLE Connect(const CHAR *pszDstIp, INT iDstPort, INT iTimeout) = 0;
//
// 当创建的对象不用时,需要调用此接口函数删除UDT对象
//
virtual RetCode CloseSocket(UDT_HANDLE hSocket) = 0;
//
// 发送UDT数据(可靠传输,通过超时重传的方式实现)
// 将数据拷贝到发送缓冲区中,直接返回。如果拷贝成功,则返回拷贝进去的字节数,否则返回错误码(RetCode)
//
virtual INT Send(UDT_HANDLE hSocket, const VOID *pData, INT nLen) = 0;
//
// 发送UDT数据(可靠传输,通过超时重传的方式实现)
// 将数据拷贝到发送缓冲区中,直到拷贝成功或者时间超时。如果拷贝成功,则返回拷贝进去的字节数,否则返回错误码(RetCode)
// nTimeout : 超时时间,单位为ms; -1表示直到拷贝成功或者断线
//
virtual INT Send(UDT_HANDLE hSocket, const VOID *pData, INT nLen, INT nTimeout) = 0;
//
// 获得UDT句柄属性(对端IP和端口)
//
virtual CHAR* GetSockAddr(UDT_HANDLE hSocket) = 0;
virtual INT GetSockPort(UDT_HANDLE hSocket) = 0;
};
//
// SEH异常处理回调接口
//
class IExceptionHandle
{
public:
//
//是否需要写Dump文件
//
virtual BOOL IsWriteDump(VOID) = 0;
//
//获得Dump文件的全路径名称
//
virtual CHAR* GetFileName(VOID) = 0;
//
//在Dump完成后,通知调用者需要做什么处理,并且需要返回值,具体返回值参数请看下面:
//EXCEPTION_EXECUTE_HANDLER = 1 表示我已经处理了异常,可以优雅地结束了
//EXCEPTION_CONTINUE_SEARCH = 0 表示我不处理,其他人来吧,于是windows调用默认的处理程序显示一个错误框,并结束
//EXCEPTION_CONTINUE_EXECUTION = -1 表示错误已经被修复,请从异常发生处继续执行
virtual LONG OnDumpComplete(VOID) = 0;
};
//
// 日志级别
//
enum LOG_LEVEL
{
LOG_NONE = 0,
LOG_INFO = 1,
LOG_WARN = 2,
LOG_ERROR = 3,
LOG_FETAL = 4,
};
//
// 日志输出选项
//
enum LOG_OPT
{
LOG_OPT_NONE = 0,
LOG_OPT_CONSOLE = 1,
LOG_OPT_FILE = 2,
LOG_OPT_ALL = 3,
};
//
// 一个日志输出类
//
class ILogger
{
public:
//
// 设置输出日志级别
//
virtual VOID SetLevel(LOG_LEVEL eLevel) = 0;
//
// 设置日志输出选项
//
virtual VOID SetOpt(LOG_OPT eOpt) = 0;
//
// 设置日志缓存队列的最大值
//
virtual VOID SetMaxCount(INT nMax) = 0;
//
// 输出Info级别的日志
//
virtual RetCode LogI(const char* format, ...) = 0;
//
// 输出Warn级别的日志
//
virtual RetCode LogW(const char* format, ...) = 0;
//
// 输出Error级别的日志
//
virtual RetCode LogE(const char* format, ...) = 0;
//
// 输出Fetal级别的日志
//
virtual RetCode LogF(const char* format, ...) = 0;
};
//
// 一个缓冲区接口
// 采用先进先出的方式
//
class IBuffer
{
public:
//
// 缓冲区是否已空
//
virtual BOOL IsEmpty(VOID) = 0;
//
// 缓冲区是否已满
//
virtual BOOL IsFull(VOID) = 0;
//
// 获取缓冲区中数据的长度
//
virtual INT Length(VOID) = 0;
//
// 获取缓冲区的容量
//
virtual INT Capacity(VOID) = 0;
//
// 将数据存入缓冲区中(同步)
// 返回已经写入的长度
//
virtual INT PutData(VOID *data, INT nLen) = 0;
//
// 从缓冲区中读取指定长度的内容到data中
// 不改变缓冲区数据内容
// 返回读取的长度
//
virtual INT FetchData(VOID *data, INT nSize) = 0;
//
// 从缓冲区中读取指定长度的内容到data中
// 清空已读取部分的内容
// 返回读取的长度
//
virtual INT GetData(VOID *data, INT nSize) = 0;
};
为了进一步方便c++ coder们的使用,我特意封装了一个C++调用类,在IOCP_API.hpp文件中:
#define CHECK_POINTE_RETURN(ptr, ret) if (ptr == NULL) return ret;
/*
类说明:
功能 : 一个简单包装IOCP_API函数的C++类,提供IOCP_API的所有功能的封装
备注 :
如果需要在C++环境中使用的话,可以包括这个头文件,并定义或者继承此类
作者 :沈毅(shenyi0106@163.com)
时间 :2014/01/26
*/
class XSocket
{
public:
XSocket()
{
m_hIocp = NULL;
m_pCache = NULL;
}
~XSocket()
{
UnInitialize();
}
public:
RetCode Initialize(INT iWorkerThread = 8, INT iThreadPool = 8, INT iMinCacheSize = 2000, INT iMaxCacheSize = 10000)
{
m_hIocp = IOCPStartup(iWorkerThread, iThreadPool);
CHECK_POINTE_RETURN(m_hIocp, RET_EFAILED);
m_pCache = IOCPCreateCache(iMinCacheSize, iMaxCacheSize);
CHECK_POINTE_RETURN(m_pCache, RET_EFAILED);
return RET_ESUC;
}
RetCode UnInitialize()
{
IOCPCleanup(m_hIocp);
IOCPDestroyCache(m_pCache);
return RET_ESUC;
}
TCPListener* CreateListener(INT iBindPort)
{
TCPListener *pListener = NULL;
CHECK_POINTE_RETURN(m_hIocp, NULL);
pListener = IOCPCreateListener(m_hIocp, iBindPort);
InitSocket(pListener);
return pListener;
}
TCPSocket* CreateClient(INT iBindPort = 0)
{
TCPSocket *pClient = NULL;
CHECK_POINTE_RETURN(m_hIocp, NULL);
pClient = IOCPCreateClient(m_hIocp, iBindPort);
InitSocket(pClient);
return pClient;
}
UDPSocket* CreateSocket(INT iBindPort)
{
UDPSocket *pSocket = NULL;
CHECK_POINTE_RETURN(m_hIocp, NULL);
pSocket = IOCPCreateSocket(m_hIocp, iBindPort);
InitSocket(pSocket);
return pSocket;
}
RetCode DestorySocket(Socket* pSocket, SocketType sockType)
{
CHECK_POINTE_RETURN(pSocket, RET_EFAILED);
return IOCPDestorySocket(pSocket, sockType);
}
private:
VOID InitSocket(Socket *pSocket)
{
if (pSocket != NULL)
{
pSocket->IOCP(m_hIocp);
pSocket->Cache(m_pCache);
}
}
public:
HANDLE m_hIocp;
ICache *m_pCache;
};
/*
类说明:
功能 : 一个简单工具集包装类
备注 :
如果需要在C++环境中使用的话,可以包括这个头文件,并定义或者继承此类
作者 :沈毅(shenyi0106@163.com)
时间 :2014/02/09
*/
class XUtil
{
public:
//
// 以下是几个两个示例数据库的连接字符串:
// MySQL : DRIVER={MySQL ODBC 5.1 Driver};SERVER=127.0.0.1;DATABASE=ckdz;UID=%s;PWD=%s;PORT=3306;OPTION=0;stmt=SET NAMES GBK
// Oracle : Provider=OraOLEDB.Oracle.1;Data Source=EQUANT;User ID=nyg;Password=nyg
// SQLServer: Provider=SQLOLEDB.1;Persist Security Info=true;User ID=sa;Password=123456;Initial Catalog=tempdb;Data Source=127.0.0.1
static ADOPool* CreatePool(INT nMinPoolSize, INT nMaxPoolSize, const CHAR *pszConnStr)
{
CHECK_POINTE_RETURN(pszConnStr, NULL);
return IOCPCreatePool(nMinPoolSize, nMaxPoolSize, pszConnStr);
}
static RetCode DestoryPool(ADOPool *pAdoPool)
{
CHECK_POINTE_RETURN(pAdoPool, RET_EARGS);
return IOCPDestoryPool(pAdoPool);
}
static BOOL Compress(CHAR *pInBuf, INT inLen, CHAR *pOutBuf, INT *outLen)
{
CHECK_POINTE_RETURN(pInBuf, RET_EARGS);
CHECK_POINTE_RETURN(pOutBuf, RET_EARGS);
return ZLIB_Compress(pInBuf, inLen, pOutBuf, outLen);
}
static BOOL Uncompress(CHAR *pInBuf, INT inLen, CHAR *pOutBuf, INT *outLen)
{
CHECK_POINTE_RETURN(pInBuf, RET_EARGS);
return ZLIB_UnCompress(pInBuf,inLen, pOutBuf, outLen);
}
static BOOL BaseEncode64(char * pInBuf,int nBufLen,char * pOutBuf)
{
CHECK_POINTE_RETURN(pInBuf, RET_EARGS);
CHECK_POINTE_RETURN(pOutBuf, RET_EARGS);
return Base64Encode(pInBuf, nBufLen, pOutBuf);
}
static BOOL BaseDencode64(char * pInBuf,int nBufLen,char * pOutBuf)
{
CHECK_POINTE_RETURN(pInBuf, RET_EARGS);
CHECK_POINTE_RETURN(pOutBuf, RET_EARGS);
return Base64Dencode(pInBuf, nBufLen, pOutBuf);
}
static RetCode UnhandledException(IExceptionHandle *exception)
{
CHECK_POINTE_RETURN(exception, RET_EARGS);
return IOCPUnhandledException(exception);
}
static ILogger* CreateLogger(char *pszFilePath)
{
CHECK_POINTE_RETURN(pszFilePath, NULL);
return IOCPCreateLogger(pszFilePath);
}
static RetCode DestoryLogger(ILogger *pLogger)
{
CHECK_POINTE_RETURN(pLogger, RET_EARGS);
return IOCPDestoryLogger(pLogger);
}
static IBuffer* CreateBuffer(INT nCapacity)
{
return IOCPCreateQueue(nCapacity);
}
static RetCode DestoryBuffer(IBuffer *pBuffer)
{
CHECK_POINTE_RETURN(pBuffer, RET_EARGS);
return IOCPDestoryQueue(pBuffer);
}
};
当您阅读完以上内容后,我们就等于是预览了IOCP_API 2.4版,下面您就可以开始使用此编程库编写您的"Hello World"了。
为了使您能够更加准确的了解此编程库的用法,在我提供的源码工程中,我还提供了一个Demo工程,它包含了这个编程库的所有主要功能,
当您需要问题时,可以查看并阅读此Demo,以便了解API的用法。
写在最后:
虽然IOCP_API经历了很多版本的演化,功能和稳定性都有了很不错的表现,但是我一个人的力量毕竟是有限的,设计场景也相对固定,测试环境也很简单,
难免会存在很多BUG,甚至在核心系统中。所以,在此我希望也恳请所有的coder们,在发现和(或者)修改一个BUG后,
能及时的通知我(email:shenyi0106@163.com, qq:52851771),以便让我能够及时更新最新到最新的代码中,感谢各位童鞋们。
下载地址: 点这里