hp-socket-tcp-&-udp-通信框架开发指南-(v4.0-20161001)


高性能 TCP & UDP 通信框架 —— HP-Socket v4.0-20161001 Bruce Liang 1 前 言 HP-Socket 是一套通用的高性能 TCP/UDP/HTTP 通信框架,包含服务端组件、 客户端组件和 Agent 组件,广泛适用于各种不同应用场景的 TCP/UDP/HTTP 通 信系统,提供 C/C++、C#、Delphi、E(易语言)、Java、Python 等编程语言接口。 HP-Socket 对通信层完全封装,应用程序不必关注通信层的任何细节;HP-Socket 提供基于事件通知模型的 API 接口,能非常简单高效地整合到新旧应用程序中。 为了让使用者能方便快速地学习和使用 HP-Socket,迅速掌握框架的设计思 想和使用方法,特此精心制作了大量 Demo 示例(如:PUSH 模型示例、PULL 模型示例、PACK 模型示例、性能测试示例以及其它编程语言示例)。HP-Socket 目前运行在 Windows 平台,将来会实现跨平台支持。  通用性  HP-Socket 的唯一职责就是接收和发送字节流,不参与应用程序的协议解析等工作。  HP-Socket 与应用程序通过接口进行交互,并完全解耦。任何应用只要实现了 HP-Socket 的接口规范都可以无缝整合 HP-Socket。  易用性  易用性对所有通用框架都是至关重要的,如果太难用还不如自己从头写一个来得方 便。因此,HP-Socket 的接口设计得非常简单和统一。  HP-Socket 完全封装了所有底层通信细节,应用程序不必也不能干预底层通信操作。 通信连接被抽象为 Connection ID,Connection ID 作为连接的唯一标识提供给应用 程序来处理不同的连接。  HP-Socket 提供 PUSH / PULL / PACK 等接收模型,应用程序可以灵活选择以手工 方式、半自动方式或全自动方式处理封解包,PULL / PACK 接收模型在降低封解包 处理复杂度的同时能大大减少出错几率。  高性能  Client 组件:基于 Event Select 通信模型,在单独线程中执行通信操作,避免与主 线程或其他线程相互干扰。每个组件对象管理一个 Socket 连接。  Server 组件:基于 IOCP 通信模型,并结合缓存池、私有堆(Private Heap)等技术, 支持超大规模连接,在高并发场景下实现高效内存管理。  Agent 组件:对于代理服务器或中转服务器等应用场景,服务器自身也作为客户端 向其它服务器发起大规模连接,一个 Agent 组件对象同时可管理多个 Socket 连接; Agent 组件与 Server 组件采用相同的技术架构,可以用作代理服务器或中转服务器 的客户端部件。  伸缩性 应用程序可以根据不同的容量要求、通信规模和资源状况等现实场景调整 HP-Socket 的 各项性能参数(如:工作线程的数量、缓存池的大小、发送模式和接收模式等),优化资源 配置,在满足应用需求的同时不必过度浪费资源。 2 目 录 前 言................................................................................................................................................. 1 1 概 述 ......................................................................................................................................... 3 1.1 整体架构 ................................................................................................................... 3 1.2 组件分类 ................................................................................................................... 5 1.3 组件接口 ................................................................................................................... 6 1.4 监听器接口 ............................................................................................................... 8 2 框架详述 ................................................................................................................................. 11 2.1 关键概念 ................................................................................................................. 11 2.1.1 接收模型 ......................................................................................................... 11 2.1.2 发送策略 ......................................................................................................... 14 2.1.3 接收策略 ......................................................................................................... 14 2.1.4 连接方式 ......................................................................................................... 15 2.1.5 连接绑定 ......................................................................................................... 16 2.2 Server 组件 ............................................................................................................. 19 2.2.1 接口描述 ......................................................................................................... 19 2.2.2 工作流程 ......................................................................................................... 22 2.3 Agent 组件 .............................................................................................................. 24 2.3.1 接口描述 ......................................................................................................... 24 2.3.2 工作流程 ......................................................................................................... 27 2.4 Client 组件 .............................................................................................................. 28 2.4.1 接口描述 ......................................................................................................... 28 2.4.2 工作流程 ......................................................................................................... 31 3 SSL 组件 ................................................................................................................................. 33 3.1 组件接口 ................................................................................................................. 33 3.2 SSL 运行环境 ......................................................................................................... 35 4 HTTP 组件 .............................................................................................................................. 37 4.1 组件接口 ................................................................................................................. 37 4.2 HTTP 监听器事件 .................................................................................................. 39 5 使用方式 ................................................................................................................................. 43 5.1 源代码 ..................................................................................................................... 43 5.2 静态库 ..................................................................................................................... 43 5.3 HPSocket DLL ........................................................................................................ 43 5.4 HPSocket4C DLL ................................................................................................... 44 5.5 其它编程语言使用 HPSocket ................................................................................ 45 6 附 录 ....................................................................................................................................... 46 6.1 示例 Demo .............................................................................................................. 46 6.2 FAQ ......................................................................................................................... 48 3 1 概 述 1.1 整体架构 HP-Socket 完全封装了底层通信细节,并为应用程序提供一套简单易用的并且与底层通 信完全无关的 API 接口,使应用程序获得高性能、高伸缩性通信的同时,免除处理通信细 节的负担。HP-Socket 的 API 接口模型如图 1.1-1 所示: 图 1.1-1 HP-Socket API 接口模型 HP-Socket 定义了组件接口(如:ITcpServer / IUdpClient)、 组件实现类(如:CTcpServer / CUdpClient)和监听器接口(如:ITcpServerListener / IUdpClientListener),其中:  组件接口:声明组件操作方法,应用程序创建组件对象后通过该接口来使用组件  组件实现类:实现组件接口,执行实际通信处理工作,并向监听器报告通信事件  监听器接口:声明组件的通信事件回调方法 每个组件对象都会关联一个监听器对象(监听器对象的实现类由应用程序定义),当组 件对象触发一个通信事件时会调用监听器对象相应的回调方法,应用程序在回调方法中处理 应用业务逻辑。图 1.1-2 以 TCP Agent 为例展示了组件与应用程序的交互: 应用程序首先创建监听器对象和 TCP Agent 对象,创建 TCP Agent 对象时传入监听器对 象,把 TCP Agent 对象与监听器对象关联起来。TCP Agent 对象创建完毕后,应用程序调用 TCP Agent 接口方法操作 TCP Agent 对象(如:Start / Connect / Send / Stop 等)。当 TCP Agent 对象触发通信事件时,会调用监听器对象的回调方法(如:OnConnect / OnSend / OnReceive / Onclose 等)通知应用程序。 注意:监听器对象的异步回调方法是在组件的通信线程中执行的,因此回调方法不应 执行耗时较长的业务逻辑代码,同时要注意多线程同步问题,也应尽量避免使用锁。 HP-Socekt 通过设置“接收策略”和“Connection Extra”能协助应用程序巧妙地避免 多线程同步和锁导致的复杂性和性能问题。 HP-Socket Component Interface Listener Interface Component Implement Class 1 My Application Listener Implement Class Logic Controller 4 图 1.1-2 TCP Agent 与应用程序交互示例 My Logic Controller My TCP Agent Listener TCP Agent ITcpAgentListener := new() <> Create Agent & Listener Start Agent Connect Operation Send Operation OnPrepareConnect Callback OnConnect Callback OnSend Callback OnReceive Callback OnClose Callback Stop Operation OnShutdown Callback listener ITcpAgent := new(listener) <> agent Start() Connect(address, port) OnPrepareConnect(connid) OnConnect(connid) OnHandshake(connid) OnHandshake Callback Send(connid, data, length) OnSend(connid, data, length) OnReceive(connid, data, length) OnClose(connid, operation, code) Stop() OnShutdown() 5 1.2 组件分类 HP-Socket 包含 27 个组件(其中 9 个 SSL 组件将在第 3 章详细讲解,6 个 HTTP 组件将 在第 4 章详细讲解),可根据通信角色(Client / Server)、通信协议(TCP / UDP/HTTP)和 接收模型(PUSH / PULL / PACK)进行归类。表 1.2-1 列出了所有组件的名称、接口、监听 器接口、实现类及其分类: 名 称 组件接口 监听器接口 实现类 通信 角色 通信 协议 接收 模型 TCP Server ITcpServer ITcpServerListener CTcpServer Server TCP PUSH TCP Pull Server ITcpPullServer ITcpServerListener CTcpPullServer Server TCP PULL TCP Pack Server ITcpPackServer ITcpServerListener CTcpPackServer Server TCP PACK UDP Server IUdpServer IUdpServerListener CUdpServer Server UDP PUSH TCP Agent ITcpAgent ITcpServerListener CTcpAgent Client TCP PUSH TCP Pull Agent ITcpPullAgent ITcpAgentListener CTcpPullAgent Client TCP PULL TCP Pack Agent ITcpPackAgent ITcpServerListener CTcpPackAgent Client TCP PACK TCP Client ITcpClient ITcpClientListener CTcpClient Client TCP PUSH TCP Pull Client ITcpPullClient ITcpClientListener CTcpPullClient Client TCP PULL TCP Pack Client ITcpPackClient ITcpClientListener CTcpPackClient Client TCP PACK UDP Client IUdpClient IUdpClientListener CUdpClient Client UDP PUSH UDP Cast IUdpCast IUdpCastListener CUdpCast Client UDP PUSH 表 1.2-1 组件分类  Agent 组件本质上是 Client 组件,一个 Agent 对象能同时管理多个客户端连接  根据实际使用场景,HP-Socket 中只实现了基于 TCP 和 HTTP Agent 组件  Cast 组件是为组播和广播而设计的 UDP 组件,可认为是一种特殊的 Client 组件  基于 TCP 的组件都分别提供 PUSH 和 PULL 两种接收模型 6 1.3 组件接口 Server、Agent 和 Client 的组件接口定义如图 1.3-1 — 1.3-3 所示,组件接口定义了组件 提供的所有操作方法。其中,PULL 模型接口多重继承于 IPullSocket / IPullCliet 接口,它提 供了 Fetch(dwConnID, pData, iDataLength) 方法,让应用程序从组件中拉取数据。 图 1.3-1 Server 组件接口 IUdpServer +SetMaxDatagramSize(dwMaxDatagramSize: DWORD): void +GetMaxDatagramSize(): DWORD +SetPostReceiveCount(dwPostReceiveCount: DWORD): void +GetPostReceiveCount(): DWORD +SetDetectAttempts(dwDetectAttempts: DWORD): void +SetDetectInterval(dwDetectInterval: DWORD): void +GetDetectAttempts(): DWORD +GetDetectInterval(): DWORD ITcpPullServer +ToPull(pServer: IServer): IPullSocket +ToServer(pPullSocket: IPullSocket): ITcpServer ITcpServer +SendSmallFile(dwConnID: CONNID, lpszFileName: LPCTSTR, pHead: LPWSABUF, pTail: LPWSABUF): BOOL +SetAcceptSocketCount(dwAcceptSocketCount: DWORD): void +SetSocketBufferSize(dwSocketBufferSize: DWORD): void +SetSocketListenQueue(dwSocketListenQueue: DWORD): void +SetKeepAliveTime(dwKeepAliveTime: DWORD): void +SetKeepAliveInterval(dwKeepAliveInterval: DWORD): void +GetAcceptSocketCount(): DWORD +GetSocketBufferSize(): DWORD +GetSocketListenQueue(): DWORD +GetKeepAliveTime(): DWORD +GetKeepAliveInterval(): DWORD IServer +Start(lpszBindAddress: LPCTSTR, usPort: USHORT): BOOL +GetListenAddress(lpszAddress: TCHAR, iAddressLen: int, usPort: USHORT): BOOL IComplexSocket +Stop(): BOOL +Send(dwConnID: CONNID, pBuffer: BYTE, iLength: int, iOffset: int): BOOL +SendPackets(dwConnID: CONNID, pBuffers: WSABUF, iCount: int): BOOL +Disconnect(dwConnID: CONNID, bForce: BOOL): BOOL +DisconnectLongConnections(dwPeriod: DWORD, bForce: BOOL): BOOL +DisconnectSilenceConnections(dwPeriod: DWORD, bForce: BOOL): BOOL +SetConnectionExtra(dwConnID: CONNID, pExtra: PVOID): BOOL +GetConnectionExtra(dwConnID: CONNID, ppExtra: PVOID): BOOL +HasStarted(): BOOL +GetState(): EnServiceState +GetMaxConnectionCount(): DWORD +SetMaxConnectionCount(dwMaxConnectionCount: DWORD): void +GetConnectionCount(): DWORD +GetAllConnectionIDs(pIDs: CONNID, dwCount: DWORD): BOOL +GetConnectPeriod(dwConnID: CONNID, dwPeriod: DWORD): BOOL +GetSilencePeriod(dwConnID: CONNID, dwPeriod: DWORD): BOOL +GetLocalAddress(dwConnID: CONNID, lpszAddress: TCHAR, iAddressLen: int, usPort: USHORT): BOOL +GetRemoteAddress(dwConnID: CONNID, lpszAddress: TCHAR, iAddressLen: int, usPort: USHORT): BOOL +GetLastError(): EnSocketError +GetLastErrorDesc(): LPCTSTR +GetPendingDataLength(dwConnID: CONNID, iPending: int): BOOL +SetSendPolicy(enSendPolicy: EnSendPolicy): void +SetMarkSilence(bMarkSilence: BOOL): void +IsMarkSilence(): BOOL +SetFreeSocketObjLockTime(dwFreeSocketObjLockTime: DWORD): void +SetFreeSocketObjPool(dwFreeSocketObjPool: DWORD): void +SetFreeBufferObjPool(dwFreeBufferObjPool: DWORD): void +SetFreeSocketObjHold(dwFreeSocketObjHold: DWORD): void +SetFreeBufferObjHold(dwFreeBufferObjHold: DWORD): void +SetWorkerThreadCount(dwWorkerThreadCount: DWORD): void +GetSendPolicy(): EnSendPolicy +GetFreeSocketObjLockTime(): DWORD +GetFreeSocketObjPool(): DWORD +GetFreeBufferObjPool(): DWORD +GetFreeSocketObjHold(): DWORD +GetFreeBufferObjHold(): DWORD +GetWorkerThreadCount(): DWORD <>-IComplexSocket() IPullSocket +Fetch(dwConnID: CONNID, pData: BYTE, iLength: int): EnFetchResult +Peek(dwConnID: CONNID, pData: BYTE, iLength: int): EnFetchResult <>-IPullSocket() IPackSocket +SetMaxPackSize(dwMaxPackSize: DWORD): void +SetPackHeaderFlag(usPackHeaderFlag: USHORT): void +GetMaxPackSize(): DWORD +GetPackHeaderFlag(): USHORT <>-IPackSocket() ITcpPackServer +ToPack(pServer: IServer): IPackSocket +ToServer(pPackSocket: IPackSocket): ITcpServer 7 图 1.3-2 Agent 组件接口 IComplexSocket +Stop(): BOOL +Send(dwConnID: CONNID, pBuffer: BYTE, iLength: int, iOffset: int): BOOL +SendPackets(dwConnID: CONNID, pBuffers: WSABUF, iCount: int): BOOL +Disconnect(dwConnID: CONNID, bForce: BOOL): BOOL +DisconnectLongConnections(dwPeriod: DWORD, bForce: BOOL): BOOL +DisconnectSilenceConnections(dwPeriod: DWORD, bForce: BOOL): BOOL +SetConnectionExtra(dwConnID: CONNID, pExtra: PVOID): BOOL +GetConnectionExtra(dwConnID: CONNID, ppExtra: PVOID): BOOL +HasStarted(): BOOL +GetState(): EnServiceState +GetMaxConnectionCount(): DWORD +SetMaxConnectionCount(dwMaxConnectionCount: DWORD): void +GetConnectionCount(): DWORD +GetAllConnectionIDs(pIDs: CONNID, dwCount: DWORD): BOOL +GetConnectPeriod(dwConnID: CONNID, dwPeriod: DWORD): BOOL +GetSilencePeriod(dwConnID: CONNID, dwPeriod: DWORD): BOOL +GetLocalAddress(dwConnID: CONNID, lpszAddress: TCHAR, iAddressLen: int, usPort: USHORT): BOOL +GetRemoteAddress(dwConnID: CONNID, lpszAddress: TCHAR, iAddressLen: int, usPort: USHORT): BOOL +GetLastError(): EnSocketError +GetLastErrorDesc(): LPCTSTR +GetPendingDataLength(dwConnID: CONNID, iPending: int): BOOL +SetSendPolicy(enSendPolicy: EnSendPolicy): void +SetMarkSilence(bMarkSilence: BOOL): void +IsMarkSilence(): BOOL +SetFreeSocketObjLockTime(dwFreeSocketObjLockTime: DWORD): void +SetFreeSocketObjPool(dwFreeSocketObjPool: DWORD): void +SetFreeBufferObjPool(dwFreeBufferObjPool: DWORD): void +SetFreeSocketObjHold(dwFreeSocketObjHold: DWORD): void +SetFreeBufferObjHold(dwFreeBufferObjHold: DWORD): void +SetWorkerThreadCount(dwWorkerThreadCount: DWORD): void +GetSendPolicy(): EnSendPolicy +GetFreeSocketObjLockTime(): DWORD +GetFreeSocketObjPool(): DWORD +GetFreeBufferObjPool(): DWORD +GetFreeSocketObjHold(): DWORD +GetFreeBufferObjHold(): DWORD +GetWorkerThreadCount(): DWORD <>-IComplexSocket() IAgent +Start(lpszBindAddress: LPCTSTR, bAsyncConnect: BOOL): BOOL +Connect(pszRemoteAddress: LPCTSTR, usPort: USHORT, pdwConnID: CONNID): BOOL ITcpAgent +SendSmallFile(dwConnID: CONNID, lpszFileName: LPCTSTR, pHead: LPWSABUF, pTail: LPWSABUF): BOOL +SetReuseAddress(bReuseAddress: BOOL): void +IsReuseAddress(): BOOL +SetSocketBufferSize(dwSocketBufferSize: DWORD): void +SetKeepAliveTime(dwKeepAliveTime: DWORD): void +SetKeepAliveInterval(dwKeepAliveInterval: DWORD): void +GetSocketBufferSize(): DWORD +GetKeepAliveTime(): DWORD +GetKeepAliveInterval(): DWORD IPullSocket +Fetch(dwConnID: CONNID, pData: BYTE, iLength: int): EnFetchResult +Peek(dwConnID: CONNID, pData: BYTE, iLength: int): EnFetchResult <>-IPullSocket() ITcpPullAgent +ToPull(pAgent: IAgent): IPullSocket +ToAgent(pPullSocket: IPullSocket): ITcpAgent IPackSocket +SetMaxPackSize(dwMaxPackSize: DWORD): void +SetPackHeaderFlag(usPackHeaderFlag: USHORT): void +GetMaxPackSize(): DWORD +GetPackHeaderFlag(): USHORT <>-IPackSocket() ITcpPackAgent +ToPack(pAgent: IAgent): IPackSocket +ToAgent(pPackSocket: IPackSocket): ITcpAgent 8 图 1.3-3 Client 组件接口 1.4 监听器接口 Server、Agent 和 Client 的监听器接口定义如图 1.4-1 — 1.4-3 所示: 图 1.4-1 Server 监听器接口 IClient +Start(lpszRemoteAddress: LPCTSTR, usPort: USHORT, bAsyncConnect: BOOL, lpszBindAddress: LPCTSTR): BOOL +Stop(): BOOL +Send(pBuffer: BYTE, iLength: int, iOffset: int): BOOL +SendPackets(pBuffers: WSABUF, iCount: int): BOOL +SetExtra(pExtra: PVOID): void +GetExtra(): PVOID +HasStarted(): BOOL +GetState(): EnServiceState +GetLastError(): EnSocketError +GetLastErrorDesc(): LPCTSTR +GetConnectionID(): CONNID +GetLocalAddress(lpszAddress: TCHAR, iAddressLen: int, usPort: USHORT): BOOL +GetPendingDataLength(iPending: int): BOOL +SetFreeBufferPoolSize(dwFreeBufferPoolSize: DWORD): void +SetFreeBufferPoolHold(dwFreeBufferPoolHold: DWORD): void +GetFreeBufferPoolSize(): DWORD +GetFreeBufferPoolHold(): DWORD <>-IClient() ITcpClient +SendSmallFile(lpszFileName: LPCTSTR, pHead: LPWSABUF, pTail: LPWSABUF): BOOL +SetSocketBufferSize(dwSocketBufferSize: DWORD): void +SetKeepAliveTime(dwKeepAliveTime: DWORD): void +SetKeepAliveInterval(dwKeepAliveInterval: DWORD): void +GetSocketBufferSize(): DWORD +GetKeepAliveTime(): DWORD +GetKeepAliveInterval(): DWORD IUdpClient +SetMaxDatagramSize(dwMaxDatagramSize: DWORD): void +GetMaxDatagramSize(): DWORD +SetDetectAttempts(dwDetectAttempts: DWORD): void +SetDetectInterval(dwDetectInterval: DWORD): void +GetDetectAttempts(): DWORD +GetDetectInterval(): DWORD IUdpCast +SetMaxDatagramSize(dwMaxDatagramSize: DWORD): void +GetMaxDatagramSize(): DWORD +SetReuseAddress(bReuseAddress: BOOL): void +IsReuseAddress(): BOOL +SetCastMode(enCastMode: EnCastMode): void +GetCastMode(): EnCastMode +SetMultiCastTtl(iMCTtl: int): void +GetMultiCastTtl(): int +SetMultiCastLoop(bMCLoop: BOOL): void +IsMultiCastLoop(): BOOL +GetRemoteAddress(lpszAddress: TCHAR, iAddressLen: int, usPort: USHORT): BOOL IPullClient +Fetch(pData: BYTE, iLength: int): EnFetchResult +Peek(pData: BYTE, iLength: int): EnFetchResult <>-IPullClient() ITcpPullClient +ToPull(pClient: IClient): IPullClient +ToClient(pPullClient: IPullClient): ITcpClient IPackClient +SetMaxPackSize(dwMaxPackSize: DWORD): void +SetPackHeaderFlag(usPackHeaderFlag: USHORT): void +GetMaxPackSize(): DWORD +GetPackHeaderFlag(): USHORT <>-IPackClient() ITcpPackClient +ToPack(pClient: IClient): IPackClient +ToClient(pPackClient: IPackClient): ITcpClient IComplexSocketListenerT +OnShutdown(pSender: T): EnHandleResult IServerListenerT +OnPrepareListen(pSender: T, soListen: SOCKET): EnHandleResult +OnAccept(pSender: T, dwConnID: CONNID, soClient: SOCKET): EnHandleResult IUdpServerListenerITcpServerListener ISocketListenerT +OnHandShake(pSender: ITcpClient, dwConnID: CONNID): EnHandleResult +OnSend(pSender: T, dwConnID: CONNID, pData: BYTE, iLength: int): EnHandleResult +OnReceive(pSender: T, dwConnID: CONNID, pData: BYTE, iLength: int): EnHandleResult +OnReceive(pSender: T, dwConnID: CONNID, iLength: int): EnHandleResult +OnClose(pSender: T, dwConnID: CONNID, enOperation: EnSocketOperation, iErrorCode: int): EnHandleResult <>-ISocketListenerT() 9 图 1.4-2 Agent 监听器接口 图 1.4-3 Client 监听器接口 IComplexSocketListenerT +OnShutdown(pSender: T): EnHandleResult IAgentListenerT +OnPrepareConnect(pSender: T, dwConnID: CONNID, socket: SOCKET): EnHandleResult +OnConnect(pSender: T, dwConnID: CONNID): EnHandleResult ITcpAgentListener ISocketListenerT +OnHandShake(pSender: ITcpClient, dwConnID: CONNID): EnHandleResult +OnSend(pSender: T, dwConnID: CONNID, pData: BYTE, iLength: int): EnHandleResult +OnReceive(pSender: T, dwConnID: CONNID, pData: BYTE, iLength: int): EnHandleResult +OnReceive(pSender: T, dwConnID: CONNID, iLength: int): EnHandleResult +OnClose(pSender: T, dwConnID: CONNID, enOperation: EnSocketOperation, iErrorCode: int): EnHandleResult <>-ISocketListenerT() IClientListenerT +OnPrepareConnect(pSender: T, dwConnID: CONNID, socket: SOCKET): EnHandleResult +OnConnect(pSender: T, dwConnID: CONNID): EnHandleResult ITcpClientListener IUdpCastListenerIUdpClientListener ISocketListenerT +OnHandShake(pSender: ITcpClient, dwConnID: CONNID): EnHandleResult +OnSend(pSender: T, dwConnID: CONNID, pData: BYTE, iLength: int): EnHandleResult +OnReceive(pSender: T, dwConnID: CONNID, pData: BYTE, iLength: int): EnHandleResult +OnReceive(pSender: T, dwConnID: CONNID, iLength: int): EnHandleResult +OnClose(pSender: T, dwConnID: CONNID, enOperation: EnSocketOperation, iErrorCode: int): EnHandleResult <>-ISocketListenerT() 10 HP-Socket 没有为 PUSH / PULL / PACK 模型组件定义单独的监听器接口,它们使用相 同的监听器接口,区别在于:PUSH 和 PACK 模型组件接收到数据时会触发监听器对象的 OnReceive(pSender, dwConnID, pData, iLength) 事件,而 PULL 模型组件接收到数据时会触 发监听器对象的 OnReceive(pSender, dwConnID, iLength) 事件。事件的含义如表 1.4-1 所示: 接 口 事 件 说 明 ISocketListenerT OnHandShake(pSender, dwConnID, pData, iLength) 握手完成 OnSend(pSender, dwConnID, pData, iLength) 数据已发送 OnReceive(pSender, dwConnID, pData, iLength) 数据到达 (PUSH) OnReceive(pSender, dwConnID, iLength) 数据到达 (PULL) OnClose(pSender, dwConnID, enOperation, iErrorCode) 连接关闭 IComplexSocketListenerT OnShutdown(pSender) 关闭组件 IServerListenerT OnPrepareListen(pSender, soListen) 准备监听 OnAccept(pSender, dwConnID, soClient) 接受连接 IAgentListenerT OnPrepareConnect(pSender, dwConnID, socket) 准备连接 OnConnect(pSender, dwConnID) 完成连接 IClientListenerT OnPrepareConnect(pSender, dwConnID, socket) 准备连接 OnConnect(pSender, dwConnID) 完成连接 表 1.4-1 监听器接口事件 监听器事件回调方法返回值的类型为 EnHandleResult:  HR_OK :成功处理  HR_IGNORE :忽略处理  HR_ERROR :处理失败 注意:当 OnReceive / OnPrepareListen / OnAccept / OnPrepareConnect / OnConnect / OnHandShake 事件回调方法返回 HR_ERROR 时,组件会立即中断连接。 为了使非 SSL 组件和 SSL 组件的处理流程一致,HP-Socket v4.0.x 开始,非 SSL 组件 也会触发 OnHandShake 事件。因此,当组件接收到 OnHandShake 事件即说明连接已建立, 并可以开始通信。 EnHandleResult <> +HR_OK +HR_IGNORE +HR_ERROR 11 2 框架详述 2.1 关键概念 2.1.1 接收模型 如表 1.2-1 所示,HP-Socket 的 TCP 组件支持 PUSH、PULL 和 PACK 三种接收模型:  PUSH 模型:组件接收到数据时会触发监听器对象的 OnReceive(pSender, dwConnID, pData, iLength) 事件,把数据“推”给应用程序。  PULL 模型:组件接收到数据时会触发监听器对象的 OnReceive(pSender, dwConnID, iTotalLength) 事件,告诉应用程序当前已经接收到多少数据,应用程序检查数据的 长度,如果满足需要则调用组件的 Fetch(dwConnID, pData, iDataLength) 方法把需 要的数据“拉”出来。  PACK 模型:PACK 模型系列组件是 PUSH 和 PULL 模型的结合体,应用程序不 必处理分包(如:PUSH)与数据抓取(如:PULL),组件保证每个 OnReceive 事 件都向应用程序提供一个完整数据包。 三种模型的比较如图 2.1.1-1 所示: 12 通信组件 TcpServer/TcpClient/TcpAgent UdpServer/UdpClient/UdpCast 应用程序 HP-Socket OnReceive(pSender, dwConnID, pData, iLength) PUSH 模型 通信组件 TcpPullServer/TcpPullClient/TcpPullAgent 应用程序 HP-Socket OnReceive(pSender, dwConnID, iTotalLength) PULL 模型 Fetch(dwConnID, pData, iDataLength) 通信组件 TcpPackServer/TcpPackClient/TcpPackAgent 应用程序 HP-Socket OnReceive(pSender, dwConnID, pData, iLength) PACK 模型 每个 OnReceive()事件都返回一个完整数据包 图 2.1.1-1 接收模型 13  PUSH 模型组件触发监听器对象的 OnReceive(pSender, dwConnID, pData, iLength) 事件时,应用程序需要立即处理接收到的数据,如:粘包处理、协议解析等。组件 不会对应用层的数据处理工作提供任何协助。  PULL 模型组件触发监听器对象的 OnReceive(pSender, dwConnID, iTotalLength) 事 件时,应用程序根据应用层协议检测接收到的数据长度(iTotalLength)是否满足处 理条件,选择性地进行处理。当 iTotalLength 小于当前期望的长度时可以忽略本次 事 件 ; 当 iTotalLength 大于或等于当前期望的长度时, 循 环 调 用 组 件 的 Fetch(dwConnID, pData, iDataLength) 方法把需要的数据拉取出来,直到剩余的数 据长度小于当前期望的长度。 Fetch(dwConnID, pData, iDataLength) 方法返回值的类型为 EnFetchResult:  FR_OK :成功拉取  HR_LENGTH_TOO_LONG :拉取的长度超过实际数据长度  HR_DATA_NOT_FOUND :没有数据可拉取,可能连接已被关闭 注意:只有当 Fetch(dwConnID, pData, iDataLength)方法返回 FR_OK 时,数据才会被 拉取出来。另外,PULL 模型组件还提供 Peek(dwConnID, pData, iDataLength)方法用于窥 探接收缓冲区,该方法不会移除缓冲区数据。 PULL 模型适用于完全清楚应用层协议,并且应用层协议可以根据当前数据包得知下一 个数据包长度的场景。典型的场景如 Head + Body,Head 长度固定,第一个数据包为 Head, 通过 Head 得知 Body 的长度,接收完 Body 之后下一个数据包一定为 Head。 注意:通过 PULL 模型与应用层协议的相互配合,使得应用程序可以免除粘包处理和 分拆包工作,从而减少应用程序的负担。  PACK 模型组件触发监听器对象的 OnReceive(pSender, dwConnID, pData, iLength) 事件时,会保证 pData 是一个完整的数据包。PACK 模型组件会对应用程序发送的 每个数据包自动加上 4 字节(32 位)的包头,组件接收到数据时根据包头信息自 动分包,每个完整数据包通过 OnReceive 事件发送给应用程序。 PACK 包头格式: XXXXXXXXXX YYYYYYYYYYYYYYYYYYYYYY EnFetchResult <> +FR_OK +FR_LENGTH_TOO_LONG +FR_DATA_NOT_FOUND 14 前 10 位 X 为包头标识位,用于数据包校验。有效包头标识取值范围 0 ~ 1023(0x3FF), 当包头标识等于 0 时不校验包头。后 22 位 Y 为长度位,记录包体长度。有效数据包最大 长度不能超过 4194303(0x3FFFFF)字节,默认长度限制为:262144(0x40000)字节。应 用程序可以通过 SetPackHeaderFlag() 和 SetMaxPackSize() 分别设置包头标识与最大包长限 制。 2.1.2 发送策略 对于 IClient 系列组件,当应用程序调用组件的 Send()、SendPackets()、SendSmallFile() 方 法发送数据时,组件内部会把数据缓存起来,在适当的时机再发送出去。 对于 IServer 和 IAgent 系列组件,当应用程序调用组件的 Send()、SendPackets()、 SendSmallFile() 方法发送数据时,根据不同的发送策略会有不同的处理方式。 (发送策略通过 SetSendPolicy(enSendPolicy) 方法进行设置)  SP_PACK :打包策略(默认) 尽量把多个发送操作的数据组合在一起发送,增加传输效率。  SP_SAFE :安全策略 尽量把多个发送操作的数据组合在一起发送,并尽量避免缓冲区溢出。  SP_DIRECT :直接策略 对每一个发送操作都直接投递,适用于负载不高但要求实时性较高的场合。 注:SP_DIRECT 通常与 TCP_NODELAY Socket 选项配合使用,从而达到获 得最低延时,TCP_NODELAY Socket 选项可以通过 HP-Socket 导出函数 SYS_SSO_NoDelay()设置。 对于 SP_PACK 和 SP_SAFE 策略,组件内部会缓存待发送的数据,应用程序可以调用 组件的 GetPendingDataLength(dwConnID, iPending) 方法获取指定连接的未发出数据量,实 现流量控制;对于 SP_DIRECT 策略,组件不会缓存待发送数据,如果要实现流量控制则需 要 比 较 组件通过 Send() 、 SendPackets() 、 SendSmallFile() 方 法 提 交 的 数 据 量 与 OnSend(pSender, dwConnID, pData, iLength) 事件报告的实际发出的数据量。 2.1.3 接收策略 对于 IClient 系列组件,每个组件对象都在单独的通信线程执行所有通信工作,OnReceive 与 Onclose 事件不可能同时触发,因此不必担心在处理 OnReceive 事件时某些共享数据被 Onclose 事件处理代码意外修改或释放的情形。 对于 IServer 和 IAgent 系列组件,由于有多个通信线程同时工作,对于同一连接,在触 EnSendPolicy <> +SP_PACK +SP_SAFE +SP_DIRECT 15 发 OnReceive 事件时可能同时触发了 Onclos 事件,因此,共享数据存在被意外修改或释放 的可能。为了应对这个问题,HP-Socket v3.3 及其之前的版本提供了以下两种接收策略(通 过 SetRecvPolicy(enRecvPolicy) 方法设置):  RP_SERIAL :串行策略(默认) 对于单个连接,顺序触发 OnReceive 和 OnClose 等事件。降低应用程序处 理的复杂度,增强安全性;但同时损失一些并发性能。  RP_PARALLEL :并行策略 对于单个连接,同时收到 OnReceive 和 OnClose 事件时,会在不同的通信 线程中同时触发这些事件,使并发性能得到提升,但应用程序需要考虑在 OnReceive 的事件处理代码中,某些公共数据可能被 OnClose 的事件处理 代码修改或释放的情形,程序代码逻辑会变得复杂,处理不好时将会产生 代码缺陷。除非有充足的理由并且完全能避免 RP_PARALLEL 策略所带来 的隐患,否则不建议应用程序使用 RP_PARALLEL 策略。 HP-Socket v3.4 版本开始,废弃了 RP_PARALLEL 接收策略。HP-Socket 保证除了 OnSend 事件以外(因为 OnSend 事件通常用处不大)其它所有事件都是线程安全的。 注意:对于 HP-Socket 的所有组件(IServer / IAgent / IClient),当连接触发了 OnClose 事件时,表示连接已被关闭。并且 OnClose 事件只会触发一次,也就是说:同一个连接不 可能收到两个或多个 OnClose 事件。 HP-Socket v3.3 及其之前版本中提供了 OnClose 和 OnError 两个事件,分别表示正常关 闭和异常关闭连接。HP-Socket v3.4 开始,这两个事件已合并为一个事件,应用程序可根据 OnClose 事件的 iErrorCode 参数判断是正常关闭还是异常关闭。 2.1.4 连接方式 HP-Socket 所有组件的通信过程都是异步的,如:调用组件的 Send() 方法会立即返回, 稍后监听器会接收到 OnSend() 事件获知发送了多少数据,或者会接收到 OnClose() 事件可 获知发送失败原因。 但 HP-Socket 的 IClient 和 IAgent 组件向服务器发起连接的过程可以是同步或异步的。 同步是指组件的连接方法(IClient - Start(),IAgent - Connect())等到建立连接成功或失败了 再返回(返回 TRUE 或 FALSE)。 异步连接是指组件的连接方法Start() / Connect() 会立即返回,如果Start() / Connect() 返 回成功(TRUE)则稍后会接收到 OnConnect() 或 OnClose() 事件,收到前者则说明连接成 功,收到后者则说明连接失败。注意:如果 Start() / Connect() 返回失败(FALSE)则稍后 不一定能接收到 OnClose() 事件。因此,对于异步连接也必须检查 Start() / Connect() 的返回 值,当返回失败(FALSE)则立即可以断定连接失败。 EnRecvPolicy <> +RP_SERIAL +RP_PARALLEL 16  IClient 建立连接方法: BOOL Start(lpszRemoteAddress, usPort, bAsyncConnect = TRUE, lpszBindAddress = nullptr) 参数 bAsyncConnect 指示是否采用异步连接方式(默认:TRUE),如果 Start()方法返回 失败可以调用组件的 GetLastError() 和 GetLastErrorDesc() 方法获取错误代码和错误描述。 如果 Start()方法返回成功可以调用组件的 GetConnectionID() 方 法 获 取 当 前 连 接 的 Connection ID。 注意:IUdpCast 组件的 Star()方法忽略 bAsyncConnect 参数。  IAgent 建立连接方法: BOOL Start(lpszBindAddress = nullptr, bAsyncConnect = TRUE) BOOL Connect(lpszRemoteAddress, usPort, pdwConnID = nullptr) Start() 方法启动 IAgent 组件并指定连接方式,参数 bAsyncConnect 指示是否采用异步 连接方式(默认:TRUE),如果 Start() 方法返回失败可以调用组件的 GetLastError() 和 GetLastErrorDesc() 方法获取错误代码和错误描述。注意:Start() 方法在整个通信周期中只 需调用 1 次。 Connect() 方法与指定服务器建立连接,参数 pdwConnID 用来获取本连接的 Connection ID(默认:nullptr,不获取),如果 Connect() 方法返回失败可以调用 Windows API 函 数 ::GetLastError() 获取 Windows 错误代码。 无论是同步或异步连接,成功完成连接的过程中都会先后触发监听器的两个事件:  OnPrepareConnect(pSender, dwConnID, socket)  OnConnect pSender, dwConnID) 其中 OnPrepareConnect(pSender, dwConnID, socket) 在发起连接前触发,socket 是本地 SOCKET 句柄,可以在该事件中通过 setsockopt() / WSAIoctl() 等方法设置 SOCKET 选项。 OnConnect(pSender, dwConnID) 则在连接建立成功后触发。 2.1.5 连接绑定 对于 IClient 系列组件,一个组件对象对应一个 Connection ID 和一个通信连接,因此很 容易把通信连接与应用层数据关联起来。应用程序与组件交互时,直接通知组件处理数据即 可(如:Send( pData, iLength))。如图 2.1.5-1 所示: 17 图 2.1.5-1 IClient 组件连接示意图 注意:对于 IClient 组件,可以通过 SetExtra() / GetExtra() 方法绑定、获取附加数据。 对于 IServer 和 IAgent 系列组件,一个组件对象管理多个通信连接,HP-Socket 把通信 连接抽象为 Connection ID,应用程序与组件交互时,需要指定 Connection ID 来告知组件处 理哪个连接(如:Send(dwConnID, pData, iLength) )。如图 2.1.5-2 示: 图 2.1.5-2 IServer/IAgent 组件连接示意图 应用程序为了建立 Connection ID 与应用层数据的对应关系通常需要维护一张映射表 (如:map),从而不但增加了应用程序的负担;另外,由于运行 在多线程环境下,对映射表的读写操作需要进行同步处理,从而降低了应用程序的并发性能。 HP-Socket 为 IServer 和 IAgent 系列组件提供以下方法组绑定 Connection ID 和应用层数 据,尽量避免让应用程序维护映射表。 18  BOOL SetConnectionExtra(CONNID dwConnID, PVOID pExtra)  BOOL GetConnectionExtra(CONNID dwConnID, PVOID* ppExtra) 通常的应用情景如下: 1) 在 OnAccept() / OnConnect() 事件中调用 SetConnectionExtra(dwConnID, pExtra) 把 Connection ID 和应用层数据进行绑定。 2) 在 OnReceive() / OnSend() 事件中调用 GetConnectionExtra(dwConnID, ppExtra) 取出与 Connection ID 绑定的应用层数据,执行相应业务逻辑处理。 3) 在 OnClose() 事件中取消 Connection ID 和应用层数据的绑定,清除应用层数据 并释放资源。 注意:由于 HP-Socket 已经确保了 OnReceive() / OnClose() 等事件的线程安全,因此应 用程序可以放心使用连接绑定机制,不用担心同步问题。 19 2.2 Server 组件 2.2.1 接口描述 图 2.2.1-1 Server 组件接口 IUdpServer +SetMaxDatagramSize(dwMaxDatagramSize: DWORD): void +GetMaxDatagramSize(): DWORD +SetPostReceiveCount(dwPostReceiveCount: DWORD): void +GetPostReceiveCount(): DWORD +SetDetectAttempts(dwDetectAttempts: DWORD): void +SetDetectInterval(dwDetectInterval: DWORD): void +GetDetectAttempts(): DWORD +GetDetectInterval(): DWORD ITcpPullServer +ToPull(pServer: IServer): IPullSocket +ToServer(pPullSocket: IPullSocket): ITcpServer ITcpServer +SendSmallFile(dwConnID: CONNID, lpszFileName: LPCTSTR, pHead: LPWSABUF, pTail: LPWSABUF): BOOL +SetAcceptSocketCount(dwAcceptSocketCount: DWORD): void +SetSocketBufferSize(dwSocketBufferSize: DWORD): void +SetSocketListenQueue(dwSocketListenQueue: DWORD): void +SetKeepAliveTime(dwKeepAliveTime: DWORD): void +SetKeepAliveInterval(dwKeepAliveInterval: DWORD): void +GetAcceptSocketCount(): DWORD +GetSocketBufferSize(): DWORD +GetSocketListenQueue(): DWORD +GetKeepAliveTime(): DWORD +GetKeepAliveInterval(): DWORD IServer +Start(lpszBindAddress: LPCTSTR, usPort: USHORT): BOOL +GetListenAddress(lpszAddress: TCHAR, iAddressLen: int, usPort: USHORT): BOOL IComplexSocket +Stop(): BOOL +Send(dwConnID: CONNID, pBuffer: BYTE, iLength: int, iOffset: int): BOOL +SendPackets(dwConnID: CONNID, pBuffers: WSABUF, iCount: int): BOOL +Disconnect(dwConnID: CONNID, bForce: BOOL): BOOL +DisconnectLongConnections(dwPeriod: DWORD, bForce: BOOL): BOOL +DisconnectSilenceConnections(dwPeriod: DWORD, bForce: BOOL): BOOL +SetConnectionExtra(dwConnID: CONNID, pExtra: PVOID): BOOL +GetConnectionExtra(dwConnID: CONNID, ppExtra: PVOID): BOOL +HasStarted(): BOOL +GetState(): EnServiceState +GetMaxConnectionCount(): DWORD +SetMaxConnectionCount(dwMaxConnectionCount: DWORD): void +GetConnectionCount(): DWORD +GetAllConnectionIDs(pIDs: CONNID, dwCount: DWORD): BOOL +GetConnectPeriod(dwConnID: CONNID, dwPeriod: DWORD): BOOL +GetSilencePeriod(dwConnID: CONNID, dwPeriod: DWORD): BOOL +GetLocalAddress(dwConnID: CONNID, lpszAddress: TCHAR, iAddressLen: int, usPort: USHORT): BOOL +GetRemoteAddress(dwConnID: CONNID, lpszAddress: TCHAR, iAddressLen: int, usPort: USHORT): BOOL +GetLastError(): EnSocketError +GetLastErrorDesc(): LPCTSTR +GetPendingDataLength(dwConnID: CONNID, iPending: int): BOOL +SetSendPolicy(enSendPolicy: EnSendPolicy): void +SetMarkSilence(bMarkSilence: BOOL): void +IsMarkSilence(): BOOL +SetFreeSocketObjLockTime(dwFreeSocketObjLockTime: DWORD): void +SetFreeSocketObjPool(dwFreeSocketObjPool: DWORD): void +SetFreeBufferObjPool(dwFreeBufferObjPool: DWORD): void +SetFreeSocketObjHold(dwFreeSocketObjHold: DWORD): void +SetFreeBufferObjHold(dwFreeBufferObjHold: DWORD): void +SetWorkerThreadCount(dwWorkerThreadCount: DWORD): void +GetSendPolicy(): EnSendPolicy +GetFreeSocketObjLockTime(): DWORD +GetFreeSocketObjPool(): DWORD +GetFreeBufferObjPool(): DWORD +GetFreeSocketObjHold(): DWORD +GetFreeBufferObjHold(): DWORD +GetWorkerThreadCount(): DWORD <>-IComplexSocket() IPullSocket +Fetch(dwConnID: CONNID, pData: BYTE, iLength: int): EnFetchResult +Peek(dwConnID: CONNID, pData: BYTE, iLength: int): EnFetchResult <>-IPullSocket() IPackSocket +SetMaxPackSize(dwMaxPackSize: DWORD): void +SetPackHeaderFlag(usPackHeaderFlag: USHORT): void +GetMaxPackSize(): DWORD +GetPackHeaderFlag(): USHORT <>-IPackSocket() ITcpPackServer +ToPack(pServer: IServer): IPackSocket +ToServer(pPackSocket: IPackSocket): ITcpServer 20 图 2.2.1-2 Server 监听器接口 Server 组件接口的继承层次结构如图 2.2.1-1 所示,其中,ITcpServer 和 IUdpServer 继承 于 IServer,ITcpPullServer 和 ITcpPackServer 则继承于 ITcpServer。主要接口方法如表 2.2.1-1 所示,其它接口方法请参考 Src/SocketInterface.h 文件的相关注释: 组件接口 操作方法 描 述 IServer Start() 启动组件 Stop() 关闭组件 Send() 发送数据 SendPackets() 发送多组数据 Disconnect() 断开连接 DisconnectLongConnections() 断开长连接 DisconnectSilenceConnections() 断开静默连接 HasStarted() 检查通信组件是否已启动 GetState() 获取通信组件当前状态 GetConnectionCount() 获取连接数 GetConnectPeriod() 获取连接时长 GetSilencePeriod() 获取静默时长 GetAllConnectionIDs() 获取所有连接的 CONNID GetLocalAddress() 获取某个连接的本地地址 IComplexSocketListenerT +OnShutdown(pSender: T): EnHandleResult IServerListenerT +OnPrepareListen(pSender: T, soListen: SOCKET): EnHandleResult +OnAccept(pSender: T, dwConnID: CONNID, soClient: SOCKET): EnHandleResult IUdpServerListenerITcpServerListener ISocketListenerT +OnHandShake(pSender: ITcpClient, dwConnID: CONNID): EnHandleResult +OnSend(pSender: T, dwConnID: CONNID, pData: BYTE, iLength: int): EnHandleResult +OnReceive(pSender: T, dwConnID: CONNID, pData: BYTE, iLength: int): EnHandleResult +OnReceive(pSender: T, dwConnID: CONNID, iLength: int): EnHandleResult +OnClose(pSender: T, dwConnID: CONNID, enOperation: EnSocketOperation, iErrorCode: int): EnHandleResult <>-ISocketListenerT() 21 GetRemoteAddress() 获取某个连接的远程地址 GetListenAddress() 获取监听 Socket 的地址 GetLastError() 获取最近一次失败操作的错误代码 GetLastErrorDesc() 获取最近一次失败操作的错误描述 SetWorkerThreadCount() 设置工作线程数量 SetMaxConnectionCount() 设置最大连接数量 ITcpServer SendSmallFile() 发送小文件 SetSocketListenQueue() 设置监听 Socket 的等候队列大小 SetAcceptSocketCount() 设置 Accept 预投递数量 SetSocketBufferSize() 设置通信数据缓冲区大小 SetKeepAliveTime() 设置心跳检测包发送间隔 SetKeepAliveInterval() 设置心跳检测重试包发送间隔 ITcpPullServer Fetch() 拉取数据 Peek() 窥探数据 ITcpPackServer SetMaxPackSize() 设置最大包长限制 SetPackHeaderFlag() 设置包头校验标识 IUdpServer SetMaxDatagramSize() 设置数据报文最大长度 SetDetectAttempts() 设置检测重试次数 SetDetectInterval() 设置检测包发送间隔 表 2.2.1-1 Server 组件接口 Server 监听器接口的继承层次结构如图 2.2.1-2 所示,其中,ITcpServerListener 和 IUdpServerListener 继承于 IServerListener,接口回调事件如表 2.2.1-2 所示: 监听器接口 回调事件 描 述 ISocketListenerT OnHandShake() 握手完成 握手完成时触发 OnSend() 数据已发送 数据发送成功后触发 OnReceive() [PUSH] 数据到达(PUSH / PACK) 接收到数据时触发 OnReceive() [PULL] 数据到达(PULL) 接收到数据时触发 OnClose() 连接关闭 连接正常或异常关闭时触发 IComplexSocketListenerT OnShutdown() 关闭通信组件 通信组件停止后触发 IServerListenerT OnPrepareListen() 准备监听 绑定监听地址前触发 OnAccept() 接受连接请求 客户端连接请求到达时触发 22 表 2.2.1-2 Server 监听器接口 2.2.2 工作流程 图 2.2.2-1 Server 工作流程 图 2.2.2-1 展示了服务端、客户端应用程序与 Server 组件的交互流程: Server Application Server Component Client Application IServer := new(listener) <> server Start() OnPrepareListen OnAccept Connect OnHandshake OnReceive Send Send() OnSend Send OnClose Disconnect Disconnect() OnClose Disconnect Stop() OnShutdown 23  服务端应用程序调用 Start() 方法启动 Server 组件,如果调用成功则返回 TRUE 并 收到 OnPrepareListen 事件。  客户端应用程序向服务端应用程序发起连接请求时,服务端应用程序将收到 OnAccept 和 OnHandshake 事件。  客户端应用程序向服务端应用程序发送数据时,服务端应用程序将收到 OnReceive 事件。  服务端应用程序调用 Send() 方法向客户端应用程序发出数据后,服务端应用程序 将收到 OnSend 事件。  断开连接时,服务端应用程序将收到 OnClos 事件。  服务端应用程序调用 Stop() 方法关闭 Server 组件,如果调用成功则返回 TRUE 并 收到 OnShutdown 事件。 24 2.3 Agent 组件 2.3.1 接口描述 图 2.3.1-1 Agent 组件接口 IComplexSocket +Stop(): BOOL +Send(dwConnID: CONNID, pBuffer: BYTE, iLength: int, iOffset: int): BOOL +SendPackets(dwConnID: CONNID, pBuffers: WSABUF, iCount: int): BOOL +Disconnect(dwConnID: CONNID, bForce: BOOL): BOOL +DisconnectLongConnections(dwPeriod: DWORD, bForce: BOOL): BOOL +DisconnectSilenceConnections(dwPeriod: DWORD, bForce: BOOL): BOOL +SetConnectionExtra(dwConnID: CONNID, pExtra: PVOID): BOOL +GetConnectionExtra(dwConnID: CONNID, ppExtra: PVOID): BOOL +HasStarted(): BOOL +GetState(): EnServiceState +GetMaxConnectionCount(): DWORD +SetMaxConnectionCount(dwMaxConnectionCount: DWORD): void +GetConnectionCount(): DWORD +GetAllConnectionIDs(pIDs: CONNID, dwCount: DWORD): BOOL +GetConnectPeriod(dwConnID: CONNID, dwPeriod: DWORD): BOOL +GetSilencePeriod(dwConnID: CONNID, dwPeriod: DWORD): BOOL +GetLocalAddress(dwConnID: CONNID, lpszAddress: TCHAR, iAddressLen: int, usPort: USHORT): BOOL +GetRemoteAddress(dwConnID: CONNID, lpszAddress: TCHAR, iAddressLen: int, usPort: USHORT): BOOL +GetLastError(): EnSocketError +GetLastErrorDesc(): LPCTSTR +GetPendingDataLength(dwConnID: CONNID, iPending: int): BOOL +SetSendPolicy(enSendPolicy: EnSendPolicy): void +SetMarkSilence(bMarkSilence: BOOL): void +IsMarkSilence(): BOOL +SetFreeSocketObjLockTime(dwFreeSocketObjLockTime: DWORD): void +SetFreeSocketObjPool(dwFreeSocketObjPool: DWORD): void +SetFreeBufferObjPool(dwFreeBufferObjPool: DWORD): void +SetFreeSocketObjHold(dwFreeSocketObjHold: DWORD): void +SetFreeBufferObjHold(dwFreeBufferObjHold: DWORD): void +SetWorkerThreadCount(dwWorkerThreadCount: DWORD): void +GetSendPolicy(): EnSendPolicy +GetFreeSocketObjLockTime(): DWORD +GetFreeSocketObjPool(): DWORD +GetFreeBufferObjPool(): DWORD +GetFreeSocketObjHold(): DWORD +GetFreeBufferObjHold(): DWORD +GetWorkerThreadCount(): DWORD <>-IComplexSocket() IAgent +Start(lpszBindAddress: LPCTSTR, bAsyncConnect: BOOL): BOOL +Connect(pszRemoteAddress: LPCTSTR, usPort: USHORT, pdwConnID: CONNID): BOOL ITcpAgent +SendSmallFile(dwConnID: CONNID, lpszFileName: LPCTSTR, pHead: LPWSABUF, pTail: LPWSABUF): BOOL +SetReuseAddress(bReuseAddress: BOOL): void +IsReuseAddress(): BOOL +SetSocketBufferSize(dwSocketBufferSize: DWORD): void +SetKeepAliveTime(dwKeepAliveTime: DWORD): void +SetKeepAliveInterval(dwKeepAliveInterval: DWORD): void +GetSocketBufferSize(): DWORD +GetKeepAliveTime(): DWORD +GetKeepAliveInterval(): DWORD IPullSocket +Fetch(dwConnID: CONNID, pData: BYTE, iLength: int): EnFetchResult +Peek(dwConnID: CONNID, pData: BYTE, iLength: int): EnFetchResult <>-IPullSocket() ITcpPullAgent +ToPull(pAgent: IAgent): IPullSocket +ToAgent(pPullSocket: IPullSocket): ITcpAgent IPackSocket +SetMaxPackSize(dwMaxPackSize: DWORD): void +SetPackHeaderFlag(usPackHeaderFlag: USHORT): void +GetMaxPackSize(): DWORD +GetPackHeaderFlag(): USHORT <>-IPackSocket() ITcpPackAgent +ToPack(pAgent: IAgent): IPackSocket +ToAgent(pPackSocket: IPackSocket): ITcpAgent 25 图 2.3.1-2 Agent 监听器接口 Agent 组件接口的继承层次结构如图 2.3.1-1 所示,其中,ITcpAgent 继承于 IAgent, ITcpPullAgent 和 ITcpPackAgent 则继承于 ITcpAgent。主要接口方法如表 2.3.1-1 所示,其它 接口方法请参考 Src/SocketInterface.h 文件的相关注释: 组件接口 操作方法 描 述 IAgent Start() 启动组件 Stop() 关闭组件 Connect() 连接服务器 Send() 发送数据 SendPackets() 发送多组数据 Disconnect() 断开连接 DisconnectLongConnections() 断开长连接 DisconnectSilenceConnections() 断开静默连接 HasStarted() 检查通信组件是否已启动 GetState() 获取通信组件当前状态 GetConnectionCount() 获取连接数 GetConnectPeriod() 获取连接时长 GetSilencePeriod() 获取静默时长 GetAllConnectionIDs() 获取所有连接的 CONNID GetLocalAddress() 获取某个连接的本地地址 GetRemoteAddress() 获取某个连接的远程地址 IComplexSocketListenerT +OnShutdown(pSender: T): EnHandleResult IAgentListenerT +OnPrepareConnect(pSender: T, dwConnID: CONNID, socket: SOCKET): EnHandleResult +OnConnect(pSender: T, dwConnID: CONNID): EnHandleResult ITcpAgentListener ISocketListenerT +OnHandShake(pSender: ITcpClient, dwConnID: CONNID): EnHandleResult +OnSend(pSender: T, dwConnID: CONNID, pData: BYTE, iLength: int): EnHandleResult +OnReceive(pSender: T, dwConnID: CONNID, pData: BYTE, iLength: int): EnHandleResult +OnReceive(pSender: T, dwConnID: CONNID, iLength: int): EnHandleResult +OnClose(pSender: T, dwConnID: CONNID, enOperation: EnSocketOperation, iErrorCode: int): EnHandleResult <>-ISocketListenerT() 26 GetLastError() 获取最近一次失败操作的错误代码 GetLastErrorDesc() 获取最近一次失败操作的错误描述 SetWorkerThreadCount() 设置工作线程数量 SetMaxConnectionCount() 设置最大连接数量 ITcpAgent SendSmallFile() 发送小文件 SetReuseAddress() 设置是否启用地址重用机制 SetSocketBufferSize() 设置通信数据缓冲区大小 SetKeepAliveTime() 设置心跳检测包发送间隔 SetKeepAliveInterval() 设置心跳检测重试包发送间隔 ITcpPullAgent Fetch() 拉取数据 Peek() 窥探数据 ITcpPackAgent SetMaxPackSize() 设置最大包长限制 SetPackHeaderFlag() 设置包头校验标识 表 2.3.1-1 Agent 组件接口 Agent 监听器接口的继承层次结构如图 2.3.1-2 所示,其中,ITcpAgentListener 继承于 IAgentListener,接口回调事件如表 2.3.1-2 所示: 监听器接口 回调事件 描 述 ISocketListenerT OnHandShake() 握手完成 握手完成时触发 OnSend() 数据已发送 数据发送成功后触发 OnReceive() [PUSH] 数据到达(PUSH / PACK) 接收到数据时触发 OnReceive() [PULL] 数据到达(PULL) 接收到数据时触发 OnClose() 连接关闭 连接正常或异常关闭时触发 IComplexSocketListenerT OnShutdown() 关闭通信组件 通信组件停止后触发 IAgentListenerT OnPrepareConnect() 准备建立连接 建立连接前触发 OnConnect() 成功建立连接 成功建立连接后触发 表 2.3.1-2 Agent 监听器接口 27 2.3.2 工作流程 图 2.3.2-1 Agent 工作流程 Client Application Agent Component Server Application IAgent := new(listener) <> agent Start() Connect() OnPrepareConnect Connect OnConnect OnHandshake Send() OnSend Send OnReceive Send OnClose Disconnect Disconnect() Disconnect OnClose Stop() OnShutdown 28 图 2.3.2-1 展示了服务端、客户端应用程序与 Agent 组件的交互流程:  客户端应用程序调用 Start() 方法启动 Agent 组件,如果调用成功则返回 TRUE。  客户端应用程序调用 Connect() 方法向服务端应用程序发起连接请求,如果连接成 功则返回 TRUE 并且会先后接收到 OnPrepareConnect、OnConnect 和 OnHandshake 事件。  客户端应用程序调用 Send() 方法向服务端应用程序发出数据后,客户端应用程序 将收到 OnSend 事件。  服务端应用程序向客户端应用程序发送数据时,客户端应用程序将收到 OnReceive 事件。  断开连接时,客户端应用程序将收到 OnClose 事件。  客户端应用程序调用 Stop() 方法关闭 Agent 组件,如果调用成功则返回 TRUE 并 收到 OnShutdown 事件。 2.4 Client 组件 2.4.1 接口描述 图 2.4.1-1 Client 组件接口 IClient +Start(lpszRemoteAddress: LPCTSTR, usPort: USHORT, bAsyncConnect: BOOL, lpszBindAddress: LPCTSTR): BOOL +Stop(): BOOL +Send(pBuffer: BYTE, iLength: int, iOffset: int): BOOL +SendPackets(pBuffers: WSABUF, iCount: int): BOOL +SetExtra(pExtra: PVOID): void +GetExtra(): PVOID +HasStarted(): BOOL +GetState(): EnServiceState +GetLastError(): EnSocketError +GetLastErrorDesc(): LPCTSTR +GetConnectionID(): CONNID +GetLocalAddress(lpszAddress: TCHAR, iAddressLen: int, usPort: USHORT): BOOL +GetPendingDataLength(iPending: int): BOOL +SetFreeBufferPoolSize(dwFreeBufferPoolSize: DWORD): void +SetFreeBufferPoolHold(dwFreeBufferPoolHold: DWORD): void +GetFreeBufferPoolSize(): DWORD +GetFreeBufferPoolHold(): DWORD <>-IClient() ITcpClient +SendSmallFile(lpszFileName: LPCTSTR, pHead: LPWSABUF, pTail: LPWSABUF): BOOL +SetSocketBufferSize(dwSocketBufferSize: DWORD): void +SetKeepAliveTime(dwKeepAliveTime: DWORD): void +SetKeepAliveInterval(dwKeepAliveInterval: DWORD): void +GetSocketBufferSize(): DWORD +GetKeepAliveTime(): DWORD +GetKeepAliveInterval(): DWORD IUdpClient +SetMaxDatagramSize(dwMaxDatagramSize: DWORD): void +GetMaxDatagramSize(): DWORD +SetDetectAttempts(dwDetectAttempts: DWORD): void +SetDetectInterval(dwDetectInterval: DWORD): void +GetDetectAttempts(): DWORD +GetDetectInterval(): DWORD IUdpCast +SetMaxDatagramSize(dwMaxDatagramSize: DWORD): void +GetMaxDatagramSize(): DWORD +SetReuseAddress(bReuseAddress: BOOL): void +IsReuseAddress(): BOOL +SetCastMode(enCastMode: EnCastMode): void +GetCastMode(): EnCastMode +SetMultiCastTtl(iMCTtl: int): void +GetMultiCastTtl(): int +SetMultiCastLoop(bMCLoop: BOOL): void +IsMultiCastLoop(): BOOL +GetRemoteAddress(lpszAddress: TCHAR, iAddressLen: int, usPort: USHORT): BOOL IPullClient +Fetch(pData: BYTE, iLength: int): EnFetchResult +Peek(pData: BYTE, iLength: int): EnFetchResult <>-IPullClient() ITcpPullClient +ToPull(pClient: IClient): IPullClient +ToClient(pPullClient: IPullClient): ITcpClient IPackClient +SetMaxPackSize(dwMaxPackSize: DWORD): void +SetPackHeaderFlag(usPackHeaderFlag: USHORT): void +GetMaxPackSize(): DWORD +GetPackHeaderFlag(): USHORT <>-IPackClient() ITcpPackClient +ToPack(pClient: IClient): IPackClient +ToClient(pPackClient: IPackClient): ITcpClient 29 图 2.4.1-2 Client 监听器接口 Client 组件接口的继承层次结构如图 2.4.1-1 所示,其中,ITcpClient、IUdpClient 和 IUdpCast 继承于 IClient,ITcpPullClient 和 ITcpPackClient 则继承于 ITcpClient。主要接口方 法如表 2.4.1-1 所示,其它接口方法请参考 Src/SocketInterface.h 文件的相关注释: 组件接口 操作方法 描 述 IClient Start() 启动组件 Stop() 关闭组件 Connect() 连接服务器 Send() 发送数据 SendPackets() 发送多组数据 HasStarted() 检查通信组件是否已启动 GetState() 获取通信组件当前状态 GetConnectionID() 获取该组件对象的 CONNID GetLocalAddress() 获取连接的本地地址 GetLastError() 获取最近一次失败操作的错误代码 GetLastErrorDesc() 获取最近一次失败操作的错误描述 ITcpClient SendSmallFile() 发送小文件 SetSocketBufferSize() 设置通信数据缓冲区大小 SetKeepAliveTime() 设置心跳检测包发送间隔 SetKeepAliveInterval() 设置心跳检测重试包发送间隔 ITcpPullClient Fetch() 拉取数据 Peek() 窥探数据 IClientListenerT +OnPrepareConnect(pSender: T, dwConnID: CONNID, socket: SOCKET): EnHandleResult +OnConnect(pSender: T, dwConnID: CONNID): EnHandleResult ITcpClientListener IUdpCastListenerIUdpClientListener ISocketListenerT +OnHandShake(pSender: ITcpClient, dwConnID: CONNID): EnHandleResult +OnSend(pSender: T, dwConnID: CONNID, pData: BYTE, iLength: int): EnHandleResult +OnReceive(pSender: T, dwConnID: CONNID, pData: BYTE, iLength: int): EnHandleResult +OnReceive(pSender: T, dwConnID: CONNID, iLength: int): EnHandleResult +OnClose(pSender: T, dwConnID: CONNID, enOperation: EnSocketOperation, iErrorCode: int): EnHandleResult <>-ISocketListenerT() 30 ITcpPackClient SetMaxPackSize() 设置最大包长限制 SetPackHeaderFlag() 设置包头校验标识 IUdpClient SetMaxDatagramSize() 设置数据报文最大长度 SetDetectAttempts() 设置检测重试次数 SetDetectInterval() 设置检测包发送间隔 IUdpCast SetMaxDatagramSize() 设置数据报文最大长度 SetReuseAddress() 设置是否启用地址重用机制 SetCastMode() 设置传播模式(组播或广播) SetMultiCastTtl() 设置组播报文 TTL SetMultiCastLoop() 设置是否启用组播环路 GetRemoteAddress() 获取当前数据包的远程地址 表 2.4.1-1 Client 组件接口 Client 监听器接口的继承层次结构如图 2.4.1-2 所示,其中,ITcpClientListener 和 IUdpClientListener 继承于 IClientListener,接口回调事件如表 2.4.1-2 所示: 监听器接口 回调事件 描 述 ISocketListenerT OnHandShake() 握手完成 握手完成时触发 OnSend() 数据已发送 数据发送成功后触发 OnReceive() [PUSH] 数据到达(PUSH / PACK) 接收到数据时触发 OnReceive() [PULL] 数据到达(PULL) 接收到数据时触发 OnClose() 连接关闭 连接正常或异常关闭时触发 IClientListenerT OnPrepareConnect() 准备建立连接 建立连接前触发 OnConnect() 成功建立连接 成功建立连接后触发 表 2.4.1-2 Client 监听器接口 31 2.4.2 工作流程 图 2.4.2-1 Client 工作流程 图 2.4.2-1 展示了服务端、客户端应用程序与 Client 组件的交互流程:  客户端应用程序调用 Start() 方法向服务端应用程序发起连接请求,如果连接成功 则返回 TRUE 并且会先后接收到 OnPrepareConnect、OnConnect 和 OnHandshake 事件。 Client Application Server ApplicationClient Component IClient := new(listener) <> client Start() OnPrepareConnect OnConnect OnHandShake Send() OnSend Send OnReceive Send OnClose Disconnect Stop() OnClose Disconnect 32  客户端应用程序调用 Send() 方法向服务端应用程序发出数据后,客户端应用程序 将收到 OnSend 事件。  服务端应用程序向客户端应用程序发送数据时,客户端应用程序将收到 OnReceive 事件。  断开连接时,客户端应用程序将收到 OnClose 事件。  客户端应用程序调用 Stop() 方法关闭 Client 组件,如果调用成功则返回 TRUE 并 收到 OnClose 事件。 33 3 SSL 组件 3.1 组件接口 HP-Socket v3.5.x 版本开始,所有 TCP 组件全面支持 SSL。SSL 组件与对应的非 SSL 组 件实现相同的接口,它们的使用方式也一致。表 3.1-1 列出了所有 SSL 组件的名称、接口、 监听器接口、实现类及其分类: 名 称 组件接口 监听器接口 实现类 通信 角色 接收 模型 SSL Server ITcpServer ITcpServerListener CSSLServer Server PUSH SSL Pull Server ITcpPullServer ITcpServerListener CSSLPullServer Server PULL SSL Pack Server ITcpPackServer ITcpServerListener CSSLPackServer Server PACK SSL Agent ITcpAgent ITcpServerListener CSSLAgent Client PUSH SSL Pull Agent ITcpPullAgent ITcpAgentListener CSSLPullAgent Client PULL SSL Pack Agent ITcpPackAgent ITcpServerListener CSSLPackAgent Client PACK SSL Client ITcpClient ITcpClientListener CSSLClient Client PUSH SSL Pull Client ITcpPullClient ITcpClientListener CSSLPullClient Client PULL SSL Pack Client ITcpPackClient ITcpClientListener CSSLPackClient Client PACK 表 3.1-1 组件分类 各 SSL 组件的层次结构如图 3.1-1 所示,所有 SSL 组件都继承于对应的 TCP 组件:  CSSLServer >> CTcpServer  CSSLAgent >> CTcpAgent  CSSLClient >> CTcpClient 34 图 3.1-1 组件层次结构 IPullSocket +Fetch() +Peek() <>-IPullSocket() CTcpAgentCTcpServer IPackSocket +SetMaxPackSize() +SetPackHeaderFlag() +GetMaxPackSize() +GetPackHeaderFlag() <>-IPackSocket() CSSLServer +SendPackets() <>-CSSLServer() <>-CSSLServer() CSSLPullServer +Fetch() +Peek() <>-CSSLPullServer() <>-CSSLPullServer() CSSLPullAgent +Fetch() +Peek() <>-CSSLPullAgent() <>-CSSLPullAgent() CSSLPackServer +SendPackets() +SetMaxPackSize() +SetPackHeaderFlag() +GetMaxPackSize() +GetPackHeaderFlag() +FireSuperReceive() <>-CSSLPackServer() <>-CSSLPackServer() CSSLPackAgent +SendPackets() +SetMaxPackSize() +SetPackHeaderFlag() +GetMaxPackSize() +GetPackHeaderFlag() +FireSuperReceive() <>-CSSLPackAgent() <>-CSSLPackAgent() CSSLAgent +SendPackets() <>-CSSLAgent() <>-CSSLAgent() CTcpClient CSSLClient +SendPackets() <>-CSSLClient() <>-CSSLClient() IPackClient +SetMaxPackSize() +SetPackHeaderFlag() +GetMaxPackSize() +GetPackHeaderFlag() <>-IPackClient() IPullClient +Fetch() +Peek() <>-IPullClient() CSSLPullClient +Fetch() +Peek() <>-CSSLPullClient() <>-CSSLPullClient() CSSLPackClient +SendPackets() +SetMaxPackSize() +SetPackHeaderFlag() +GetMaxPackSize() +GetPackHeaderFlag() +FireSuperReceive() <>-CSSLPackClient() <>-CSSLPackClient() EnSSLSessionMode <> +SSL_SM_CLIENT +SSL_SM_SERVER EnSSLVerifyMode <> +SSL_VM_NONE +SSL_VM_PEER +SSL_VM_FAIL_IF_NO_PEER_CERT +SSL_VM_CLIENT_ONCE 35 3.2 SSL 运行环境 SSL 组件在启动通信前需要初始化 SSL 全局环境参数,通信完毕或退出应用程序前需 要清理 SSL 全局运行环境。HP-Socket 导出相应函数执行此类工作:  初始化 SSL 全局环境参数: BOOL HP_SSL_Initialize(enSessionMode, iVerifyMode = SSL_VM_NONE, lpszPemCertFile = nullptr, lpszPemKeyFile = nullptr, lpszKeyPasswod = nullptr, lpszCAPemCertFileOrPath = nullptr, fnServerNameCallback = nullptr) 参数 enSessionMode 指定 SSL 工作模式;参数 iVerifyMode 指定 SSL 验证模式;参数 lpszPemCertFile、lpszPemKeyFile、lpszKeyPasswod 和 lpszCAPemCertFileOrPath 分别指定证 书文件、私钥文件、私钥密码和 CA 证书文件/目录;参数 fnServerNameCallback 指定 SNI 回调函数指针,该参数只用于 HTTPS 服务端。参数详细说明请参考 Src/HPSocket-SSL.h 或 Src/HPSocket4C-SSL.h 头文件。初始化成功返回 TRUE,失败返回 FALSE,初始化失败可通 过 SYS_GetLastError() 获取错误代码。  增加 SNI 主机证书: int HP_SSL_AddServerContext (iVerifyMode, lpszPemCertFile, lpszPemKeyFile, lpszKeyPasswod = nullptr, lpszCAPemCertFileOrPath = nullptr) 参数 iVerifyMode 指定 SSL 验证模式;参数 lpszPemCertFile、lpszPemKeyFile、 lpszKeyPasswod 和 lpszCAPemCertFileOrPath 分别指定证书文件、私钥文件、私钥密码和 CA 证书文件/目录。执行成功返回 SNI 主机证书对应的索引,该索引用于在 SNI 回调函数中 定位 SNI 主机;失败返回-1,可通过 SYS_GetLastError() 获取错误代码。 注意:应用程序一般在初始化 SSL 运行环境时调用 HP_SSL_AddServerContext()加载所 有 SNI 主机证书,但一些特殊应用可能要动态加载 SNI 主机证书,此时需要对调用 HP_SSL_AddServerContext()的代码段进行同步处理,避免重复加载相同证书。  清理 SSL 全局环境: void HP_SSL_Cleanup() 应用程序退出时会自动清理 SSL 全局环境,因此,如果应用程序只需调用一次 HP_SSL_Initialize() 初始化 SSL 全局环境参数,则可以不调用本函数。但是,当要调用多次 HP_SSL_Initialize() 重新设置 SSL 全局环境参数时,需要先调用本函数清理原先的环境参数。  清理线程局部环境 SSL 资源: void HP_SSL_RemoveThreadLocalState() 36 任何一个操作 SSL 的线程,在退出时都需要清理线程的局部环境 SSL 资源,主线程和 HP-Socket 工作线程在通信结束时会自动清理线程局部环境 SSL 资源。因此,一般情况下不 必手工调用本函数;特殊情况下,当自定义线程参与 HP-Socket 通信操作(如:通信组件发 送策略为 SP_DIRECT 并且该自定义线程调用了 Send() 方法发送数据)并检查到 SSL 内存 泄漏时,需在每次通信结束时在该自定义线程调用本方法。  检查 SSL 全局运行环境是否初始化完成: BOOL HP_SSL_IsValid() 初始化完成返回 TRUE,否则返回 FALSE。 注意:对于 C++应用程序,Src/HPSocket-SSL.h 提供了帮助类 CHPSSLInitializer 封装 上述方法,方便应用程序使用。 37 4 HTTP 组件 4.1 组件接口 HP-Socket v4.0.x 版本开始加入 HTTP 组件。HTTP 组件继承于相应的 TCP 组件并增加 HTTP 相关操作方法; HTTP 组件监听器也继承于相应的 TCP 组件监听器并提供 HTTP 相 关通信事件。表 4.1-1 列出了所有 HTTP 组件的名称、接口、监听器接口、实现类及 TCP 组 件父类: 名 称 组件接口 监听器接口 实现类 通信 角色 TCP 父类 Http Server IHttpServer IHttpServerListener CHttpServer Server CTcpServer Https Server IHttpServer IHttpServerListener CHttpsServer Server CSSLServer Http Agent IHttpAgent IHttpAgentListener CHttpAgent Client CTcpAgent Https Agent IHttpAgent IHttpAgentListener CHttpsAgent Client CSSLAgent Http Client IHttpClient IHttpClientListener CHttpClient Client CTcpClient Https Client IHttpClient IHttpClientListener CHttpsClient Client CSSLClient 表 4.1-1 组件分类 38 图 4.1-1 HTTP 组件层次结构 CHttpAgentT +SendRequest() +SendLocalFile() +SendPost() +SendPut() +SendPatch() +SendGet() +SendDelete() +SendHead() +SendTrace() +SendOptions() +SendConnect() +SetLocalVersion() +GetLocalVersion() +IsUpgrade() +IsKeepAlive() +GetVersion() +GetContentLength() +GetContentType() +GetUpgradeType() +GetParseErrorCode() +GetHeader() +GetHeaders() +GetAllHeaders() +GetAllHeaderNames() +GetCookie() +GetAllCookies() +AddCookie() +DeleteCookie() +DeleteAllCookies() +GetStatusCode() <>-CHttpAgentT() <>-CHttpAgentT() CHttpClientT +SendRequest() +SendLocalFile() +SendPost() +SendPut() +SendPatch() +SendGet() +SendDelete() +SendHead() +SendTrace() +SendOptions() +SendConnect() +SetLocalVersion() +GetLocalVersion() +IsUpgrade() +IsKeepAlive() +GetVersion() +GetContentLength() +GetContentType() +GetUpgradeType() +GetParseErrorCode() +GetHeader() +GetHeaders() +GetAllHeaders() +GetAllHeaderNames() +GetCookie() +GetAllCookies() +AddCookie() +DeleteCookie() +DeleteAllCookies() +GetStatusCode() <>-CHttpClientT() <>-CHttpClientT() CHttpServerT +Start() +SendResponse() +SendLocalFile() +Release() +SetLocalVersion() +SetReleaseDelay() +GetLocalVersion() +GetReleaseDelay() +IsUpgrade() +IsKeepAlive() +GetVersion() +GetContentLength() +GetContentType() +GetUpgradeType() +GetParseErrorCode() +GetHeader() +GetHeaders() +GetAllHeaders() +GetAllHeaderNames() +GetCookie() +GetAllCookies() +GetUrlFieldSet() +GetUrlField() +GetMethod() <>-CHttpServerT() <>-CHttpServerT() ITcpServer ITcpAgent ITcpClient IComplexHttp +SetLocalVersion() +GetLocalVersion() +IsUpgrade() +IsKeepAlive() +GetVersion() +GetContentLength() +GetContentType() +GetUpgradeType() +GetParseErrorCode() +GetHeader() +GetHeaders() +GetAllHeaders() +GetAllHeaderNames() +GetCookie() +GetAllCookies() <>-IComplexHttp() IComplexHttpRequester +SendRequest() +SendLocalFile() +SendPost() +SendPut() +SendPatch() +SendGet() +SendDelete() +SendHead() +SendTrace() +SendOptions() +SendConnect() +GetStatusCode() +AddCookie() +DeleteCookie() +DeleteAllCookies() IComplexHttpResponder +SendResponse() +SendLocalFile() +Release() +SetReleaseDelay() +GetReleaseDelay() +GetUrlFieldSet() +GetUrlField() +GetMethod() IHttp +SetLocalVersion() +GetLocalVersion() +IsUpgrade() +IsKeepAlive() +GetVersion() +GetContentLength() +GetContentType() +GetUpgradeType() +GetParseErrorCode() +GetStatusCode() +GetHeader() +GetHeaders() +GetAllHeaders() +GetAllHeaderNames() +GetCookie() +GetAllCookies() +AddCookie() +DeleteCookie() +DeleteAllCookies() <>-IHttp() IHttpRequester +SendRequest() +SendLocalFile() +SendPost() +SendPut() +SendPatch() +SendGet() +SendDelete() +SendHead() +SendTrace() +SendOptions() +SendConnect() CHttpAgent <> CHttpsAgent <> CHttpClient <> CHttpsClient <> CHttpServer <> CHttpsServer <> IHttpServer <> IHttpAgent <> IHttpClient <> 39 图 4.1-2 HTTP 组件监听器层次结构 4.2 HTTP 监听器事件 HTTP 组件监听器接口继承于 IHttpListenerT 和相应的 TCP 组件监听器接口。 IHttpListenerT 的 HTTP 事件可以理解为 TCP 组件监听器 OnReceive 事件的分解。HTTP 事 件返回值的类型为 EnHttpParseResult: ITcpServerListener ITcpAgentListener ITcpClientListener IHttpListenerT +OnMessageBegin() +OnRequestLine() +OnStatusLine() +OnHeader() +OnHeadersComplete() +OnBody() +OnChunkHeader() +OnChunkComplete() +OnMessageComplete() +OnUpgrade() +OnParseError() <>-IHttpListenerT() IHttpServerListener IHttpAgentListener IHttpClientListener CHttpServerListener +OnPrepareListen() +OnAccept() +OnHandShake() +OnReceive() +OnReceive() +OnSend() +OnShutdown() +OnMessageBegin() +OnRequestLine() +OnStatusLine() +OnHeader() +OnChunkHeader() +OnChunkComplete() +OnUpgrade() CHttpAgentListener +OnPrepareConnect() +OnConnect() +OnHandShake() +OnReceive() +OnReceive() +OnSend() +OnShutdown() +OnMessageBegin() +OnRequestLine() +OnStatusLine() +OnHeader() +OnChunkHeader() +OnChunkComplete() +OnUpgrade() CHttpClientListener +OnPrepareConnect() +OnConnect() +OnHandShake() +OnReceive() +OnReceive() +OnSend() +OnMessageBegin() +OnRequestLine() +OnStatusLine() +OnHeader() +OnChunkHeader() +OnChunkComplete() +OnUpgrade() EnHttpParseResult <> +HPR_OK +HPR_SKIP_BODY +HPR_UPGRADE +HPR_ERROR 40  HPR_OK :解析成功,继续执行  HPR_SKIP_BODY :跳过当前请求 BODY,完成本次请求 (仅用于 OnHeadersComplete 事件)  HPR_UPGRADE :升级协议,完成本次请求,并且不再进行后续 HTTP 解析 (仅用于 OnHeadersComplete 事件)  HPR_ERROR :解析错误,终止解析,断开连接  开始解析事件: EnHttpParseResult OnMessageBegin(pSender, dwConnID)  pSender -- 事件源对象  dwConnID -- 连接 ID  请求行解析完成事件(仅用于 HTTP 服务端): EnHttpParseResult OnRequestLine(pSender, dwConnID, lpszMethod, lpszUrl)  pSender -- 事件源对象  dwConnID -- 连接 ID  lpszMethod -- 请求方法名  lpszUrl -- 请求行中的 URL 域  状态行解析完成事件(仅用于 HTTP 客户端): EnHttpParseResult OnStatusLine(pSender, dwConnID, usStatusCode, lpszDesc)  pSender -- 事件源对象  dwConnID -- 连接 ID  usStatusCode -- HTTP 状态码  lpszDesc -- 状态描述  请求头事件: EnHttpParseResult OnHeader(pSender, dwConnID, lpszName, lpszValue)  pSender -- 事件源对象  dwConnID -- 连接 ID  lpszName -- 请求头名称  lpszValue -- 请求头值  请求头完成事件: EnHttpParseResult OnHeadersComplete(pSender, dwConnID) 41  pSender -- 事件源对象  dwConnID -- 连接 ID  BODY 报文事件: EnHttpParseResult OnBody(pSender, dwConnID, pData, iLength)  pSender -- 事件源对象  dwConnID -- 连接 ID  pData -- 数据缓冲区  iLength -- 数据长度  Chunked 报文头事件: EnHttpParseResult OnChunkHeader(pSender, dwConnID, iLength)  pSender -- 事件源对象  dwConnID -- 连接 ID  iLength -- Chunked 报文体数据长度  Chunked 报文结束事件: EnHttpParseResult OnChunkComplete(pSender, dwConnID)  pSender -- 事件源对象  dwConnID -- 连接 ID  完成解析事件: EnHttpParseResult OnMessageComplete(pSender, dwConnID)  pSender -- 事件源对象  dwConnID -- 连接 ID  升级协议事件: EnHttpParseResult OnUpgrade(pSender, dwConnID)  pSender -- 事件源对象  dwConnID -- 连接 ID  enUpgradeType -- 协议类型  解析错误事件: 42 EnHttpParseResult OnParseError(pSender, dwConnID, iErrorCode, lpszErrorDesc)  pSender -- 事件源对象  dwConnID -- 连接 ID  iErrorCode -- 错误代码  lpszErrorDesc -- 错误描述 其中,OnHeadersComplete、OnBody、OnMessageComplete 和 OnParseError 是最基本的 4 个 HTTP 事件,所有应用程序都要对这些事件进行处理,其他事件则根据应用程序的实际 情况进行处理。例如:如果应用程序不用处理 Chunked 报文则可忽略 OnChunkHeader 和 OnChunkComplete 事件;如果应用程序不会进行协议升级则可忽略 OnUpgrade 事件。 HP-Socket 支持 WebSocket 和 Http Tunnel 协议升级,协议升级时会触发 OnUpgrade 事 件,该事件的 enUpgradeType 参数指示升级类型:  HUT_WEB_SOCKET :Web Socket  HUT_HTTP_TUNNEL :HTTP Tunnel 当完成协议升级后,后续接收的所有数据都被看作为 TCP 数据,触发 OnReceive 事件, 而不再进行 HTTP 解析。 注意: WebSocket:HP-Socket 只实现了 WebSocket 协议升级握手,后续的 WebSocket 通信协 议需要应用程序自身实现。 Cookie:HP-Socket 目前只维护基于连接的 Cookie,如果应用程序需要在不同连接间共 享 Cookie,则需要应用程序自身实现。 EnHttpUpgradeType <> +HUT_NONE +HUT_WEB_SOCKET +HUT_HTTP_TUNNEL +HUT_UNKNOWN 43 5 使用方式 HP-Socket 支持 MBCS 和 Unicode 字符集,支持 32 位和 64 位应用程序。可以通过源代 码、DLL 或 LIB 方式使用 HP-Socket。HP-Socket 发布包中已经提供了 HPSocket DLL 和 HPSocket4C DLL。 5.1 源代码 HP-Socket 依赖于 Common/Src 目录下的一些公共代码。所以,通过源代码方式使用 HP-Socket 时需要把 HP-Socket 的 Src 目录和 Common/Src 目录下的相应代码文件加入到工程 项目(参考:TestEcho / TestEcho-UDP 示例 Demo)。 5.2 静态库 HP-Socket 发布包中的 Project/HPSocketLIB 和Project/HPSocketLIB4C 工程项目用于编译 HPSocket LIB 和 HPSocket4C LIB,输出目录为 Bin\x86(x64)\static。如果需要可以自己编译。 静态库与动态库的使用方式一致,请参考后续章节。 (静态库引用方式参考:TestEcho-SSL-4C / TestEcho-SSL-PFM 示例 Demo)。 注意:如果工程项目使用 HPSocket LIB 或 HPSocket4C LIB,需要在工程属性中定义预 处理宏 -> HPSOCKET_STATIC_LIB。 5.3 HPSocket DLL HPSocket DLL 导出 C++ 编程接口,是 C++ 程序使用 HP-Socket 的首选方式。HPSocket DLL 通过 HP-Socket 发布包中的 Project/HPSocketDLL 工程项目编译生成,输出以下 DLL:  Bin\x86\HPSocket.dll (32 位/MBCS/Release)  Bin\x86\HPSocket_D.dll (32 位/MBCS/Debug)  Bin\x86\HPSocket_U.dll (32 位/Unicode/Release)  Bin\x86\HPSocket_UD.dll (32 位/Unicode/Debug)  Bin\x64\HPSocket.dll (64 位/MBCS/Release)  Bin\x64\HPSocket_D.dll (64 位/MBCS/Debug)  Bin\x64\HPSocket_U.dll (64 位/Unicode/Release)  Bin\x64\HPSocket_UD.dll (64 位/Unicode/Debug)  Bin\x86\HPSocket-SSL.dll (32 位/MBCS/Release)  Bin\x86\HPSocket-SSL_D.dll (32 位/MBCS/Debug)  Bin\x86\HPSocket-SSL_U.dll (32 位/Unicode/Release) 44  Bin\x86\HPSocket-SSL_UD.dll (32 位/Unicode/Debug)  Bin\x64\HPSocket-SSL.dll (64 位/MBCS/Release)  Bin\x64\HPSocket-SSL_D.dll (64 位/MBCS/Debug)  Bin\x64\HPSocket-SSL_U.dll (64 位/Unicode/Release)  Bin\x64\HPSocket-SSL_UD.dll (64 位/Unicode/Debug) 使用 HPSocket DLL 时需要把 Src/HPSocket.h、Src/SocketInterface.h 以及 DLL 对应的*.lib 文件加入到工程项目。Src/HPSocket.h 除了导出组件的创建、销毁方法和组件接口外,还定 义了各组件的智能指针(如:CTcpServerPtr / CTcpClientPtr),通过这些智能指针可以更方便 地使用 HP-Socket 组件。(参考:TestEcho-Pull / TestEcho-PFM 示例 Demo)。 HPSocket-SSL DLL 同时导出 SSL 组件和非 SSL 组件,使用 HPSocket-SSL DLL 时需要 把 Src/HPSocket-SSL.h、Src/SocketInterface-SSL.h 以及 DLL 对应的*.lib 文件加入到工程项目。 (参考:TestEcho-SSL-Pack 示例 Demo)。 通过 DLL 方式使用 HP-Socket,当需要更新或升级 HP-Socket 时,如果 DLL 接口发生 变化则必须重新编译应用程序;如果 DLL 接口没有改变则直接替换 DLL 即可,不需要重新 编译应用程序。 5.4 HPSocket4C DLL HPSocket4C DLL 导出 C 编程接口,提供给 C 语言或其它编程语言使用 HP-Socket。 HPSocket4C DLL 通过 HP-Socket 发布包中的 Project/HPSocketDLL4C 工程项目编译生成,输 出以下 DLL:  Bin\x86\HPSocket4C.dll (32 位/MBCS/Release)  Bin\x86\HPSocket4C_D.dll (32 位/MBCS/Debug)  Bin\x86\HPSocket4C_U.dll (32 位/Unicode/Release)  Bin\x86\HPSocket4C_UD.dll (32 位/Unicode/Debug)  Bin\x64\HPSocket4C.dll (64 位/MBCS/Release)  Bin\x64\HPSocket4C_D.dll (64 位/MBCS/Debug)  Bin\x64\HPSocket4C_U.dll (64 位/Unicode/Release)  Bin\x64\HPSocket4C_UD.dll (64 位/Unicode/Debug)  Bin\x86\HPSocket4C-SSL.dll (32 位/MBCS/Release)  Bin\x86\HPSocket4C-SSL_D.dll (32 位/MBCS/Debug)  Bin\x86\HPSocket4C-SSL_U.dll (32 位/Unicode/Release)  Bin\x86\HPSocket4C-SSL_UD.dll (32 位/Unicode/Debug)  Bin\x64\HPSocket4C-SSL.dll (64 位/MBCS/Release)  Bin\x64\HPSocket4C-SSL_D.dll (64 位/MBCS/Debug)  Bin\x64\HPSocket4C-SS _U.dll (64 位/Unicode/Release)  Bin\x64\HPSocket4C-SSL_UD.dll (64 位/Unicode/Debug) 使用 HPSocket4C DLL 时需要把 Src/HPSocket4C.h 以及 DLL 对应的*.lib 文件加入到工 45 程项目。(参考:TestEcho-4C 示例 Demo)。 HPSocket4C-SSL DLL 同时导出 SSL 组件和非 SSL 组件,使用 HPSocket4C-SSL DLL 时 需要把 Src/HPSocket4C-SSL.h 以及 DLL 对应的*.lib 文件加入到工程项目。(参考: TestEcho-SSL-Pack 示例 Demo)。 通过 4C DLL 方式使用 HP-Socket,当需要更新或升级 HP-Socket 时,如果 DLL 接口发 生变化则必须重新编译应用程序;如果 DLL 接口没有改变则直接替换 DLL 即可,不需要重 新编译应用程序。 注意:HPSocket DLL 和 HPSocket4C DLL 均导出以下几个辅助方法:  LPCTSTR HP_GetSocketErrorDesc() :获取 HPSocket 错误码对应描述  DWORD SYS_GetLastError() :封装 API 函数 GetLastError()  int SYS_WSAGetLastError() :封装 API 函数 WSAGetLastError()  int SYS_SetSocketOption() :封装 API 函数 setsockopt()  int SYS_GetSocketOption() :封装 API 函数 getsockopt()  int SYS_IoctlSocket() :封装 API 函数 ioctlsocket()  int SYS_WSAIoctl() :封装 API 函数 WSAIoctl() 5.5 其它编程语言使用 HPSocket HP-Socket 发布包中提供了 C# 和易语言 SDK,应用程序可以通过 SDK 方式使用 HP-Socket;对于其它没有提供 SDK 的编程语言(如:Delphi、Java),可以通过导入 HPSocket4C DLL 的方式使用 HP-Socket。 C#、Delphi 和易语言使用 HP-Socket 的例子请参考 HP-Socket 发布包中 Demo/Other Languages Demo 目录下的示例 Demo。 46 6 附 录 6.1 示例 Demo 项目名称 使用组件 引用方 式 描 述 HttpProxy TCP Server TCP Agent DLL HTTP 代理服务器 TestEcho TCP Server TCP Client SRC Echo 服务端和客户端 TestEcho-4C TCP PULL Server TCP PULL Client 4C DLL Echo 服务端和客户端 TestEcho-Agent (Agent-4C) TCP PULL Agent 4C DLL Echo 客户端 TestEcho-Agent (Agent-PFM) TCP Agent SRC Echo 性能测试客户端 TestEcho-Agent (Agent-PULL) TCP PULL Agent DLL Echo 客户端 TestEcho-PFM TCP Server TCP Client DLL Echo 性能测试服务端和客户端 TestEcho-Pull TCP PULL Server TCP PULL Client DLL Echo 服务端和客户端 TestEcho-Pack TCP PACK Server TCP PACK Client 4C DLL DLL Echo 服务端和客户端 TestEcho-UDP UDP Server UDP Client SRC Echo 服务端和客户端 TestEcho-UDP-PFM UDP Server UDP Client DLL Echo 性能测试服务端和客户端 TestUDPCast UDP Cast SRC 组播(广播)网络成员 TestEcho-SSL SSL Server SSL Client SRC Echo 服务端和客户端 TestEcho-SSL-4C SSL PULL Server SSL PULL Client 4C LIB Echo 服务端和客户端 TestEcho-SSL-Pack SSL PACK Server SSL PACK Client 4C DLL DLL Echo 服务端和客户端 TestEcho-SSL-PFM SSL Server SSL Agent LIB Echo 性能测试服务端和客户端 TestEcho-Http Http Server Http Client SRC Echo 服务端和客户端 47 TestEcho-Http-4C Http Server Http Client 4C LIB 4C DLL Echo 服务端和客户端 表 6.1-1 示例 Demo 48 6.2 FAQ  Q-01:Connection ID 的长度是多少字节?数值溢出怎么办?  A:Connection ID 是一个从 1 开始顺序递增的整数值,当溢出时会重新从 1 开始递 增。Connection ID 在 32 位程序中是 4 字节,在 64 位程序中是 8 字节,理论上在 32 位程序中存在溢出的可能,但不必过于担心,因为极少有“创建了 40 亿次连接 后第一个连接还没断开”的场景。  Q-02:可以在通信线程中调用 Start() / Stop() 吗?  A:不能。由于监听器事件(OnReceive / OnClose 等)通常都在通信线程中被触发, 因此不能在监听器事件处理代码中调用 Start() / Stop() 控制方法。  Q-03:如何断开超长连接?  A:所谓超长连接是指连接时长超过正常时长的连接。Server 和 Agent 组件提供 DisconnectLongConnections() 方法断开所有超长连接,也提供 GetConnectPeriod() 方法用来获取某个连接的时长。  Q-04:如何断开静默连接?  A:所谓静默连接是长时间没有数据交互的指连接。Server 和 Agent 组件提供 DisconnectSilenceConnections() 方法断开所有静默连接,也提供 GetSilencePeriod() 方法用来获取某个连接的静默时间。注意:当组件开启了静默标记时上述两个方法 才有效,可在组件启动前调用“SetMarkSilence(TRUE)”方法来开启静默标记。 HP-Socket v3.5.x 及后续版本默认开启静默标记。  Q-05:断线重连该如何实现?  A:Agent 组件可以在接收到断线通知事件(OnClose)时立刻发起 Connect() 调用 进行重连;Client 组件则不能接收到断线通知事件(OnClose)时立刻调用 Start() 方 法进行重连。因此,Client 组件可以选择以下方法实现重连: 1) 启动一个监测线程或定时器,定期调用组件对象的 GetState() 方法检查组 件对象的状态,如果状态为 SS_STOPED 则执行重连。 2) 启动一个监测线程,在组件的 OnClose 事件中向监测线程发送断线重连通 知(Event)激活监测线程,监测线程循环调用组件对象的 GetState() 方 法检查组件对象的状态,直到状态为 SS_STOPED 则执行重连。 3) 使用窗口消息机制结合 ::PostMessage() / ::PostThreadMessage() API 函数 替代 2) 中的监测线程和通知(Event)。  Q-06:HP-Socket 有心跳检测机制吗?  A:有(UdpCast 组件除外)。TCP 组件使用 TCP 协议内置的心跳检测机制,UDP 组件通过互发 0 字节数据包实现心跳检测: 1) TCP 心跳检测:SetKeepAliveTime() / SetKeepAliveInterval(),单位 - 毫秒 超时时间计算公式:KeepAliveTime + (KeepAliveInterval * N) 其中 N 为固定值:WinXP 以下系统 N=5;Win7 以上系统 N=10 2) UDP 心跳检测:SetDetectInterval() / SetDetectAttempts(),单位 - 秒 49 超时时间计算公式:DetectInterval * (DetectAttempts + X) 其中 X 可能为 0~1 之间的任意值 3) 对于 Server 或 Agent 组件,可以通过“断开静默连接”方式间接实现心跳 检测。如:使用定时器或独立线程定时调用 DisconnectSilenceConnections() 方法断开静默连接。  Q-07:为什么 HP-Socket 的 UDP 组件与我的 UDP 程序通信经常会断开连接?  A:HP-Socket 的 UDP 服务端和客户端组件默认都开启了心跳检测机制,与第三方 UDP 程序通信时,有两种选择: 1) 调用 SetDetectInterval(0) / SetDetectAttempts(0) 关闭 UDP 组件的心跳检测 机制。 2) 你自己的程序实现与 HP-Socket 的 UDP 心跳检测握手。具体方法是:  使用 IUdpClient 作为客户端:当服务端接收到 0 字节的 UDP 心跳数 据包时立刻回复一个 0 字节的握手包。  使用 IUdpServer 作为服务端:客户端需要定期向服务端发送 0 字节 的 UDP 心跳数据包。  Q-08:HP-Socket 的 TCP 组件是否处理了粘包?  A:三种选择: 1) PUSH 模型:应用程序手工处理粘包。 2) PULL 模型:与应用层协议配合,半自动处理粘包。 3) PACK 模型:通信组件自动处理粘包。  Q-09:HP-Socket 如何与第三方 Socket 应用通信?  A:根据 HP-Socket 组件接收模型分别处理: 1) PUSH 模型:与应用层协议无关,可以直接通信。 2) PULL 模型:与对端协商应用层协议。 3) PACK 模型:对端需遵守 HP-Socket PACK 的数据包格式。  Q-10:多个线程同时发送数据时会不会造成发送方或接收方发送数据包乱序?  A:不会。对于发送方,HP-Socket 会确保每个 Send() 方法调用所发出的数据都是 完整有序的,不会受其它 Send() 方法干扰;对于接收方,HP-Socket 对同一连接不 会同时触发多个 OnReceive 事件,因此,接收方的数据包也不会发生乱序。  Q-11:多个通信组件能共享同一个监听器对象吗?  A:可以。根据实际的使用场景来确定是否应该用一个监听器对象监听多个组件的 通信事件。  Q-12:有些机器在关闭 TCP Server 时,会一直卡在 Stop()方法里面,Why?  A:处理方法: 1) 以管理员身份在命令行工具中执行:“netsh winsock reset”,重启机器。 2) 少上不良网站,卸载不良软件。
还剩49页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

wzhelp

贡献于2016-10-12

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf