半导体:Gem/Secs基本协议库的开发(4)

2023-12-16 20:04

本文主要是介绍半导体: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)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/501712

相关文章

基于Python开发电脑定时关机工具

《基于Python开发电脑定时关机工具》这篇文章主要为大家详细介绍了如何基于Python开发一个电脑定时关机工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 简介2. 运行效果3. 相关源码1. 简介这个程序就像一个“忠实的管家”,帮你按时关掉电脑,而且全程不需要你多做

使用Python进行文件读写操作的基本方法

《使用Python进行文件读写操作的基本方法》今天的内容来介绍Python中进行文件读写操作的方法,这在学习Python时是必不可少的技术点,希望可以帮助到正在学习python的小伙伴,以下是Pyth... 目录一、文件读取:二、文件写入:三、文件追加:四、文件读写的二进制模式:五、使用 json 模块读写

Java中的Opencv简介与开发环境部署方法

《Java中的Opencv简介与开发环境部署方法》OpenCV是一个开源的计算机视觉和图像处理库,提供了丰富的图像处理算法和工具,它支持多种图像处理和计算机视觉算法,可以用于物体识别与跟踪、图像分割与... 目录1.Opencv简介Opencv的应用2.Java使用OpenCV进行图像操作opencv安装j

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

Java如何接收并解析HL7协议数据

《Java如何接收并解析HL7协议数据》文章主要介绍了HL7协议及其在医疗行业中的应用,详细描述了如何配置环境、接收和解析数据,以及与前端进行交互的实现方法,文章还分享了使用7Edit工具进行调试的经... 目录一、前言二、正文1、环境配置2、数据接收:HL7Monitor3、数据解析:HL7Busines

C#图表开发之Chart详解

《C#图表开发之Chart详解》C#中的Chart控件用于开发图表功能,具有Series和ChartArea两个重要属性,Series属性是SeriesCollection类型,包含多个Series对... 目录OverviChina编程ewSeries类总结OverviewC#中,开发图表功能的控件是Char

鸿蒙开发搭建flutter适配的开发环境

《鸿蒙开发搭建flutter适配的开发环境》文章详细介绍了在Windows系统上如何创建和运行鸿蒙Flutter项目,包括使用flutterdoctor检测环境、创建项目、编译HAP包以及在真机上运... 目录环境搭建创建运行项目打包项目总结环境搭建1.安装 DevEco Studio NEXT IDE

Python开发围棋游戏的实例代码(实现全部功能)

《Python开发围棋游戏的实例代码(实现全部功能)》围棋是一种古老而复杂的策略棋类游戏,起源于中国,已有超过2500年的历史,本文介绍了如何用Python开发一个简单的围棋游戏,实例代码涵盖了游戏的... 目录1. 围棋游戏概述1.1 游戏规则1.2 游戏设计思路2. 环境准备3. 创建棋盘3.1 棋盘类

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template