一个ACE 架构的 Socket Client
.h
/**************************************************************
* Filename: TcpClient.h
* Copyright: Shanghai X Co., Ltd.
*
* Description: TcpClient头文件.
*
* @author: w
* @version 10/28/2016 @Reviser Initial Version
**************************************************************/ #ifndef _TCPCLIENT_
#define _TCPCLIENT_ #include <string>
#include <ace/Svc_Handler.h>
#include <ace/Connector.h>
#include <ace/SOCK_Connector.h>
#include <ace/Task.h> using namespace std; //连接状态改变时回调
typedef void (__stdcall *pfnConnectChangeCallBack)(bool);
//接收到数据时回调
typedef void (__stdcall *pfnReceiveCallBack)(char*, const int); class CTcpClient : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_MT_SYNCH>
{
public:
// 是否退出的标识
long m_lStop; public:
// 是否允许重连
bool m_nReconnect;
// 通信超时ms
int m_nCommunicateTimeOut; public:
//typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_MT_SYNCH> Base;
CTcpClient();
virtual ~CTcpClient(); public:
/**
* 设置连接参数.
*
* @param -[in] char* szHost: [主链接地址];
* @param -[in] char* szBackup: [备连接地址];
* @param -[in] int nRemotePort: [目标端口号];
* @param -[in] int nLocalPort: [本地端口号];
* @return int.
* @version 10/28/2016 w Initial Version
*/
virtual long SetConnectParam(char* szHost, char* szBackup, int nRemotePort, int nLocalPort = );
/**
* 首次连接.
*
* @return int.
* @version 10/28/2016 w Initial Version
*/
virtual int Connect(); /**
* 断开连接.
*
* @return int.
* @version 10/28/2016 w Initial Version
*/
virtual int Reconnect();
/**
* 断开连接.
*
* @return int.
* @version 10/28/2016 w Initial Version
*/
virtual int Disconnect(); /**
* 发送数据.
*
* @param -[in,out] char* szSend: [数据]
* @param -[in] char* lSendSize: [大小]
* @param -[in] int nCommunicateTimeOut: [超时ms]
* @return int.
* @version 10/28/2016 w Initial Version
*/
virtual long Send(const char* szSend, long lSendSize, int nCommunicateTimeOut = COMMUNICATE_TIMEOUT); /**
* 接收数据.
*
* @param -[in,out] char* szReceive: [数据]
* @param -[in] long lReceiveSize: [大小]
* @param -[in] int nCommunicateTimeOut: [超时ms]
* @version 10/28/2016 w Initial Version
*/
virtual long Receive(char* szReceive, long lReceiveSize, int nCommunicateTimeOut = COMMUNICATE_TIMEOUT);
//
virtual bool IsConnected() { return m_nIsConnected; }
/**
* 设置连接改变回调函数.
*
* @version 10/28/2016 w Initial Version
*/
void SetOnConnectChangeCallBack(pfnConnectChangeCallBack func);
/**
* 设置数据接收回调函数.
*
* @version 10/28/2016 w Initial Version
*/
void SetOnReceiveCallBack(pfnReceiveCallBack func); public:
/**
* 建立连接时被调用.
*
* @param -[in,out] char* param: [参数]
* @return int.
* @version 10/28/2016 w Initial Version
*/
int open(void* param = ); /**
* 当有输入时该函数被调用.
*
* @param -[in] ACE_HANDLE: [参数]
* @return int.
* @version 10/28/2016 w Initial Version
*/
int handle_input(ACE_HANDLE handle = ACE_INVALID_HANDLE); /**
* 当有输出时该函数被调用.
*
* @param -[in] ACE_HANDLE handle: [参数]
* @return int.
* @version 10/28/2016 w Initial Version
*/
virtual int handle_output(ACE_HANDLE handle = ACE_INVALID_HANDLE); /**
* 当SockHandler从ACE_Reactor中移除时该函数被调用.
*
* @param -[in] ACE_HANDLE handle: [参数]
* @param -[in] ACE_HANDLE closeMask: [参数]
* @return int
* @version 10/28/2016 w Initial Version
*/
int handle_close(ACE_HANDLE handle, ACE_Reactor_Mask closeMask); /**
* 任务的主流程.
* 1.激活事件
*
* @return int.
* @version 10/10/2016 w Initial Version
*/
int svc(); protected:
/**
* 触发连接改变回调函数.
*
* @param -[in] bool nIsConnected: [是否已连接]
*
* @version 10/28/2016 w Initial Version
*/
void OnConnectChange(bool nIsConnected); /**
* 触发数据接收回调函数.
*
* @param -[in,out] char* pszReceive: [接收的数据区]
* @param -[in] const int nReceiveSize: [数据大小]
* @version 10/28/2016 w Initial Version
*/
void OnReceive(char* pszReceive, const int nReceiveSize); protected:
// 是否已经连接
bool m_nIsConnected;
// 当前连接IP地址
string m_strConnectIPAddress;
// 主线连接IP地址
string m_strHostIPAddress;
// 备线连接IP地址
string m_strBackupIPAddress;
// 远程连接端口号
unsigned short m_nRemotePort;
// 本地连接端口号
unsigned short m_nLocalPort; pfnConnectChangeCallBack m_pfnOnConnectChange;
pfnReceiveCallBack m_pfnOnReceive; protected:
// 最后一次连接时间
time_t m_tmLastConnect; private:
/**
* 关闭Socket.
*
* @return int.
* @version 10/28/2016 w Initial Version
*/
int CloseSocket(); };
//typedef ACE_Connector<CTcpClient, ACE_SOCK_CONNECTOR> CONNECTOR; #endif // !_TCPCLIENT_
.cpp
/**************************************************************
* Filename: TcpClient.cpp
* Copyright: Shanghai X Co., Ltd.
*
* Description: TcpClient源文件.
*
* @author: w
* @version 10/28/2016 @Reviser Initial Version
**************************************************************/ #include "TcpClient.h"
#include <iostream>
#include <string> #include <ace/ACE.h>
#include <ace/OS_NS_sys_socket.h>
#include <ace/OS_NS_strings.h> using namespace std; //ctor
CTcpClient::CTcpClient()
{
m_lStop = true;
m_nReconnect = true;
m_nIsConnected = false; m_pfnOnConnectChange = NULL;
m_pfnOnReceive = NULL; m_strConnectIPAddress = "";
m_strHostIPAddress = "";
m_strBackupIPAddress = "";
m_nRemotePort = ;
m_nLocalPort = ; m_tmLastConnect = ;
}
//dctor
CTcpClient::~CTcpClient()
{
m_lStop = true;
wait();
//
close();
m_pfnOnConnectChange = NULL;
m_pfnOnReceive = NULL;
} long CTcpClient::SetConnectParam(char* szHost, char* szBackup, int nRemotePort, int nLocalPort)
{
m_strConnectIPAddress = m_strHostIPAddress = szHost;
m_strBackupIPAddress = szBackup;
m_nRemotePort = nRemotePort;
m_nLocalPort = nLocalPort;
return ;
} void CTcpClient::SetOnConnectChangeCallBack(pfnConnectChangeCallBack func)
{
this->m_pfnOnConnectChange = func;
} void CTcpClient::SetOnReceiveCallBack(pfnReceiveCallBack func)
{
this->m_pfnOnReceive = func;
} void CTcpClient::OnConnectChange(bool nIsConnected)
{
m_nIsConnected = nIsConnected;
if(!m_nIsConnected)
Log(LOGLEVEL_ERROR, "Disconnect from(%s:%d).", m_strConnectIPAddress.c_str(), m_nRemotePort);
else
Log(LOGLEVEL_NOTICE, "Connected to(%s:%d).", m_strConnectIPAddress.c_str(), m_nRemotePort);
//
if(m_pfnOnConnectChange)
m_pfnOnConnectChange(m_nIsConnected);
} void CTcpClient::OnReceive(char* pszReceive, const int nReceiveSize)
{
ACE_Message_Block *pFrame = new ACE_Message_Block(nReceiveSize);
memcpy(pFrame->wr_ptr(), pszReceive, nReceiveSize);
pFrame->wr_ptr(nReceiveSize);
this->putq(pFrame);
/*if(m_pfnOnReceive)
m_pfnOnReceive(pszReceive, nReceiveSize);*/
delete[] pszReceive;
} int CTcpClient::Disconnect()
{
CloseSocket();
OnConnectChange(false);
return ;
} int CTcpClient::CloseSocket()
{
//ACE_OS::shutdown(get_handle(), ACE_SHUTDOWN_BOTH);
//int nRet = ACE_OS::closesocket(m_sockHandler.get_handle());
this->peer().close();
set_handle(ACE_INVALID_HANDLE);
return ;
} int CTcpClient::Reconnect()
{
//已连接
if (IsConnected())
return ;
//未设置重连机制
if(!m_nReconnect)
{
Log(LOGLEVEL_INFO, "Reconnect is disabled.");
return -;
}
//小于超时时间3s不能重连
time_t tmNow;
time(&tmNow);
if(abs(tmNow - m_tmLastConnect) <= CONNECTION_TIMEOUT)
return -;
//清理Socket
CloseSocket();
return Connect();
} int CTcpClient::Connect()
{
//与服务器建立连接
CTcpClient *pSockHandler = this;
//创建连接器
ACE_Connector<CTcpClient, ACE_SOCK_CONNECTOR> connector;
//设置默认连接超时
ACE_Time_Value connTimeOut(CONNECTION_TIMEOUT);
ACE_Synch_Options synch_option(ACE_Synch_Options::USE_TIMEOUT, connTimeOut);
//远程端点
ACE_INET_Addr remoteEP(m_nRemotePort, m_strConnectIPAddress.c_str());
Log(LOGLEVEL_INFO, "Connecting to(%s:%d) ...", remoteEP.get_host_addr(), remoteEP.get_port_number());
//更新当前连接时间戳
time(&m_tmLastConnect);
int nRet = ;
if (m_nLocalPort > )
{
//绑定本地固定端口号
ACE_INET_Addr localEP(m_nLocalPort);
nRet = connector.connect(pSockHandler, remoteEP, synch_option, localEP);
}
else
{
//绑定本地随机端口号
nRet = connector.connect(pSockHandler, remoteEP, synch_option);
}
//连接失败
if(nRet == -)
{
//轮询切换连接主备服务器(存在)
if(!m_strBackupIPAddress.empty() && m_strBackupIPAddress.compare(m_strHostIPAddress) != )
{
m_strConnectIPAddress =
m_strConnectIPAddress.compare(m_strHostIPAddress) == ? m_strBackupIPAddress : m_strHostIPAddress;
}
OnConnectChange(false);
return -;
}
//启动接收事件(OneTime)
if(!nRet && m_lStop)
{
m_lStop = false;
this->activate(THR_NEW_LWP | THR_JOINABLE |THR_INHERIT_SCHED);
}
OnConnectChange(true);
return ;
} int CTcpClient::svc()
{
//接收
while(!m_lStop)
{
ACE_Time_Value tvSleep;
tvSleep.msec(TASK_NAP_TIME_VALUE);
ACE_OS::sleep(tvSleep);
ACE_Time_Value tvWaite(, TASK_NAP_TIME_VALUE);
//BLOCKED
this->reactor()->handle_events(&tvWaite);
}
return ;
} int CTcpClient::open(void* param)
{
return this->reactor()->register_handler(this, ACE_Event_Handler::READ_MASK | ACE_Event_Handler::WRITE_MASK);
//if (Base::open(param) == -1)
//{
// Log(LOGLEVEL_ERROR, "open() Failied.");
// return -1;
//}
//return 0;
} int CTcpClient::handle_input(ACE_HANDLE)
{
char *szBuffer = new char[DEFAULT_BUFFER_SIZE];
//接收数据
ssize_t length = this->peer().recv(szBuffer, DEFAULT_BUFFER_SIZE);
//连接断开接收失败
if(length <= )
{
delete[] szBuffer;
return -;//implicit call handle_close() clear up
}
OnReceive(szBuffer, length);
return ;
} int CTcpClient::handle_close(ACE_HANDLE handle, ACE_Reactor_Mask closeMask)
{
int nRet = ACE_Event_Handler::handle_close(handle, closeMask);
Disconnect();
return nRet;
} int CTcpClient::handle_output(ACE_HANDLE handle /* = ACE_INVALID_HANDLE */)
{
//调用一次
return ;
} long CTcpClient::Receive(char* szReceive, long lReceiveSize, int nCommunicateTimeOut)
{
//Confirmed
//implicit call handle_close() clear up
if(!IsConnected())
return -; ACE_Time_Value tvTimeout(, nCommunicateTimeOut);
//return this->peer().recv((void *)szReceive, lReceiveSize, &tvTimeout);
return this->peer().recv_n((void *)szReceive, lReceiveSize, &tvTimeout);
} long CTcpClient::Send(const char* szSend, long lSendSize, int nCommunicateTimeOut)
{
//Uncertainty
//implicit call handle_close() clear up
if(!IsConnected())
return -; ACE_Time_Value tvTimeout(, nCommunicateTimeOut);
ssize_t length = this->peer().send_n(szSend, lSendSize, &tvTimeout);
return length;
}
一个ACE 架构的 Socket Client的更多相关文章
- 一个ACE 架构的 C++ Timer
.h #ifndef _Timer_Task_ #define _Timer_Task_ #pragma once #include <ace/Task.h> #include <a ...
- Python编程-架构、Socket
一.客户端/服务器架构 1.C/S架构 Client/Server架构,即服务器/客户端架构. 客户端和服务器端的程序不同,用户的程序主要在客户端,服务器端主要提供数据管理.数据共享.数据及系统维护和 ...
- [原]一个简单的Linux TCP Client所涉及到的头文件
今天在Linux环境下写了一个最简单的TCP Client程序,没想到Linux环境下的头文件竟然这么分散,让我这样的菜鸟很是郁闷啊.编译成功的代码如下: #include <iostream& ...
- python -socket -client
socket client 发起连接. 流程为: 创建接口 发起连接 创建接口参数同socket server相同 发起连接的函数为socket.connect(ip,port) 这个地方的ip与po ...
- workerman是一个高性能的PHP socket服务器框架
workerman-chatorkerman是一款纯PHP开发的开源高性能的PHP socket服务器框架.被广泛的用于手机app.手游服务端.网络游戏服务器.聊天室服务器.硬件通讯服务器.智能家居. ...
- .Net机试题——编写一个BS架构的多层表结构的信息管理模块
要求: 编写一个BS架构的多层表结构的信息管理模块,用户体验需要注意.包含错误处理,需要最终能完整的跑起来.页面可以不美化,但是整洁还是必须的.在不能完成详细功能需求的情况下优先保证基本功能. 1 ...
- 一个Android 架构师的成长之路
前言 总所周知,当下流行的编程语言有Java.PHP.C.C++.Python.Go等.其中,稳坐榜首的仍然是Java编程语言,且在以面向对象思想占主导的应用开发中,Java往往成为其代名词.Java ...
- Kubernetes实战 - 从零开始搭建微服务 1 - 使用kind构建一个单层架构Node/Express网络应用程序
使用kind构建一个单层架构Node/Express网络应用程序 Kubernetes实战-从零开始搭建微服务 1 前言 准备写一个Kubernetes实战系列教程,毕竟cnblogs作为国内最早的技 ...
- C语言写了一个socket client端,适合windows和linux,用GCC编译运行通过
////////////////////////////////////////////////////////////////////////////////* gcc -Wall -o c1 c1 ...
随机推荐
- Java实现 LeetCode 391 完美矩形
391. 完美矩形 我们有 N 个与坐标轴对齐的矩形, 其中 N > 0, 判断它们是否能精确地覆盖一个矩形区域. 每个矩形用左下角的点和右上角的点的坐标来表示.例如, 一个单位正方形可以表示为 ...
- Swift 语法总结
1,用 var 定义变量 ,与js类似. let 用于定义常量,定义完后不能修改. var 用于定义变量,可以修改. swift可以自动识别属性类别. 2,使用 import 语句来引入任何的 Obj ...
- 在WinForms里嵌入MediaPlayer的一些版本问题, tlbimp导入, 以及不导入而纯用C#+字符串来动态调用.
网上很多写使用WindowsMediaPlayer WMP控件的文章. 大多数都是从工具栏或COM导入. 最近正在做的CEF整合Asp.Net Core Blazor server side的过程中, ...
- 面试官:说说Redis的Hash底层 我:......(来自阅文的面试题)
redis源码分析系列文章 [Redis源码系列]在Liunx安装和常见API 为什么要从Redis源码分析 String底层实现——动态字符串SDS Redis的双向链表一文全知道 前言 hello ...
- pip install 执行过程中遇到的各种问题
一.pip install 安装指定版本的包 要用 pip 安装指定版本的 Python 包,只需通过 == 操作符 指定. pip install robotframework == 2.8.7 将 ...
- root和sudo
root用户是系统中唯一的超级管理员,它具有等同于操作系统的权限.一些需要root权限的应用,譬如广告阻挡,是需要root权限的.可问题在于root比windows的系统管理员的能力更大,足以把整个系 ...
- 【转】Windows下PATH等环境变量详解
[转]“肖凡的专栏” 博客,请务必保留此出处http://legend2011.blog.51cto.com/3018495/553255 在学习JAVA的过程中,涉及到多个环境变量(environm ...
- Windows7/10实现ICMP(ping命令)
如果觉得本文如果帮到你或者你想转载都可以,只需要标注出处即可.谢谢 利用ICMP数据包.C语言实现Ping命令程序,能实现基本的Ping操作,发送ICMP回显请求报文,用于测试—个主机到只一个主机之间 ...
- spring boot actuator端点高级进阶metris指标详解、git配置详解、自定义扩展详解
https://www.cnblogs.com/duanxz/p/3508267.html 前言 接着上一篇<Springboot Actuator之一:执行器Actuator入门介绍>a ...
- 调用php命令出错
调用php -v命令.php artisan route:list等命令均出现一下错误. MIB search path: c:/usr/share/snmp/mibsCannot find modu ...