我的编程空间,编程开发者的网络收藏夹
学习永远不晚

在C++中使用HP-Socket

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

在C++中使用HP-Socket

1、简介

HP-Socket 是一套通用的高性能 TCP/UDP /HTTP 通信 框架 ,包含服务端组件、客户端组件和 Agent 组件,广泛适用于各种不同应用场景的 TCP/UDP /HTTP 通信系统,提供 C/C++ C# Delphi E (易语言)、 Java Python 等编程语言接口。
HP-Socket是一套国产的开源通讯库,使用C++语言实现,提供多种编程语言的接口,支持 Windows 和 Linux 平台:

  • 官网:http://www.hpsocket.net/
  • github:https://github.com/ldcsaa/HP-Socket

HP-Socket包含30多个组件 ,可根据通信角色Client/Server)、通信协议TCP/UDP/HTTP)和接收模型PUSH/PULL/PACK)进行归类,这里只简单介绍一下:

  • Server组件:基于IOCP/EPOLL通信模型 ,并结合缓存池 、私有堆等技术实现高效内存管理,支持超大规模、高并发通信场景。
  • Agent组件:实质上是Multi-Client组件,与Server组件采用相同的技术架构,可同时建立和高效处理大规模Socket连接 。
  • Client组件:基于Event Select/POLL通信模型,每个组件对象创建一个通信线程并管理一个Socket连接, 适用于小规模客户端场景。
  • Thread Pool组件:HP-Socket实现的高效易用的线程池组件,当成普通的第三方线程池库使用即可。

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模型的结合体,应用程序不必处理分包与数据抓取,组件保证每个OnReceive事件都向应用程序提供一个完整数据包。

注:PACK模型组件会对应用程序发送的每个数据包自动加上 4 字节(32位的包头),前10位为用于数据包校验的包头标识位,后22位为记录包体长度的长度位。

2、使用方式

HP-Socket支持MBCS和Unicode字符集,支持32位和64位应用程序。可以通过源代码、 DLL或LIB方式使用HP-Socket。 HP-Socket发行包中已经提供了HPSocket DLL和HPSocket4C DLL。
HP-Socket提供了各种情况下的dll文件,不需要我们重新编译,dll文件按编程接口分为两大类:

HPSocket DLL:导出C++编程接口 ,C++程序的首选方式,使用时需要把SocketInterface.h(及其依赖文件HPTypeDef.h) 、HPSocket.h以及 DLL 对应的 *.lib 文件加入到工程项目,用到SSL组件还需要HPSocket-SSL.h文件。

HPSocket4C DLL:导出C编程接口,提供给C语言或其它编程语言使用,使用时需要把HPSocket4C.h以及 DLL 对应的 *.lib 文件加入到工程项目,用到SSL组件还需要HPSocket4C-SSL.h文件。

3、实现简单线程池

使用HP-Socket的线程池组件可以在程序中实现一个简单的、公用的线程池,TCP通讯的断线重连、发送心跳都会用到线程池。

线程池组件的主要函数如下:

  • Start:启动线程池,具体的使用可以参考源代码的注释。
  • Submit:提交任务,主要使用BOOL Submit(fnTaskProc,pvArg,dwMaxWait=INFINITE),另一个函数重载是使用一个特殊的数据类型(把Socket任务参数和任务函数封装成一个数据结构)作为参数。
  • Stop:关闭线程池,参数dwMaxWait代表最大等待时间(毫秒,默认: INFINITE ,一直等待)。

先实现线程池的CHPThreadPoolListener接口,然后构造IHPThreadPool智能指针,后面线程池的操作都通过智能指针操作,

代码如下:


class CHPThreadPoolListenerImpl : public CHPThreadPoolListener
{
private:
 void LogInfo(string logStr)
 {
  cout <<"ThreadPool " <<logStr << endl;
 }
public:
 virtual void OnStartup(IHPThreadPool* pThreadPool) 
 {
  LogInfo("线程池启动");
 }
 virtual void OnShutdown(IHPThreadPool* pThreadPool) 
 {
  LogInfo("线程池启动关闭");
 }
 virtual void OnWorkerThreadStart(IHPThreadPool* pThreadPool, THR_ID dwThreadID) 
 {    
  LogInfo("[" + to_string(dwThreadID) + "] " + "工作线程启动");
 }
 virtual void OnWorkerThreadEnd(IHPThreadPool* pThreadPool, THR_ID dwThreadID) 
 {
  LogInfo("[" + to_string(dwThreadID) + "] " + "工作线程退出");
 }
};

CHPThreadPoolListenerImpl ThreadPoolListener;
//全局共享变量使用extern关键字修饰
extern CHPThreadPoolPtr ThreadPool(&ThreadPoolListener);

4、实现TCP客户端

先实现一个打印函数,显示客户端相关的信息,代码如下:


void PrintInfo(ITcpClient* pSender, CONNID dwConnID)
{
 char buffer[20]; 
 TCHAR* ipAddr = buffer;
 int ipLen;
 USHORT port;

 pSender->GetLocalAddress(ipAddr, ipLen, port); 
 cout << string(ipAddr,0,ipLen) << ":" << port << " " << " [" << dwConnID << "] -> ";

 pSender->GetRemoteHost(ipAddr, ipLen, port); 
 cout << string(ipAddr, 0, ipLen) << ":" << port << " ";
}

实现CTcpClientListener监听接口,客户端断线后自动重连,以换行符分割接收到的字符串,

代码如下:


bool SysExit = false;
void ReConnect(ITcpClient* pSender)
{
 while (pSender->GetState() != SS_STOPPED)
 {
  Sleep(10);
 }
 pSender->Start("127.0.0.1", 60000);
}

class CClientListenerImpl : public CTcpClientListener
{

public: 
 virtual EnHandleResult OnConnect(ITcpClient* pSender, CONNID dwConnID) 
 { 
  PrintInfo(pSender, dwConnID);
  cout << "连接成功" << endl;
  return HR_OK;  
 }

 string resStr = "";
 string commStr="";
 virtual EnHandleResult OnReceive(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength) 
 {
  
  string str((char*)pData,0, iLength);
  resStr.append(str);
  int index;
  while (true)
  {
   index = resStr.find("\r\n");
   if (index == -1)break;

   commStr = resStr.substr(0, index);
   resStr = resStr.substr(index +2, resStr.length() - (index +2));
   if (commStr!="")
   {
    PrintInfo(pSender, dwConnID);
    cout << "收到分割字符串 " << commStr << endl;
   }
  } 

  PrintInfo(pSender, dwConnID);
  cout << "数据接受 " << str << endl;

  return HR_OK;
 }

 virtual EnHandleResult OnClose(ITcpClient* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
 {
  resStr = "";

  PrintInfo(pSender, dwConnID);
  cout << "连接断开,"<< enOperation <<"操作导致错误,错误码 " << iErrorCode<< endl;
  if (!SysExit) 
  {
   ThreadPool->Submit((Fn_TaskProc)(&ReConnect), (PVOID)pSender);
  }  
  return HR_OK;
 } 
};

循环输入字符串发送服务端,代码如下:


int main()
{
 //启动线程池
 ThreadPool->Start();

 CClientListenerImpl listener;
 CTcpClientPtr client(&listener);

 if (!client->Start("127.0.0.1", 60000))
 {
  cout << "连接错误:" << client->GetLastError() << "-" << client->GetLastErrorDesc();
 }
 
 string sendMsg;
 while (!SysExit)
 {  
  cin >> sendMsg;
  if (sendMsg == "esc") 
  {
   SysExit = true;
   break;
  }

  if (client->GetState() == SS_STARTED) 
  {
   const BYTE* data = (BYTE*)(sendMsg.c_str());
   if (client->Send(data, sizeof(data)))
   {
    PrintInfo(client, client->GetConnectionID());
    cout << "发送成功 "<<sendMsg<<endl;
   }
   else
   {
    PrintInfo(client, client->GetConnectionID());
    cout << "发送失败,错误描述 " << client->GetLastError() << "-" << client->GetLastErrorDesc() << endl;
   }
  }
  else 
  {
   PrintInfo(client, client->GetConnectionID());   
   cout << "无法发送,当前状态 " <<client->GetState()<< endl;
  }
 } 
 client->Stop();
 //关闭线程池
 ThreadPool->Stop();

 return 0; 
}

5、实现TCP服务端

先实现一个打印函数,基本上和客户端的相同,只有获取本地IP的地方不同,

代码如下:


void PrintInfo(ITcpServer* pSender, CONNID dwConnID)
{
 char buffer[20];
 TCHAR* ipAddr = buffer;
 int ipLen;
 USHORT port;

 pSender->GetListenAddress(ipAddr, ipLen, port);
 cout << string(ipAddr, 0, ipLen) << ":" << port << " " << "<-  [" << dwConnID << "] ";

 pSender->GetRemoteAddress(dwConnID, ipAddr, ipLen, port);
 cout << string(ipAddr, 0, ipLen) << ":" << port << " ";
}

为了演示客户端和应用数据的绑定,定义一个用户数据类型并创建一个队列,代码如下:


class UserData 
{
public:
 UserData(string name="") 
 {
  Name = name;
 }
 string Name;
};
queue<UserData*> qName;  //创建队列对象

实现CTcpServerListener监听接口,收到字符串后加上用户名再发送回去,

代码如下:


class CTcpServerListenerImpl : public CTcpServerListener
{
public: 
 virtual EnHandleResult OnAccept(ITcpServer* pSender, CONNID dwConnID, UINT_PTR soClient)
 {

  pSender->SetConnectionExtra(dwConnID,qName.front());
  qName.pop();
  PrintInfo(pSender, dwConnID);
  cout << "连接成功" << endl;
  return HR_OK;
 }
 virtual EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
 { 
  string str((char*)pData, 0, iLength);
  PrintInfo(pSender, dwConnID);
  cout << "数据接受 " << str<<endl;

  PVOID pInfo = nullptr;  
  pSender->GetConnectionExtra(dwConnID, &pInfo);
  str = "reply-" + ((UserData*)pInfo)->Name + str;

  const BYTE* data = (BYTE*)(str.c_str());  
  pSender->Send(dwConnID, data,str.size());
  return HR_OK;
 } 
 virtual EnHandleResult OnClose(ITcpServer* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
 {
  PVOID pInfo = nullptr;
  pSender->GetConnectionExtra(dwConnID, &pInfo);
  qName.push((UserData*)pInfo);
  PrintInfo(pSender, dwConnID);
  cout << "断开连接"<< endl;

  pSender->SetConnectionExtra(dwConnID, NULL);  
  return HR_OK;
 }
};

循环输入字符串发送到客户端,自动回复客户端发送的消息,代码如下:


bool SysExit = false;
int main()
{ 
 UserData user1("NO1-User");
 UserData user2("NO2-User");
 UserData user3("NO3-User");
 UserData user4("NO4-User");

 qName.push(&user1);
 qName.push(&user2);
 qName.push(&user3);
 qName.push(&user4);

 CTcpServerListenerImpl listener;
 CTcpServerPtr server(&listener);
  
 if (!server->Start("127.0.0.1", 60000))
 {
  cout << "启动错误:" << server->GetLastError() << "-" << server->GetLastErrorDesc();
 }
 
 string sendMsg;
 while (!SysExit)
 {
  cin >> sendMsg;
  if (sendMsg == "esc")
  {
   SysExit = true;
   break;
  }

  //如果数组长度小于当前连接数量,则获取失败
  DWORD count= 1000;   
  CONNID pIDs[1000]; 
  ZeroMemory(pIDs, 1000);;

  if (server->GetAllConnectionIDs(pIDs, count)&& count >0)
  {
   for (size_t i = 0; i < count; i++)
   {
    const BYTE* data = (BYTE*)(sendMsg.c_str());
    if (server->Send(*(pIDs+i),data, sendMsg.size()))
    {
     PrintInfo(server, pIDs[i]);
     cout << "发送成功 " << sendMsg << endl;
    }
    else
    {
     PrintInfo(server, pIDs[i]);
     cout << "发送失败,错误描述 " << server->GetLastError() << "-" << server->GetLastErrorDesc() << endl;
    }
   }  
  }
  else
  {   
   cout << "无法发送,当前连接数 " << count << endl;
  }
 }
 server->Stop();
}

注:获取连接时指针数组的长度一定要大于当前连接数量,否则会失败。

6、实现Http客户端

HP-SocketHttp客户端有同步、异步两种,同步客户端不需要绑定监听器,这里使用同步客户端演示。

Sync Client:同步HTTP客户端组件(CHttpSyncClient和CHttpsSyncClient)内部会处理所有事件,因此,它们不需要绑定监听器(构造方法的监听器参数传入null); 如果绑定了监听器则可以跟踪组件的通信过程。

测试客户端可以使用实时天气接口上面的测试示例,当前的测试示例为:


http://api.k780.com/?app=weather.today&weaId=1&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json

直接开始测试,代码如下:


int main()
{
    CHttpSyncClientPtr SyncClient;
    THeader type;
    type.name = "Content-Type";
    type.value = "text/html;charset=UTF-8";
    
    if (SyncClient->OpenUrl("GET", "http://api.k780.com/?app=weather.today&weaId=1&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json",&type))
    {
        LPCBYTE pData = nullptr;
        int iLength = 0;
        SyncClient->GetResponseBody(&pData, &iLength);        
        string body((char*)pData, iLength);
        //返回的有中文,需要转化编码格式
        cout << body << endl;        
        cout << endl;
        cout << StringToUtf(body) << endl;
        cout << endl;
        cout << UtfToString(StringToUtf(body)) << endl;
    }
    else
    {
        cout << "打开失败:"<<SyncClient->GetLastError()<<"-"<< SyncClient->GetLastErrorDesc()<<endl;
    }   
}

上面的StringToUtfUtfToString函数是转载至C++ 中文乱码的问题,该函数实现UTF-8ANSI编码格式的转化,

代码如下:


string UtfToString(string strValue)
{
    int nwLen = ::MultiByteToWideChar(CP_ACP, 0, strValue.c_str(), -1, NULL, 0);
    wchar_t* pwBuf = new wchar_t[nwLen + 1];//加上末尾'\0'
    ZeroMemory(pwBuf, nwLen * 2 + 2);
    ::MultiByteToWideChar(CP_ACP, 0, strValue.c_str(), strValue.length(), pwBuf, nwLen);
    int nLen = ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, -1, NULL, NULL, NULL, NULL);
    char* pBuf = new char[nLen + 1];
    ZeroMemory(pBuf, nLen + 1);
    ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);
    std::string retStr(pBuf);
    delete[]pwBuf;
    delete[]pBuf;
    pwBuf = NULL;
    pBuf = NULL;
    return retStr;
}

string StringToUtf(string strValue)
{
    int nwLen = MultiByteToWideChar(CP_UTF8, 0, strValue.c_str(), -1, NULL, 0);
    wchar_t* pwBuf = new wchar_t[nwLen + 1];//加上末尾'\0'
    memset(pwBuf, 0, nwLen * 2 + 2);
    MultiByteToWideChar(CP_UTF8, 0, strValue.c_str(), strValue.length(), pwBuf, nwLen);
    int nLen = WideCharToMultiByte(CP_ACP, 0, pwBuf, -1, NULL, NULL, NULL, NULL);
    char* pBuf = new char[nLen + 1];
    memset(pBuf, 0, nLen + 1);
    WideCharToMultiByte(CP_ACP, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);
    std::string retStr = pBuf;
    delete[]pBuf;
    delete[]pwBuf;
    return retStr;
}

注:函数实现需放在main函数之前。

到此这篇关于在C++中使用HP-Socket的文章就介绍到这了,更多相关C++ 使用HP-Socket内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

附件:

HP-Socket-5.8.5 源码+dll+文档 提取码: 2uyv
项目源码 提取码: 2uyv

到此这篇关于在C++中使用HP-Socket的文章就介绍到这了,更多相关C++ 使用HP-Socket内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

在C++中使用HP-Socket

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

在c#中使用Socket实现一个tcp协议

这篇文章给大家介绍在c#中使用Socket实现一个tcp协议,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。一、概述UDP和TCP是网络通讯常用的两个传输协议,C#一般可以通过Socket来实现UDP和TCP通讯,由于.
2023-06-06

如何在C#中使用Socket读取超大的数据

这期内容当中小编将会给大家带来有关如何在C#中使用Socket读取超大的数据,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。/// /// 返回摄像头信息 /
2023-06-06

java中socket怎么使用

在Java中使用Socket进行网络通信可以分为服务端和客户端两部分。服务端:1. 创建ServerSocket对象,并指定端口号。2. 调用ServerSocket的accept()方法接收客户端连接请求,返回一个Socket对象。3.
2023-09-23

vue中如何使用socket

这篇“vue中如何使用socket”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“vue中如何使用socket”文章吧。一、W
2023-07-06

怎么在python中使用socket连接客户端

本篇文章给大家分享的是有关怎么在python中使用socket连接客户端,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Python的优点有哪些1、简单易用,与C/C++、Jav
2023-06-14

在Java中使用Socket通信会遇到哪些问题

本篇文章给大家分享的是有关在Java中使用Socket通信会遇到哪些问题,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Socket通信模型如图所示:不管Socket通信的功能有
2023-05-31

如何在C#中使用Filewatcher

要在C#中使用FileWatcher,首先需要创建一个FileWatcher对象,并设置所需的属性和事件处理程序。以下是一个简单的示例,展示了如何在C#中使用FileWatcher来监视文件的创建、修改和删除事件:```csharpusin
2023-09-27

dynamic怎么在c#中使用

这篇文章给大家介绍 dynamic怎么在c#中使用,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。dynamic是FrameWork4.0的新特性。dynamic的出现让C#具有了弱语言类型的特性。编译器在编译的时候不再
2023-06-14

Dictionary怎么在C#中使用

这篇文章给大家介绍Dictionary怎么在C#中使用,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Dictionary需要注意的特性1.任何键都必须是唯一的 ——> 不能添加相同key的键值对,不然就报错:如果要修改
2023-06-14

如何在java中使用socket对zip文件进行传输

今天就跟大家聊聊有关如何在java中使用socket对zip文件进行传输,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。服务器端程序:import java.io.*;import j
2023-05-31

怎么在Java中使用Socket实现一个多人聊天室

本篇文章给大家分享的是有关怎么在Java中使用Socket实现一个多人聊天室,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。流程首先建立一个服务器端,构建ServerSocket
2023-06-14

C#中怎么利用Socket实现异步通讯

这篇文章将为大家详细讲解有关C#中怎么利用Socket实现异步通讯,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。C# Socket异步通讯客户端之主程序:using System;using
2023-06-17

在C#中怎么使用Dapper ORM

这篇文章将为大家详细讲解有关在C#中怎么使用Dapper ORM,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。使用 Visual Studio 安装 Dapper ORM要想了解 Dapper,从下面的步
2023-06-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录