一、定义

PULL 模型: 组件接收到数据时会触发监听器对象的 OnReceive(pSender, dwConnID,iTotalLength) 事件,告诉应用程序当前已经接收到多少数据,应用程序检查数据的长度,如果满足需要则调用组件的 Fetch(dwConnID, pData, iDataLength) 方法把需要的数据“拉”出来。

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 模型与应用层协议的相互配合,使得应用程序可以免除粘包处理和分拆包工作,从而减少应用程序的负担。
pull模型的一般使用场景:
通信协议为:包头域(定长)+数据域(不定长)的情形

二、开发步骤

2.1 新建项目工程

打开vs2019新建c空项目TcpServer_pull,pull模型属于半自动模型,这里我们用纯c语言编写,调用HPSocket4C.h头文件,编码步骤几乎和上一篇push模型步骤一样,有问题的话可以参考上一篇《Hp-Socket高性能网络库一–tcp组件push接收模型》
只需将相应的类型换掉即可,这里用的是:

// 创建监听器对象m_pListener = ::Create_HP_TcpPullServerListener();// 创建 Socket 对象m_Server = ::Create_HP_TcpPullServer(m_pListener);

2.2添加hp-socket头文件和lib,dll文件到项目中

2.2.1添加头文件

复制ldcsaa-HP-Socket-master\HP-Socket\Windows\Include\HPSocket目录下两个h文件到项目目录下

2.2.2添加lib文件和dll文件

1.复制ldcsaa-HP-Socket-master\HP-Socket\Windows\Bin\HPSocket4C\x86目录下dll、lib文件到项目目录下

2.附加依赖项

2.3编写main.cpp

server端编码原则我们是按照hp-socket development guide来的,可以看到跟原生的socket编程差不多。

监听器接口 回调事件 描述
ISocketListenerT OnHandShake() 握手完成
ISocketListenerT OnSend() 数据已发送
ISocketListenerT OnReceive() 数据到达
ISocketListenerT OnClose 连接关闭
IComplexSocketListenerT OnShutdown 关闭通信组件
IServerListenerT OnPrepareListen() 准备监听
IServerListenerT OnAccept() 接受连接请求

具体步骤:
1.创建监听器对象
2.创建 Socket 对象
3.设置 Socket 监听器回调函数

一共只有7个回调函数,自己处理好即可。代码如下,不超过200行,即实现了简单的tcp server框架搭建。

#include "HPSocket4C.h"
#include <stdio.h>//8字节包头,固定长度
struct TPkgHeader
{DWORD seq;int body_len;
};struct TPkgBody
{char name[30];short age;char desc[1];
};struct TPkgInfo
{bool is_header;int length;TPkgInfo(bool header = true, int len = sizeof(TPkgHeader)) : is_header(header), length(len) {}void Reset() { is_header = true, length = sizeof(TPkgHeader); }~TPkgInfo() {}
};TPkgInfo* FindPkgInfo(HP_Server pSender, CONNID dwConnID)
{PVOID pInfo = nullptr;::HP_Server_GetConnectionExtra(pSender, dwConnID, &pInfo);return (TPkgInfo*)pInfo;
}void RemovePkgInfo(HP_Server pSender, CONNID dwConnID)
{TPkgInfo* pInfo = FindPkgInfo(pSender, dwConnID);delete pInfo;
}En_HP_HandleResult __stdcall OnPrepareListen(HP_Server pSender, SOCKET soListen)
{TCHAR szAddress[50];int iAddressLen = sizeof(szAddress) / sizeof(TCHAR);USHORT usPort;::HP_Server_GetListenAddress(pSender, szAddress, &iAddressLen, &usPort);return HR_OK;
}En_HP_HandleResult __stdcall OnAccept(HP_Server pSender, HP_CONNID dwConnID, SOCKET soClient)
{BOOL bPass = TRUE;TCHAR szAddress[50];int iAddressLen = sizeof(szAddress) / sizeof(TCHAR);USHORT usPort;TPkgInfo* pInfo = new TPkgInfo(true, sizeof(TPkgInfo));::HP_Server_SetConnectionExtra(pSender, dwConnID, pInfo);::HP_Server_GetRemoteAddress(pSender, dwConnID, szAddress, &iAddressLen, &usPort);printf("%s:%d connected...\n", szAddress, usPort);return bPass ? HR_OK : HR_ERROR;
}En_HP_HandleResult __stdcall OnSend(HP_Server pSender, HP_CONNID dwConnID, const BYTE* pData, int iLength)
{return HR_OK;
}En_HP_HandleResult __stdcall OnReceive(HP_Server pSender, HP_CONNID dwConnID, int iLength)
{TPkgInfo* pInfo = FindPkgInfo(pSender, dwConnID);int required = pInfo->length;int remain = iLength;while (remain >= required){remain -= required;BYTE* buffer = new BYTE[required];En_HP_FetchResult result;result = ::HP_TcpPullServer_Fetch(pSender, dwConnID, buffer, required);if (result == FR_OK){if (pInfo->is_header){TPkgHeader* pHeader = (TPkgHeader*)buffer;printf("[Server] head -> seq: %d, body_len: %d\n", pHeader->seq, pHeader->body_len);required = pHeader->body_len;}else{TPkgBody* pBody = (TPkgBody*)(BYTE*)buffer;printf("[Server] body -> name: %s, age: %d, desc: %s\n", pBody->name, pBody->age, pBody->desc);required = sizeof(TPkgHeader);}pInfo->is_header = !pInfo->is_header;pInfo->length = required;if (!::HP_Server_Send(pSender, dwConnID, buffer, required))return HR_ERROR;}}return HR_OK;
}En_HP_HandleResult __stdcall OnClose(HP_Server pSender, HP_CONNID dwConnID, En_HP_SocketOperation enOperation, int iErrorCode)
{printf("[connID=%d] closed\n", dwConnID);return HR_OK;
}En_HP_HandleResult __stdcall OnShutdown(HP_Server pSender)
{return HR_OK;
}int main()
{HP_TcpServer m_Server;HP_TcpServerListener m_pListener;// 创建监听器对象m_pListener = ::Create_HP_TcpPullServerListener();// 创建 Socket 对象m_Server = ::Create_HP_TcpPullServer(m_pListener);// 设置 Socket 监听器回调函数::HP_Set_FN_Server_OnPrepareListen(m_pListener, OnPrepareListen);::HP_Set_FN_Server_OnAccept(m_pListener, OnAccept);::HP_Set_FN_Server_OnSend(m_pListener, OnSend);::HP_Set_FN_Server_OnPullReceive(m_pListener, OnReceive);::HP_Set_FN_Server_OnClose(m_pListener, OnClose);::HP_Set_FN_Server_OnShutdown(m_pListener, OnShutdown);if (::HP_Server_Start(m_Server, "127.0.0.1", 6000)){printf("start listrn successfully\n");}else{printf("start listrn failed\n");}getchar();
}

值得注意是有两点:
1.::HP_Set_FN_Server_OnPullReceive(m_pListener, OnReceive);函数中OnReceive函数声明不同于push,pack模型;
2.在组件的OnReceive()回调函数中,总是先fetch出包头部分,然后根据包头中的length fetch出相应的数据部分;
其实就是应用程序根据应用层协议,配合OnReceive得到的数据长度,按需索取数据,这也是半自动模型名称的含义。
运行如下:

通过本篇的练习,希望对socket或hp-socket框架的pull模型有一定帮助,同时对自己手工实现tcp数据流的解包也有一定参考意义。
本篇源码已上传码云

Hp-socket高性能网络库二--tcp组件pull接收模型相关推荐

  1. socket编程之二:两种链接类型tcp和udp

    前面一篇文章说到了一些计算机网络的基础知识.引入了socket.从这节開始,就进入正题了. 一 概述 TCP:Transimission Control Protocol传输控制协议. UPD:Use ...

  2. C++socket编程(二):系统socket库介绍

    什么是套接字: 套接字是一个介质,由操作系统控制, 下面演示下windows和linux中的socket接口建立的代码,通用 #ifdef WIN32 #include <Windows.h&g ...

  3. Linux网络编程 | Socket编程(二)TCPSocket的封装、TCP服务器多进程、多线程版本的实现

    目录 TCP的通信流程 TCPSocket的封装 TCP客户端 TCP服务器 多进程版本 多线程版本 TCP的通信流程 计算机网络 (三) 传输层 :一文搞懂UDP与TCP协议 在这篇博客中,我描述了 ...

  4. 【★更新★】高性能 Windows Socket 服务端与客户端组件(HP-Socket v2.0.1 源代码及测试用例下载)...

    HP-Socket 以前为某大型通信项目开发了一套通用 Windows Socket TCP 底层通信组件,组件代号为 HP-Socket.现在把 HP-Socket 的所有代码向大众公开,希望能对大 ...

  5. socket第三方库 AsyncSocket(GCDAsyncSocket)

    为什么80%的码农都做不了架构师?>>>    Socket描述了一个IP.端口对.它简化了程序员的操作,知道对方的IP以及PORT就可以给对方发送消息,再由服务器端来处理发送的这些 ...

  6. java socket ip_JAVA 网络编程 TCP/IP、Socket 和协议设计

    [JAVA 网络编程 TCP/IP.Socket 和协议设计] TCP/IP 协议简介 IP 首先我们看 IP(Internet Protocol)协议.IP 协议提供了主机和主机间的通信. 为了完成 ...

  7. linux tcp连接计算机,计算机基础知识——linux socket套接字tcp连接分析

    2016.7.4 今天晚上对项目顶层文件(daemon)进行了分析,对其中的TCP连接进行具体的代码级分析. 1.需求分析 首先得知道我们这里为什么要用TCP连接,我们的整个测试系统是由上位机作为客户 ...

  8. socket套接字TCP API

    socket套接字TCP API socket概念 socket又称"套接字",是计算机网络中进程间通信数据通道的一个端点,或称之为句柄.IP地址+端口号就可以唯一确定一个sock ...

  9. 基于Socket的UDP和TCP编程介绍-转

    一.概述 TCP(传输控制协议)和UDP(用户数据报协议是网络体系结构TCP/IP模型中传输层一层中的两个不同的通信协议. TCP:传输控制协议,一种面向连接的协议,给用户进程提供可靠的全双工的字节流 ...

最新文章

  1. Oracle 常见的33个等待事件
  2. 数据中台实战:商品售前分析
  3. web.xml中相关标签的加载顺序
  4. 计算机函授本科题库,计算机应用基础函授本科考试题库
  5. 前端学习(2275)初始化react
  6. spring boot(十五)spring boot+thymeleaf+jpa增删改查示例
  7. 通过iLO进行Zabbix监控——针对HP服务器集成
  8. 高保真扬声器系统的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告的全球与中国市场
  9. 地铁怎么坐才不能做反_坐地铁怎样才不会坐反方向
  10. 电脑保护眼睛的背景颜色
  11. 在html页面上引用脚本文件,如何在html中调用JS文件
  12. c语言测试软件的编写,用c语言编写智商测试软件
  13. 好用的Mac视频下载器:Allavsoft for Mac中文版
  14. 道德经和译文_老子道德经第五十章原文及译文
  15. 想知道如何批量旋转图片?只要学会这两招就可以
  16. UnitTest单元测试框架详解
  17. 物联网在农业生产中有哪些应用呢
  18. 关于句子embedding的一些工作简介(二)---- InferSent
  19. R1 协议:基于以太坊的去中心化交易协议
  20. Java | Kotlin byte转Int

热门文章

  1. 3dMax 骨骼动画导入到Unity
  2. 解析模板 [index.html] 时出错,模板可能不存在或可能无法被任何已配置的模板解析器访问
  3. keil5安装及51单片机入门程序实例
  4. 如何彻底删除Mac磁盘中的文件
  5. linux命令---dstat
  6. iOS- JSPatch 热更新
  7. 性能优化还不会?吃掉这五个类别,摆平性能优化
  8. Javascript 实现二维码生成
  9. Study JavaScript《JS操作SVG的一些知识》
  10. Ypai windows部署小白也可以哦