半导体: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在各种场景中的配置。 本章包含以下内容: …...

浅谈安科瑞无线测温产品在巴西某工厂的应用
摘 要:高压开关设备是变电站和配电站中保证电力系统安全运行的重要设备之一,因此,开关柜的稳定运行对于整个电力系统有非常重要的意义。设备老化、长期高负荷运行都可能使设备局部温度过高而发生火灾,因此,对变电站内的敏感设备进行温度检测变得尤为重要…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
day51 python CBAM注意力
目录 一、CBAM 模块简介 二、CBAM 模块的实现 (一)通道注意力模块 (二)空间注意力模块 (三)CBAM 模块的组合 三、CBAM 模块的特性 四、CBAM 模块在 CNN 中的应用 一、CBAM 模块简介 在之前的探索中…...
Spring Boot SQL数据库功能详解
Spring Boot自动配置与数据源管理 数据源自动配置机制 当在Spring Boot项目中添加数据库驱动依赖(如org.postgresql:postgresql)后,应用启动时自动配置系统会尝试创建DataSource实现。开发者只需提供基础连接信息: 数据库URL格…...

短视频时长预估算法调研
weighted LR o d d s T p 1 − p ( 1 − p ) o d d s T p ( T p o d d s ∗ p ) o d d s p o d d s T o d d s odds \frac{Tp}{1-p} \newline (1-p)odds Tp \newline (Tp odds * p) odds \newline p \frac{odds}{T odds} \newline odds1−pTp(1−p)oddsTp(Tpodds…...
时间序列预测的机器学习方法:从基础到实战
时间序列预测是机器学习中一个重要且实用的领域,广泛应用于金融、气象、销售预测、资源规划等多个行业。本文将全面介绍时间序列预测的基本概念、常用方法,并通过Python代码示例展示如何构建和评估时间序列预测模型。 1. 时间序列预测概述 时间序列是按…...
分享今天做的力扣SQL题
其实做之前就打算分享的,但是做完又不想分享了。。。结果没几分钟,还是,写一下吧。我就当各位是监督我的。 说一下,这是第一天做SQL题,虽然我也是软件工程专业,但是学的本来就不好,又忘了个差不…...

【网络安全】fastjson原生链分析
fastjson 原生链 前言 说起 fastjson 反序列化,大部分的利用都是从 type 把 json 串解析为 java 对象,在构造方法和 setter、getter 方法中,做一些文件或者命令执行的操作。当然,在 fastjson 的依赖包中,也存在着像 …...