半导体: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会有更高效率~~ 有机会再进一步优化吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/225828.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker中的常见命令

Docker开机自启 systemctl enable dockerDocker容器开机自启 docker update --restartalways [容器名/容器id]案例&#xff1a;docker操作nginx 拉取Nginx镜像 docker pull nginx查看镜像 docker images创建并运行Nginx容器 docker run -d --name nginx -p 80:80 nginx查…

LT8711HE方案《任天堂Switch底座方案》

LT8711HE Type-c转HDMI方案 LT8711HE是高性能的Type-C/DP1.2转HDMI2.0转换器&#xff0c;设计用于连接 USB Type-C 源或 DP1.2 源到 HDMI2.0 接收器。该LT8711HE集成了符合 DP1.2 标准的接收器和符合 HDMI2.0 标准的发射器。此外&#xff0c;两个 CC 控制器是包括用于 CC 通信以…

python matplotlib 三维图形添加文字且不随图形变动而变动

要在三维图形中添加文字并使其不随图形变动而变动&#xff0c;可以使用 annotate() 方法。这个方法可以在三维图形中添加文字&#xff0c;并且可以指定文字的位置、对齐方式和字体大小等属性。 下面是一个示例代码&#xff0c;演示如何在三维图形中添加文字&#xff1a; impo…

1.6 实战:Postman请求Get接口-获取用于登录的图形验证码

上一小节我们学习了Postman的布局,对Postman有了一个整体的认知,本小节我们就来实操一下Get接口。 我们打开Postman,点击我们之前创建的请求”获取登录页验证码“。我们在地址栏里填入获取登录页验证码的接口地址。怎么查看这个接口地址呢?我们打开校园二手交易系统,打开…

Leetcode—2414.最长的字母序连续子字符串的长度【中等】

2023每日刷题&#xff08;六十&#xff09; Leetcode—2414.最长的字母序连续子字符串的长度 实现代码 class Solution { public:int longestContinuousSubstring(string s) {int ans 1;int t 1;for(int i 1; i < s.size(); i) {if(s[i] - s[i - 1] 1) {t;ans max(an…

师兄啊师兄第二季开播:男主成海神?玄机是懂联动的!

《师兄啊师兄》动画第二季在12月14日终于正式开播&#xff0c;首播两集&#xff0c;还是很有诚意的。 这部动画改编自言归正传的小说《我师兄实在太稳健了》&#xff0c;原著的知名度不算很高&#xff0c;但玄机制作的动画让这个IP火出了圈。 动画第一季就凭借高颜值的人物建模…

spring boot 实现直播聊天室

spring boot 实现直播聊天室 技术方案: spring bootwebsocketrabbitmq 使用 rabbitmq 提高系统吞吐量 引入依赖 <dependencies><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.42&…

AI全栈大模型工程师(二十三)用 PyTorch 训练一个最简单的神经网络

文章目录 四、求解器五、一些常用的损失函数六、用 PyTorch 训练一个最简单的神经网络后记四、求解器 为了让训练过程更好的收敛,人们设计了很多更复杂的求解器 比如:SGD、L-BFGS、Rprop、RMSprop、Adam、AdamW、AdaGrad、AdaDelta 等等但是,好在最常用的就是 Adam 或者 Ad…

LeetCode 300最长递增子序列 674最长连续递增序列 718最长重复子数组 | 代码随想录25期训练营day52

动态规划算法10 LeetCode 300 最长递增子序列 2023.12.15 题目链接代码随想录讲解[链接] int lengthOfLIS(vector<int>& nums) {//创建变量result存储最终答案,设默认值为1int result 1;//1确定dp数组&#xff0c;dp[i]表示以nums[i]为结尾的子数组的最长长度ve…

VS Code连接远程Linux服务器调试C程序

1.在 VS Code 上安装扩展 C/C 2.通过 VS Code 连接远程 Linux 服务器 3.通过 VS Code 在远程 Linux 服务器上安装扩展 C/C 4.打开远程 Linux 服务器上的文件夹 【注】本文以 /root/ 为例。 5.创建项目文件夹&#xff0c;并在项目文件夹下创建C程序 6.按 F5&#xff0c;选…

mysql中的int(1)和int(10)的区别

今天偶然发现同事在写sql建表的时候把int类型括号后面的数字写成了1&#xff0c;但是我发现数据库里面的值已经远远超过了1位所能表示的范围&#xff0c;所以括号里面的数字肯定不是表示长度了&#xff08;印象中早期的navivat建表的时候&#xff0c;int类型如果默认不指定长度…

devc++如何建立一个c++项目?devc++提示源文件未编译?

打开devc APP后是这样的界面&#xff1b; 点击文件-> 新建->项目&#xff0c;这一点应该不难&#xff0c;主要是最后这个选择什么&#xff1f; 这样即可。 devc提示源文件未编译&#xff1f; 点击工具->编译选项&#xff1b; 如果不能解决&#xff0c;那就是可能路径…

文物数字化建模纹理贴图

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 1、文物3D数字化建模的特点 文物埋在地下历经千年&#xff0c;由于时…

Kafka Avro序列化之一:使用自定义序列化

定义Schema 通过 Apache Avro编程快速入门,我们定义了User实体类。 user.avsc {"namespace": "org.example.avro","type": "record","name"

Java MyBatis 中 #{}和 ${}的区别是什么?

Java MyBatis 中 #{}和 ${}的区别是什么&#xff1f; 在 MyBatis 中&#xff0c;#{} 和 ${} 是两种不同的参数注入方式&#xff0c;主要区别在于参数的预处理和安全性。 #{} &#xff1a;预处理参数 #{} 用于预处理参数&#xff0c;会将参数值以预编译的形式传递给 SQL 引擎…

转动惯量与惯性张量 的推导

从牛顿第二定律推出绕固定轴旋转的转动惯量&#xff0c;再用类似方法从牛顿第二定律推出绕固定点转动的惯性张量 基础定义 角速度 ω \omega ω是一个三维向量&#xff0c;方向表示旋转轴&#xff0c;用右手定则代表旋转方向&#xff0c;长度代表旋转弧度的速度 线速度&#…

WebGL+Three.js入门与实战——给画布换颜色、绘制一个点、三维坐标系

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

为了吃鸡苦练狙击,避免坑队友自己造一个狙击游戏!

引言 一文教会你造一个简易的狙击游戏。 说到狙击&#xff0c;相信大家都不陌生&#xff0c;无论是影视作品还是网络游戏&#xff0c;都经常能看到狙击枪的身影&#xff0c;最深刻的是它能够从百里之外&#xff0c;一枪爆头。 本文将介绍如何在Cocos Creator中造一个简易的狙…

UE5 动画 Sequencer-学习笔记

P2. 课程介绍 资料&#xff1a;https://www.bilibili.com/video/BV1Ag411873f?p2&vd_source707ec8983cc32e6e065d5496a7f79ee6 Sequencer不仅可以做互动动画&#xff0c;还可以导出视频与序列帧 P3-4. 界面介绍 https://www.bilibili.com/video/BV1Ag411873f?p3&spm_…

android实战之感知型组件的最佳实践

引言 使界面控制器&#xff08;activity 和 fragment&#xff09;尽可能保持精简。它们不应试图获取自己的数据&#xff0c;而应使用 ViewModel 执行此操作&#xff0c;同时应观测 LiveData 对象以在视图中体现相应变化。设法编写数据驱动型界面&#xff0c;在此类界面中&…