半导体:Gem/Secs基本协议库的开发(4)
继续接上篇 《半导体:Gem/Secs基本协议库的开发(3)》,本篇我们分享的比较简单,windows系统下tcp和串口通讯。这也是我们协议开发比较重要的一部分,不过我们在此把它封装程一个单独的通讯库,毕竟,它的作用也只是收发消息而已。so easy~
[codes]
// Commucation.proTEMPLATE = lib
DEFINES += COMMUCATION_LIBRARY
TARGET = JC_Commucation
CONFIG += c++11 no_debug_releasewin32:CONFIG(release, debug|release){DESTDIR = $${PWD}/../../../deploy/lib/Release
}
else:win32:CONFIG(debug, debug|release){DESTDIR = $${PWD}/../../../deploy/lib/Debug
}OBJECTS_DIR = $${PWD}/../../../build/$${TARGET}/obj
MOC_DIR = $${PWD}/../../../build/$${TARGET}/mocSOURCES += \commucation.cpp \commucationbase.cpp \serialportobject_win.cpp \#stringhelper.cpp \tcpclientobject_win.cpp \tcpserverobject_win.cppHEADERS += \commucation.h \commucationbase.h \serialportobject_win.h \#stringhelper.h \tcpclientobject_win.h \tcpserverobject_win.h#BEFORE_LINK_CMD_LINE = echo begin_to_compile_JC_Commucation!
#QMAKE_PRE_LINK += $$quote($$BEFORE_LINK_CMD_LINE)
#AFTER_LINK_CMD_LINE = '$$PWD/move.bat' commucation.h ../../../deploy/include/$$TARGET
#QMAKE_POST_LINK += $$quote($$AFTER_LINK_CMD_LINE)
// commucation.h
#ifndef COMMUCATION_H
#define COMMUCATION_H#if defined(COMMUCATION_LIBRARY)
# define _API extern "C" __declspec(dllexport)
#else
# define _API extern "C" __declspec(dllimport)
#endif#include <iostream>
#include <winsock.h>
#define JC_UNUSED(x) (void)x;typedef enum CommucationType
{TcpServer,TcpClient,SerialPort
}CommType;struct EthernetCommucationParam{__int32 nT3; // Reply timeout__int32 nT5; // Connect separation timeout__int32 nT6; // Control transaction timeout__int32 nT7; // Not selected timeout__int32 nT8; // Network intercharacter timeout__int32 nConnectMode; // 1=Passive, 0=Active__int32 nPort; // port, set default as 5000__int32 nDeviceID; // Session ID(device ID),set default as 0char DeviceName[50]; // Describle a device ,could be empty.char pIP[24]; // a string IP "127.0.0.1"
};struct SerialCommucationParam{uint32_t portNo = 1;uint32_t baud;char parity; // check byte, 'Y' or 'N'uint32_t databits;uint32_t stopsbits;
};typedef struct CommucationParam{EthernetCommucationParam eParam;SerialCommucationParam sParam;
}CommParam;class ICommucation;/// rigister call back event
typedef void (*OnMsgRecivedEvent) (ICommucation* pComm, char* message,int iRecvSize,void *pClientData);
typedef void (*OnStateChangedEvent) (ICommucation* pComm, __int32 nState, void *pClientData);
typedef void (*OnAsyncMsgTimeoutEvent) (ICommucation* pComm, __int32 nTransfer, void *pClientData);enum SendDirection{H2E , /// Host -> EquipmentE2H /// Equipment -> Host
};class ICommucation
{
public:ICommucation(){}virtual ~ICommucation(){}virtual bool CreateCommObject() = 0 ;virtual void ReleaseCommObject() = 0;virtual void run() = 0;virtual int SendData(SOCKET fd ,const char *msg, int len) = 0;virtual bool SendSyncMessage(std::string strSendBuf, bool needReply,std::string &strRecvMsg, int iTimeOut = 5) = 0;virtual void setEventCallBack(OnMsgRecivedEvent eProc1,OnStateChangedEvent eProc2,OnAsyncMsgTimeoutEvent eProc3) = 0;
};/*** @brief JC_CreatCommObject 创建通信对象* @param type* @param param* @return*/
_API ICommucation * JC_CreatCommObject(CommucationType type,CommucationParam parm);/*** @brief run 在独立的线程中执行消息监听(异步)* @param p*/
_API void JC_RunListenThread(ICommucation* p);/*** @brief JC_ReleaseCommObject 释放通信对象* @param p*/
_API void JC_ReleaseCommObject(ICommucation* p);/*** @brief JC_SetEventCallBack 注册事件回调* @param pObject 通信连接对象* @param pMsgRecivedProc 接收消息的回调函数* @param pStateChangedProc 状态改变的回调函数* @param OnAsyncMsgTimeoutProc 异步发送消息超时回调*/
_API void JC_SetEventCallBack(ICommucation* pObject,OnMsgRecivedEvent pMsgRecivedProc,OnStateChangedEvent pStateChangedProc,OnAsyncMsgTimeoutEvent OnAsyncMsgTimeoutProc);/**
* @brief JC_SendSyncMessage 同步发送消息并接收请求数据
* @param pObject 通信连接对象
* @param direction
* @param data 发送数据
* @param needReply 是否需要回复
* @param pReplyData 接收到的回复数据
* @return
*/
_API bool JC_SendSyncMessage( ICommucation* pObject,const SendDirection direction,const std::string& data,const bool needReply,std::string& pReplyData);/*!
* \brief JC_SendAsyncMessage 异步发送消息
* \param pObject
* \param pData
* \return
*/
_API int JC_SendAsyncMessage(ICommucation* pObject, const std::string data);/*!* \brief JC_Version* \return*/
_API const char *JC_CommDllVersion();#endif // COMMUCATION_H
// commucationbase.h#ifndef COMMUCATIONBASE_H
#define COMMUCATIONBASE_H#include "commucation.h"class CommucationBase : public ICommucation
{
public:CommucationBase();virtual ~CommucationBase();virtual bool CreateCommObject();virtual void ReleaseCommObject();virtual void run();virtual int SendData(SOCKET fd, const char *msg, int len);virtual bool SendSyncMessage(std::string strSendBuf, bool needReply, std::string &strRecvMsg, int iTimeOut = 5);virtual void setEventCallBack(OnMsgRecivedEvent eProc1,OnStateChangedEvent eProc2,OnAsyncMsgTimeoutEvent eProc3);};#endif // COMMUCATIONBASE_H
// commucationbase.cpp#include "commucationbase.h"CommucationBase::CommucationBase()
{}CommucationBase::~CommucationBase()
{}bool CommucationBase::CreateCommObject()
{return true;
}void CommucationBase::ReleaseCommObject()
{return;
}void CommucationBase::run()
{return;
}int CommucationBase::SendData( SOCKET fd, const char *msg, int len)
{fd,msg,len;return 0;
}bool CommucationBase::SendSyncMessage(std::string strSendBuf, bool needReply, std::string &strRecvMsg, int iTimeOut)
{JC_UNUSED(strSendBuf);JC_UNUSED(needReply);JC_UNUSED(strRecvMsg);JC_UNUSED(iTimeOut);return true;
}void CommucationBase::setEventCallBack(OnMsgRecivedEvent eProc1, OnStateChangedEvent eProc2, OnAsyncMsgTimeoutEvent eProc3)
{JC_UNUSED(eProc1);JC_UNUSED(eProc2);JC_UNUSED(eProc3);return;
}
// serialportobject_win.h
#ifndef SERIALPORTCOMMUCATIONOBJECT_H
#define SERIALPORTCOMMUCATIONOBJECT_H#include "commucationbase.h"
#include <deque>
#include <map>class SerialportCommucationObject : public CommucationBase
{
public:SerialportCommucationObject(CommucationParam param);bool CreateCommObject();void ReleaseCommObject();int SendData(SOCKET fd,const char *msg, int len);bool SendSyncMessage(std::string strSendBuf, bool needReply,std::string &strRecvMsg, int iTimeOut = 5);void run();void setEventCallBack(OnMsgRecivedEvent eProc1,OnStateChangedEvent eProc2,OnAsyncMsgTimeoutEvent eProc3);
private:/** 初始化串口函数* @param: UINT portNo 串口编号,默认值为1,即COM1,注意,尽量不要大于9* @param: UINT baud 波特率,默认为9600* @param: char parity 是否进行奇偶校验,'Y'表示需要奇偶校验,'N'表示不需要奇偶校验* @param: UINT databits 数据位的个数,默认值为8个数据位* @param: UINT stopsbits 停止位使用格式,默认值为1* @param: DWORD dwCommEvents 默认为EV_RXCHAR,即只要收发任意一个字符,则产生一个事件* @return: bool 初始化是否成功* @note: 在使用其他本类提供的函数前,请先调用本函数进行串口的初始化* /n本函数提供了一些常用的串口参数设置,若需要自行设置详细的DCB参数,可使用重载函数* /n本串口类析构时会自动关闭串口,无需额外执行关闭串口* @see:*/bool InitPort(UINT portNo = 1, UINT baud = CBR_9600, char parity = 'N',UINT databits = 8, UINT stopsbits = 1, DWORD dwCommEvents = EV_RXCHAR);/** 串口初始化函数* 本函数提供直接根据DCB参数设置串口参数* @param: UINT portNo* @param: const LPDCB & plDCB* @return: bool 初始化是否成功* @note: 本函数提供用户自定义地串口初始化参数* @see:*/bool InitPort(UINT portNo, const LPDCB& plDCB);/** 开启监听线程* 本监听线程完成对串口数据的监听,并将接收到的数据打印到屏幕输出* @return: bool 操作是否成功* @note: 当线程已经处于开启状态时,返回flase* @see:*/bool OpenListenThread();/** 关闭监听线程* @return: bool 操作是否成功* @note: 调用本函数后,监听串口的线程将会被关闭* @see:*/bool CloseListenTread();/** 向串口写数据* 将缓冲区中的数据写入到串口* @param: unsigned char * pData 指向需要写入串口的数据缓冲区* @param: unsigned int length 需要写入的数据长度* @return: bool 操作是否成功* @note: length不要大于pData所指向缓冲区的大小* @see:*/bool WriteData(unsigned char* pData, unsigned int length);/** 获取串口缓冲区中的字节数* @return: UINT 操作是否成功* @note: 当串口缓冲区中无数据时,返回0* @see:*/UINT GetBytesInCOM();/** 读取串口接收缓冲区中一个字节的数据* @param: char & cRecved 存放读取数据的字符变量* @return: bool 读取是否成功* @note:* @see:*/bool ReadChar(char &cRecved);/** 打开串口* @param: UINT portNo 串口设备号* @return: bool 打开是否成功* @note:* @see:*/bool openPort(UINT portNo);/** 关闭串口* @return: void 操作是否成功* @note:* @see:*/void ClosePort();/** 串口监听线程 : 监听来自串口的数据和信息* @param: void * pParam 线程参数* @return: UINT WINAPI 线程返回值* @note:* @see:*/static UINT WINAPI ListenThread(void* pParam);private:CommucationParam m_param;/** 串口句柄 */HANDLE m_hComm;/** 线程退出标志变量 */static bool s_bExit;/** 线程句柄 */volatile HANDLE m_hListenThread;/** 同步互斥,临界区保护 */CRITICAL_SECTION m_csCommunicationSync;/** 事件回调 */OnMsgRecivedEvent msgRecivedEventProc;OnStateChangedEvent stateChangedEventProc;OnAsyncMsgTimeoutEvent asyncMsgTimeoutEvent;};#endif // SERIALPORTCOMMUCATIONOBJECT_H
串口
// serialportobject_win.cpp#include "serialportobject_win.h"
#include <process.h>
#include <iostream>/** 线程退出标志 */
bool SerialportCommucationObject::s_bExit = false;/** 当串口无数据时,sleep至下次查询间隔的时间,单位:秒 */
const UINT SLEEP_TIME_INTERVAL = 5;SerialportCommucationObject::SerialportCommucationObject(CommucationParam param): m_param(param), m_hListenThread(INVALID_HANDLE_VALUE)
{
}bool SerialportCommucationObject::CreateCommObject()
{return InitPort(m_param.sParam.portNo,m_param.sParam.baud,m_param.sParam.parity,m_param.sParam.databits,m_param.sParam.stopsbits);}void SerialportCommucationObject::ReleaseCommObject()
{CloseListenTread();ClosePort();DeleteCriticalSection(&m_csCommunicationSync);
}int SerialportCommucationObject::SendData(SOCKET fd, const char *msg, int len)
{JC_UNUSED(fd);unsigned char* umsg = new unsigned char[len]{0};memcpy(umsg,msg,len);bool ok = WriteData(umsg,len) ;delete[] umsg;umsg = NULL;return ok ? len : 0;
}void SerialportCommucationObject::run()
{if(!CreateCommObject()) {std::cout << "创建窗口通讯对象失败" << std::endl;return;}else{std::cout << "创建窗口通讯对象成功" << std::endl;}OpenListenThread();
}void SerialportCommucationObject::setEventCallBack(OnMsgRecivedEvent eProc1, OnStateChangedEvent eProc2, OnAsyncMsgTimeoutEvent eProc3)
{msgRecivedEventProc = eProc1;stateChangedEventProc = eProc2;asyncMsgTimeoutEvent = eProc3;}bool SerialportCommucationObject::SendSyncMessage(std::string strSendBuf, bool needReply, std::string &strRecvMsg, int iTimeOut)
{JC_UNUSED(strSendBuf);JC_UNUSED(iTimeOut);JC_UNUSED(needReply);JC_UNUSED(strRecvMsg);puts("SerialportCommucationObject::SendSyncMessage not achieved.\n");return false;
}bool SerialportCommucationObject::InitPort(UINT portNo, UINT baud, char parity, UINT databits, UINT stopsbits, DWORD dwCommEvents)
{dwCommEvents = dwCommEvents;m_hComm = INVALID_HANDLE_VALUE;m_hListenThread = INVALID_HANDLE_VALUE;InitializeCriticalSection(&m_csCommunicationSync);/** 临时变量,将制定参数转化为字符串形式,以构造DCB结构 */char szDCBparam[50];sprintf_s(szDCBparam, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopsbits);/** 打开指定串口,该函数内部已经有临界区保护,上面请不要加保护 */if (!openPort(portNo)){return false;}/** 进入临界段 */EnterCriticalSection(&m_csCommunicationSync);/** 是否有错误发生 */BOOL bIsSuccess = TRUE;/** 在此可以设置输入输出的缓冲区大小,如果不设置,则系统会设置默认值.* 自己设置缓冲区大小时,要注意设置稍大一些,避免缓冲区溢出*//*if (bIsSuccess ){bIsSuccess = SetupComm(m_hComm,10,10);}*//** 设置串口的超时时间,均设为0,表示不使用超时限制 */COMMTIMEOUTS CommTimeouts;CommTimeouts.ReadIntervalTimeout = 0;CommTimeouts.ReadTotalTimeoutMultiplier = 0;CommTimeouts.ReadTotalTimeoutConstant = 0;CommTimeouts.WriteTotalTimeoutMultiplier = 0;CommTimeouts.WriteTotalTimeoutConstant = 0;if (bIsSuccess){bIsSuccess = SetCommTimeouts(m_hComm, &CommTimeouts);}DCB dcb;if (bIsSuccess){// 将ANSI字符串转换为UNICODE字符串DWORD dwNum = MultiByteToWideChar(CP_ACP, 0, szDCBparam, -1, NULL, 0);wchar_t *pwText = new wchar_t[dwNum];if (!MultiByteToWideChar(CP_ACP, 0, szDCBparam, -1, pwText, dwNum)){bIsSuccess = TRUE;}/** 获取当前串口配置参数,并且构造串口DCB参数 */bIsSuccess = GetCommState(m_hComm, &dcb) && BuildCommDCB(pwText, &dcb);/** 开启RTS flow控制 */dcb.fRtsControl = RTS_CONTROL_ENABLE;/** 释放内存空间 */delete[] pwText;}if (bIsSuccess){/** 使用DCB参数配置串口状态 */bIsSuccess = SetCommState(m_hComm, &dcb);}/** 清空串口缓冲区 */PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);/** 离开临界段 */LeaveCriticalSection(&m_csCommunicationSync);return bIsSuccess == TRUE;
}bool SerialportCommucationObject::InitPort(UINT portNo, const LPDCB &plDCB)
{/** 打开指定串口,该函数内部已经有临界区保护,上面请不要加保护 */if (!openPort(portNo)){return false;}/** 进入临界段 */EnterCriticalSection(&m_csCommunicationSync);/** 配置串口参数 */if (!SetCommState(m_hComm, plDCB)){return false;}/** 清空串口缓冲区 */PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);/** 离开临界段 */LeaveCriticalSection(&m_csCommunicationSync);return true;
}bool SerialportCommucationObject::OpenListenThread()
{/** 检测线程是否已经开启了 */if (m_hListenThread != INVALID_HANDLE_VALUE){/** 线程已经开启 */return false;}s_bExit = false;/** 线程ID */UINT threadId;/** 开启串口数据监听线程 */m_hListenThread = (HANDLE)_beginthreadex(NULL, 0, ListenThread, this, 0, &threadId);if (!m_hListenThread){return false;}/** 设置线程的优先级,高于普通线程 */if (!SetThreadPriority(m_hListenThread, THREAD_PRIORITY_ABOVE_NORMAL)){return false;}return true;
}bool SerialportCommucationObject::CloseListenTread()
{if (m_hListenThread != INVALID_HANDLE_VALUE){/** 通知线程退出 */s_bExit = true;/** 等待线程退出 */Sleep(10);/** 置线程句柄无效 */CloseHandle(m_hListenThread);m_hListenThread = INVALID_HANDLE_VALUE;}return true;
}bool SerialportCommucationObject::WriteData(unsigned char *pData, unsigned int length)
{BOOL bResult = TRUE;DWORD BytesToSend = 0;if (m_hComm == INVALID_HANDLE_VALUE){return false;}/** 临界区保护 */EnterCriticalSection(&m_csCommunicationSync);/** 向缓冲区写入指定量的数据 */bResult = WriteFile(m_hComm, pData, length, &BytesToSend, NULL);if (!bResult){DWORD dwError = GetLastError();printf("error code %d\n",dwError);/** 清空串口缓冲区 */PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);LeaveCriticalSection(&m_csCommunicationSync);return false;}/** 离开临界区 */LeaveCriticalSection(&m_csCommunicationSync);return true;
}UINT SerialportCommucationObject::GetBytesInCOM()
{DWORD dwError = 0; /** 错误码 */COMSTAT comstat; /** COMSTAT结构体,记录通信设备的状态信息 */memset(&comstat, 0, sizeof(COMSTAT));UINT BytesInQue = 0;/** 在调用ReadFile和WriteFile之前,通过本函数清除以前遗留的错误标志 */if (ClearCommError(m_hComm, &dwError, &comstat)){BytesInQue = comstat.cbInQue; /** 获取在输入缓冲区中的字节数 */}return BytesInQue;
}bool SerialportCommucationObject::ReadChar(char &cRecved)
{BOOL bResult = TRUE;DWORD BytesRead = 0;if (m_hComm == INVALID_HANDLE_VALUE){return false;}/** 临界区保护 */EnterCriticalSection(&m_csCommunicationSync);/** 从缓冲区读取一个字节的数据 */bResult = ReadFile(m_hComm, &cRecved, 1, &BytesRead, NULL);if ((!bResult)){/** 获取错误码,可以根据该错误码查出错误原因 */DWORD dwError = GetLastError();printf("error code %d\n",dwError);/** 清空串口缓冲区 */PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);LeaveCriticalSection(&m_csCommunicationSync);return false;}/** 离开临界区 */LeaveCriticalSection(&m_csCommunicationSync);return (BytesRead == 1);
}bool SerialportCommucationObject::openPort(UINT portNo)
{/** 进入临界段 */EnterCriticalSection(&m_csCommunicationSync);/** 把串口的编号转换为设备名 */char szPort[50] = {0};sprintf_s(szPort, "COM%d", portNo);/** 打开指定的串口 */m_hComm = CreateFileA(szPort, /** 设备名,COM1,COM2等 */GENERIC_READ | GENERIC_WRITE, /** 访问模式,可同时读写 */0, /** 共享模式,0表示不共享 */NULL, /** 安全性设置,一般使用NULL */OPEN_EXISTING, /** 该参数表示设备必须存在,否则创建失败 */0,0);/** 如果打开失败,释放资源并返回 */if (m_hComm == INVALID_HANDLE_VALUE){LeaveCriticalSection(&m_csCommunicationSync);return false;}/** 退出临界区 */LeaveCriticalSection(&m_csCommunicationSync);return true;
}void SerialportCommucationObject::ClosePort()
{/** 如果有串口被打开,关闭它 */if (m_hComm != INVALID_HANDLE_VALUE){CloseHandle(m_hComm);m_hComm = INVALID_HANDLE_VALUE;}
}UINT SerialportCommucationObject::ListenThread(void *pParam)
{SerialportCommucationObject *pSerialPort = reinterpret_cast<SerialportCommucationObject*>(pParam);// 线程循环,轮询方式读取串口数据while (!pSerialPort->s_bExit){UINT BytesInQue = pSerialPort->GetBytesInCOM();/** 如果串口输入缓冲区中无数据,则休息一会再查询 */if (BytesInQue == 0){Sleep(SLEEP_TIME_INTERVAL);continue;}/** 读取输入缓冲区中的数据并输出显示 */char cRecved = 0x00;do{cRecved = 0x00;if (pSerialPort->ReadChar(cRecved) == true){std::cout << cRecved << std::endl;continue;}} while (--BytesInQue);}return 0;}
tcp 客户端
// tcpclientobject_win.h#ifndef JC_TCPCLIENTOBJECT_H
#define JC_TCPCLIENTOBJECT_H#define _CRT_SECURE_NO_WARNINGS
#include <winsock.h>
#include "commucationbase.h"
#include <QByteArray>class JcTcpClientObject : public CommucationBase
{
public:JcTcpClientObject(CommucationParam param);bool CreateCommObject();void ReleaseCommObject();void run();void setEventCallBack(OnMsgRecivedEvent eProc1,OnStateChangedEvent eProc2,OnAsyncMsgTimeoutEvent eProc3);int SendData(SOCKET fd, const char *msg, int len);bool SendSyncMessage(std::string strSendBuf, bool needReply,std::string &strRecvMsg, int iTimeOut = 5);private:bool initialization();private:CommucationParam m_param;int m_bufferSize = 10*1024 ; // 10kchar recvBuf[1024*10] = { 0 }; // per messageQByteArray m_buffer; // cur buf;int m_messageLength; // cur message lengthbool m_isRunning = true;bool m_connected_status = false;std::atomic_bool m_asyncFlag = false; // 默认异步接收SOCKET c_client;/// event call backOnMsgRecivedEvent msgRecivedEventProc = NULL;OnStateChangedEvent stateChangedEventProc = NULL;OnAsyncMsgTimeoutEvent asyncMsgTimeoutEvent = NULL;
};#endif // JC_TCPCLIENTOBJECT_H
// tcpclientobject_win.cpp#include "tcpclientobject_win.h"
#include <iostream>
#include <thread>
#include "stringhelper.h"#pragma comment(lib,"ws2_32.lib")
using namespace std;#define BUFSIZE 1024*10JcTcpClientObject::JcTcpClientObject(CommucationParam param): m_param(param) {m_connected_status = false;
}bool JcTcpClientObject::CreateCommObject()
{if( !initialization() ) return false;c_client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (c_client == INVALID_SOCKET){WSACleanup();return false;}struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(m_param.eParam.nPort);addr.sin_addr.S_un.S_addr = inet_addr(m_param.eParam.pIP);if (connect(c_client, (struct sockaddr*)&addr, sizeof(addr))== INVALID_SOCKET){WSACleanup();return false;}return true;
}void JcTcpClientObject::ReleaseCommObject()
{if(m_isRunning){m_isRunning = false;}closesocket(c_client);WSACleanup();
}int JcTcpClientObject::SendData(SOCKET fd,const char* msg,int len)
{JC_UNUSED(fd);int sLen = 0;if (sLen = send( c_client /*fd*/, msg, len, 0) < 0) {std::cout << __FILE__ << __LINE__ << " Send message failed." << std::endl;}return sLen;
}bool JcTcpClientObject::initialization()
{WORD w_req = MAKEWORD(2, 2);WSADATA wsadata;if (WSAStartup(w_req, &wsadata) != 0) {std::cout << "通讯库加载失败" << std::endl;return false;}else {std::cout << "通讯库加载成功" << std::endl;return true;}
}void JcTcpClientObject::run()
{m_connected_status = CreateCommObject();if(!m_connected_status) return ;std::thread thrd([=](){while (m_isRunning) {if(m_asyncFlag){memset(recvBuf,0,BUFSIZE);int iRecvsize = 0;iRecvsize = recv(c_client, recvBuf, BUFSIZE, 0);if (iRecvsize <= 0){continue;}/// 判断当前读取的数据包是否为完整packetm_buffer += QByteArray(recvBuf,iRecvsize);do{if(m_buffer.size() >= 4) // 前 4 个字节是 Message Length{m_messageLength = static_cast<uint8_t>(m_buffer.at(0));m_messageLength = (m_messageLength << 8) + static_cast<uint8_t>(m_buffer.at(1));m_messageLength = (m_messageLength << 8) + static_cast<uint8_t>(m_buffer.at(2));m_messageLength = (m_messageLength << 8) + static_cast<uint8_t>(m_buffer.at(3));}if(m_buffer.size() >= m_messageLength + 4){ // 到这里说明收到了一个完整的 Message/// call back on message recivedif( msgRecivedEventProc != nullptr) {msgRecivedEventProc(this,recvBuf,iRecvsize,(void*)&c_client);m_buffer.clear();m_messageLength = 0;}}}while(m_buffer.size() > 0);}::Sleep(10);}closesocket(c_client);WSACleanup();});thrd.detach();
}void JcTcpClientObject::setEventCallBack(OnMsgRecivedEvent eProc1, OnStateChangedEvent eProc2, OnAsyncMsgTimeoutEvent eProc3)
{msgRecivedEventProc = eProc1;stateChangedEventProc = eProc2;asyncMsgTimeoutEvent = eProc3;
}bool JcTcpClientObject::SendSyncMessage(std::string strSendBuf, bool needReply,std::string& strRecvMsg, int iTimeOut)
{if (!m_connected_status)return false;if (needReply){m_asyncFlag = false;}int timeOut = iTimeOut * 1000 ; //secsetsockopt(c_client, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeOut, sizeof(timeOut));setsockopt(c_client, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeOut, sizeof(timeOut));printf("开始发送消息\n");int iRet = send(c_client, strSendBuf.c_str(), strSendBuf.length(), 0);if (iRet == 0){printf("发送消息超时\n");return false;}printf("发送消息: %s\n", strSendBuf.c_str());if(needReply){iRet = recv(c_client, recvBuf, sizeof(recvBuf), 0);if (iRet == -1){printf("接受消息超时\n");return false;}strRecvMsg = std::string(recvBuf);}if (needReply){m_asyncFlag = true;}return true;
}
tcp服务端
// tcpserverobject_win.h#ifndef JC_TCPSERVEROBJECT_H
#define JC_TCPSERVEROBJECT_H#define _CRT_SECURE_NO_WARNINGS
#include <winsock.h>
#include "commucationbase.h"#ifndef BUFSIZE
#define BUFSIZE 1024*10
#endif#include <QByteArray>class JcTcpServerObject : public CommucationBase
{
public:JcTcpServerObject(CommucationParam param);virtual ~ JcTcpServerObject();bool CreateCommObject();void ReleaseCommObject();void run();void setEventCallBack(OnMsgRecivedEvent eProc1,OnStateChangedEvent eProc2,OnAsyncMsgTimeoutEvent eProc3);int SendData(SOCKET fd, const char *msg, int len);bool SendSyncMessage(std::string strSendBuf, bool needReply,std::string &strRecvMsg, int iTimeOut = 5);private:bool initialization();
private:CommucationParam m_param;char m_ip[20] = {0};UINT m_port = 5000;SOCKET m_socket;int m_bufferSize = 10*1024 ; // 10kQByteArray m_buffer; // cur buf;int m_messageLength; // cur message lengthbool m_isRunning = true;bool m_connected_status;SOCKET s_server;fd_set fd;bool needsplicing = false;char m_tBuffer[10*1024] ={0};private:/// eventCallBackOnMsgRecivedEvent msgRecivedEventProc;OnStateChangedEvent stateChangedEventProc;OnAsyncMsgTimeoutEvent asyncMsgTimeoutEvent;};#endif // JC_TCPSERVEROBJECT_H
// tcpserverobject_win.cpp#include "tcpserverobject_win.h"
#include <iostream>
#include <thread>
#include "stringhelper.h"
#include <QByteArray>#pragma comment(lib,"ws2_32.lib")
using namespace std;JcTcpServerObject::JcTcpServerObject(CommucationParam param): m_param(param)
{
}JcTcpServerObject::~JcTcpServerObject()
{
}bool JcTcpServerObject::CreateCommObject()
{if(!initialization()) return false;s_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (s_server == INVALID_SOCKET) {cout << "套接字创建失败!" << endl;WSACleanup();return false;}else {cout << "套接字创建成功!" << endl;}/// 设置端口复用bool bReuseaddr = true;setsockopt(s_server,SOL_SOCKET,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(bool));/// 设置超时// int nNetTimeout=1000;//1秒// setsockopt(s_server,SOL_SOCKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int));// setsockopt(s_server,SOL_SOCKET,SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int));struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(m_param.eParam.nPort);server_addr.sin_addr.S_un.S_addr = inet_addr(m_param.eParam.pIP);if (bind(s_server, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {cout << "套接字绑定失败!" << endl;WSACleanup();return false;}else {cout << "套接字绑定成功!" << endl;}//3.设置套接字为监听状态 SOMAXCONN 监听的端口数 右键转到定义为5if (listen(s_server, SOMAXCONN) < 0) {cout << "设置监听状态失败!" << endl;WSACleanup();return false;}else {cout << "设置监听状态成功!" << endl;}return true;}void JcTcpServerObject::ReleaseCommObject()
{if(m_isRunning){m_isRunning = false;}closesocket(s_server);WSACleanup();
}int JcTcpServerObject::SendData(SOCKET fd, const char *msg, int len)
{int sLen = send(fd, msg, len, 0);if (sLen <= 0) {std::cout << "Send message failed." << std::endl;}return sLen;
}bool JcTcpServerObject::initialization()
{WORD w_req = MAKEWORD(2, 2);WSADATA wsadata;if (WSAStartup(w_req, &wsadata) != 0) {std::cout << "通讯库加载失败" << std::endl;return false;}else {std::cout << "通讯库加载成功" << std::endl;return true;}
}void JcTcpServerObject::run()
{std::thread thrd( [=](){// 初始化启动套接字if(!CreateCommObject()){return;}std::cout << "等待Host连接到设备" << std::endl;// 定义接受请求套接字SOCKET s_accept;char szDataBuff[BUFSIZE] = {0};int iResult = 0;sockaddr_in addrAccept;int iAcceptLen = sizeof(addrAccept);int iRecvSize = 0;sockaddr_in addrTemp;int iTempLen;FD_ZERO(&fd);FD_SET(s_server,&fd);// timeval tm;// tm.tv_sec = 0;// tm.tv_usec = 1000;while(m_isRunning) {fd_set fdOld = fd;iResult = select(0,&fdOld,NULL,NULL,/*&tm*/NULL);if (0 <= iResult){for(UINT i = 0;i < fd.fd_count; i++){if (FD_ISSET(fd.fd_array[i],&fdOld)){/// 如果socket是服务器,则接收连接if (fd.fd_array[i] == s_server){memset(&addrAccept,0,sizeof(addrTemp));s_accept = accept(s_server,(sockaddr *)&addrAccept,&iAcceptLen);if ( INVALID_SOCKET != s_accept){/// 客户端连接if(stateChangedEventProc){stateChangedEventProc(this,0,(void*)&fd.fd_array[i]);}FD_SET(s_accept,&fd);printf("%s:%d has connected to server!\n",inet_ntoa(addrAccept.sin_addr),ntohs(addrAccept.sin_port));}}else { /// 非服务器,接收数据(因为fd是读数据集)memset(szDataBuff,0,BUFSIZE);iRecvSize = recv(fd.fd_array[i],szDataBuff,BUFSIZE,0);memset(&addrTemp,0,sizeof(addrTemp));iTempLen = sizeof(addrTemp);getpeername(fd.fd_array[i],(sockaddr *)&addrTemp,&iTempLen);if (SOCKET_ERROR == iRecvSize){/// 触发客户端关闭的回调函数 param2: 0 表示正常连接;1表示断开连接if(stateChangedEventProc){stateChangedEventProc(this,1,(void*)&fd.fd_array[i]);}closesocket(fd.fd_array[i]);FD_CLR(fd.fd_array[i],&fd);i--;printf("Failed to recv data ,%s:%d errorcode:%d.\n",inet_ntoa(addrTemp.sin_addr),ntohs(addrTemp.sin_port),WSAGetLastError());continue;}if (0 == iRecvSize){/// 客户端socket关闭printf("%s:%d has closed!\n",inet_ntoa(addrTemp.sin_addr),ntohs(addrTemp.sin_port));/// 触发客户端关闭的回调函数 param2: 0 表示正常连接;1表示断开连接if(stateChangedEventProc){stateChangedEventProc(this,1,(void*)&fd.fd_array[i]);}closesocket(fd.fd_array[i]);FD_CLR(fd.fd_array[i],&fd);i--;}if (0 < iRecvSize){/// 打印接收的数据printf("recv len=%d from %s:%d \n",iRecvSize,inet_ntoa(addrTemp.sin_addr),ntohs(addrTemp.sin_port));/// 判断当前读取的数据包是否为完整packetm_buffer += QByteArray(szDataBuff,iRecvSize);do{if(m_buffer.size() >= 4) // 前 4 个字节是 Message Length{m_messageLength = static_cast<uint8_t>(m_buffer.at(0));m_messageLength = (m_messageLength << 8) + static_cast<uint8_t>(m_buffer.at(1));m_messageLength = (m_messageLength << 8) + static_cast<uint8_t>(m_buffer.at(2));m_messageLength = (m_messageLength << 8) + static_cast<uint8_t>(m_buffer.at(3));}if(m_buffer.size() >= m_messageLength + 4){ /// 到这里说明收到了一个完整的 Message/// call back on message recivedif( msgRecivedEventProc != nullptr) {msgRecivedEventProc(this,szDataBuff,iRecvSize,(void*)&fd.fd_array[i]);m_buffer.clear();m_messageLength = 0;}}}while(m_buffer.size() > 0);}}}/// it's import here,don't removeSleep(30);}}else if (SOCKET_ERROR == iResult){Sleep(100);}}// WSACleanup();});thrd.detach();
}void JcTcpServerObject::setEventCallBack(OnMsgRecivedEvent eProc1, OnStateChangedEvent eProc2, OnAsyncMsgTimeoutEvent eProc3)
{msgRecivedEventProc = eProc1;stateChangedEventProc = eProc2;asyncMsgTimeoutEvent = eProc3;
}bool JcTcpServerObject::SendSyncMessage(std::string strSendBuf, bool needReply, std::string &strRecvMsg, int iTimeOut)
{JC_UNUSED(strSendBuf);JC_UNUSED(needReply);JC_UNUSED(strRecvMsg);JC_UNUSED(iTimeOut);return true;
}
🆗,此通讯库暂时如此,其实考虑IOCP会有更高效率~~ 有机会再进一步优化吧。
相关文章:
半导体:Gem/Secs基本协议库的开发(4)
继续接上篇 《半导体:Gem/Secs基本协议库的开发(3)》,本篇我们分享的比较简单,windows系统下tcp和串口通讯。这也是我们协议开发比较重要的一部分,不过我们在此把它封装程一个单独的通讯库,毕竟…...
解锁知识的新大门:自建知识付费小程序的技术指南
在数字化时代,知识付费小程序的崛起为创作者和学习者提供了全新的学习和分享方式。本文将以“知识付费小程序源码”为关键词,从技术角度出发,为你展示如何搭建一个独具特色的知识付费平台。 步骤1:选择适用的知识付费小程序源码…...
Java8实战 - 行为参数化传递代码
背景: 根据《java8实战》把第二章简单概括一下。 在软件工程中,一个最重要的问题是,用户的需求会一直变化,如何应对不断变化的需求,并且把工作量降到最低是需要考虑的,而行为参数化就是一个处理频繁变更需…...
jmeter,取“临时重定向的登录接口”响应头中的cookie
1、线程组--创建线程组; 2、线程组--添加--取样器--HTTP请求; 3、Http请求--添加--后置处理器--正则表达式提取器; 4、线程组--添加--监听器--查看结果树; 5、线程组--添加--取样器--调试取样器。 首先理解 自动重定向 与跟随…...
流程控制之条件判断
目录 流程控制之条件判断 2.1.if语句语法 2.1.1单分支结构 2.1.2双分支结构 2.1.3多分支结构 2.2.案例 例一: 例2: 例3: 例4: 例5: 例6: 例7: 例8: 例9: 2.3.case多条件判断 2.3.1.格式 2.3.2.执行过程 例10: 流程控制之条件判断 2.1.if语句语法 2.1.1单分…...
2 - Electron 核心概念
Electron 核心概念 主进程 通过Node.js、Electron提供的API与系统底层打交道启动项目时运行的 main.js 脚本就是我们说的主进程。在主进程运行的脚本可以以创建 Web 页面的形式展示 GUI。主进程只有一个 渲染进程 每个 Electron 的页面都在运行着自己的进程,这样…...
Cmake找不到mysql.h和libmysqlclient.so
查看mysql.h和libmysqlclient.so的路径 eikeik-Virtual-Machine:~/桌面/dbpool/bin$ locate mysql.h /usr/include/mysql/mysql.h eikeik-Virtual-Machine:~/桌面/dbpool/bin$ locate libmysqlclient.so /usr/lib/x86_64-linux-gnu/libmysqlclient.so /usr/lib/x86_64-linux-g…...
图论——二分图
图论——二分图 二分图通俗解释 有一个图,将顶点分成两类,边只存在不同类顶点之间,同类顶点之间设有边。称图 G 为二部图,或称二分图,也称欧图。 性质 二分图不含有奇数环图中没有奇数环,一定可以转换为二…...
国产浪潮服务器:风扇免手动调节脚本
简介:浪潮集团,是中国本土顶尖的大型IT企业之一,中国领先的云计算、大数据服务商。浪潮集团旗下拥有浪潮信息、浪潮软件、浪潮国际,业务涵盖云计算、大数据、工业互联网等新一代信息技术产业领域,为全球120多个国家和地…...
智能科技企业网站搭建的作用是什么
随着科学技术快速提升,各种智能产品随之而来,每个赛道里都涌入了大量企业商家,有些热门产品更是广受关注,对企业来说,形象、品牌、信息等方面需要完美呈现到用户眼前,而网站无疑是很好的工具。 企业通过【…...
【多组学数据驱动的机器学习:生物医学研究的创新与突破】
简介:随着生物医学研究的不断发展,多组学数据在疾病预防、诊断和治疗方面发挥着越来越重要的作用。本文将介绍如何利用机器学习技术对多组学数据进行综合分析,以及这种方法在生物医学研究中的优势和潜力。 正文: 一、多组学数据…...
AI影响谷歌正在推出新的人工智能模型,用于医疗保健。以下是医生如何使用它们的介绍
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
云仓酒庄带您品法国葡萄酒
说起葡萄酒肯定绕不开法国,法国葡萄酒闻名中外,口碑卓越。作为世界上的产酒大国,可以说是每一寸土地都可以种植葡萄。云仓酒庄的品牌雷盛红酒分享这么优秀的一个葡萄酒产酒国有哪些特点呢? 1.产区特色:波国有最著名的…...
XIAO ESP32S3之实现口罩检测
一、例程介绍 此例程是运行FOMO 轻量检测模型实现人员佩戴口罩检测,Demo中已包含训练好的模型参数,无需再训练。 FOMO(Faster Objects, More Objects) 是由 Edgeimpulse 工程师提出的一种轻量级的目标检测模型,其主要特点是模型非常小&#…...
LVS简介及LVS-NAT负载均衡群集的搭建
目录 LVS群集简介 群集的含义和应用场景 性能扩展方式 群集的分类 负载均衡(LB) 高可用(HA) 高性能运算(HPC) LVS的三种工作模式 NAT 地址转换 TUN IP隧道 IP Tunnel DR 直接路由 Direct Rout…...
ElasticSearch之cat segments API
命令样例如下: curl -X GET "https://localhost:9200/_cat/segments?vtrue&pretty" --cacert $ES_HOME/config/certs/http_ca.crt -u "elastic:ohCxPHQBEs5*lo7F9"执行结果输出如下: index shard prirep ip segment g…...
docker镜像与容器的迁移
docker容器迁移有两组命令,分别是 save & load :操作的是images, 所以要先把容器commit成镜像export & import:直接操作容器 我们先主要看看他们的区别: 一 把容器打包为镜像再迁移到其他服务器 如把mysq…...
Cmake基础(2)
使用一个简单的示例来应用cmake,无任何三方库的单一的应用程序项目 你可以收获 使用cmake生成VS项目生成mingw项目(makefile) 1 首先新建一个cpp,我们要做一个控制台应用程序 #include<iostream> void main(){std::cout<<"hello cm…...
OSPF理论总结与实验
第1章 OSPF[1] 本章阐述了OSPF协议的特征、术语,OSPF的路由器类型、网络类型、区域类型、LSA类型,OSPF报文的具体内容及作用,描述了OSPF的邻居关系,通过实例让读者掌握OSPF在各种场景中的配置。 本章包含以下内容: …...
浅谈安科瑞无线测温产品在巴西某工厂的应用
摘 要:高压开关设备是变电站和配电站中保证电力系统安全运行的重要设备之一,因此,开关柜的稳定运行对于整个电力系统有非常重要的意义。设备老化、长期高负荷运行都可能使设备局部温度过高而发生火灾,因此,对变电站内的敏感设备进行温度检测变得尤为重要…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
