一、开始
上一篇我们说到了Hp-Socket提供的3种接收模型,本篇通过代码示例一步一步教大家怎么使用hp-socket进行网络通信程序开发。
HP-Socket 提供 PUSH / PULL / PACK 等接收模型,应用程序可以灵活选择以手工方式、半自动方式或全自动方式处理封解包。
接收模型 | 接收事件 | 说明 |
---|---|---|
PUSH | OnReceive(pSender, dwConnID, pData, iLength) | 手工方式/原生方式 |
PULL | OnReceive(pSender, dwConnID, iLength) | 半自动 |
PACK | OnReceive(pSender, dwConnID, pData, iLength) | 全自动 |
- PUSH 模型组件触发监听器对象的 OnReceive(pSender, dwConnID, pData, iLength)事件时,应用程序需要立即处理接收到的数据,如:粘包处理、协议解析等。组件不会对应用层的数据处理工作提供任何协助。
- PULL 模型组件触发监听器对象的 OnReceive(pSender, dwConnID, iTotalLength) 事件时,应用程序根据应用层协议检测接收到的数据长度(iTotalLength)是否满足处理条件,选择性地进行处理。 当 iTotalLength 小于当前期望的长度时可以忽略本次事 件 ;当iTotalLength 大 于 或 等 于 当 前 期 望 的 长 度 时 , 循 环 调 用 组 件 的Fetch(dwConnID, pData, iDataLength) 方法把需要的数据拉取出来,直到剩余的数据长度小于当前期望的长度。PULL 模型适用于完全清楚应用层协议,并且应用层协议可以根据当前数据包得知下一个数据包长度的场景。典型的场景如 Head + Body, Head 长度固定,第一个数据包为 Head,通过 Head 得知 Body 的长度,接收完 Body 之后下一个数据包一定为 Head。
- PACK 模型组件触发监听器对象的 OnReceive(pSender, dwConnID, pData, iLength)事件时,会保证 pData 是一个完整的数据包。 PACK 模型组件会对应用程序发送的每个数据包自动加上 4 字节(32 位) 的包头, 组件接收到数据时根据包头信息自动分包, 每个完整数据包通过 OnReceive 事件发送给应用程序。
二、push模型练习
纯C语言编写,调用HPSocket4C动态库的形式,利用vs2019开发环境,一步步教你使用hp-socket框架进行socket通信开发,对新手非常友好。
2.1准备工作
2.1.1编译得到hp-socket的dll和lib
在HP-Socket\Windows\Project目录下,打开HPSocket-2019.sln,直接编译,会在Bin目录下生成HPSocket4C_D.dll,HPSocket4C_D.lib两个文件。
编译好后会得到两个版本:c++版本(HPSocket)
c版本(HPSocket4C)
2.2开发步骤
2.2.1 新建项目工程
打开vs2019新建c++空项目TcpServer_push,我们选取hp-socket最原生的push接收模型,对push、pull、pack不清楚的可以查看上一篇《Hp-Socket高性能网络库介绍零--整体结构》,里面有详细描述三者的区别。
2.2.2添加hp-socket头文件和lib,dll文件到项目中
复制ldcsaa-HP-Socket-master\HP-Socket\Windows\Include\HPSocket目录下两个h文件到项目目录下
a.复制ldcsaa-HP-Socket-master\HP-Socket\Windows\Bin\HPSocket4C\x86目录下dll、lib文件到项目目录下
b.附加依赖项
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个回调函数,自己处理好即可。代码如下,不超过80行,即实现了简单的tcp server框架搭建。
#include "HpInclude/HPSocket4C.h"
#include <stdio.h>
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;
::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, const BYTE* pData, int iLength)
{
printf("receive data [connID=%d]:%s\n", dwConnID, pData);
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_pServer;
HP_TcpServerListener m_pListener;
// 创建监听器对象
m_pListener = ::Create_HP_TcpServerListener();
// 创建 Socket 对象
m_pServer = ::Create_HP_TcpServer(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_OnReceive(m_pListener, OnReceive);
::HP_Set_FN_Server_OnClose(m_pListener, OnClose);
::HP_Set_FN_Server_OnShutdown(m_pListener, OnShutdown);
if (::HP_Server_Start(m_pServer, "127.0.0.1", 6000))
{
printf("start tcp server successfully\n");
}
else
{
printf("start tcp server failed\n");
return 0;
}
getchar();
}
至此F7应该能编译成功,如果编译不成功,查看下是否是“使用多字节字符集”
运行如下:
三、最后
通过本篇的讨论,我们了解了一般tcp网络通信程序中需要考虑的几点问题,学习了hp-socket的3种接收模型,并完成了push接收模型的tcp server框架的纯c语言版本,希望对socket或hp-socket框架初学者有一定帮助。