半导体: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在各种场景中的配置。 本章包含以下内容: …...
浅谈安科瑞无线测温产品在巴西某工厂的应用
摘 要:高压开关设备是变电站和配电站中保证电力系统安全运行的重要设备之一,因此,开关柜的稳定运行对于整个电力系统有非常重要的意义。设备老化、长期高负荷运行都可能使设备局部温度过高而发生火灾,因此,对变电站内的敏感设备进行温度检测变得尤为重要…...
RabbitMQ 命令
Docker # 进入容器 > docker exec -it rabbitmq /bin/bash# 帮助 > rabbitmq-service help# 查看所有队列 > rabbitmqctl list_queues Windows 进入安装目录【D:\Program Files\RabbitMQ Server\rabbitmq_server-3.9.10\sbin】输入cmd # 帮助 > rabbitmq-servic…...
数据库系列之简要对比下GaussDB和OpenGauss数据库
GaussDB作为一款企业级的数据库产品,和开源数据库OpenGauss之间又是什么样的关系,刚开始接触的时候是一头雾水,因此本文简要对比下二者的区别,以加深了解。 1、GaussDB和OpenGauss数据库简要对比 GaussDB是华为基于PostgreSQL数据…...
FFmpeg的AVInputFormat
文章目录 结构体定义操作函数支持的AVOutputFormat 通过上面的分析,基本可以看到ffmpeg的套路了,首先一个context上下文,上下文里面一个priv_data 指针,然后再插件结构体中有一个priv_data_size,然后回调函数。 结构体…...
SQL命令---删除字段
介绍 使用sql语句删除表字段。 命令 alter table 表名 drop 字段名;例子 删除a表中的name字段。 alter table a drop name;下面是执行删除后的表结构:...
深入探讨 Python 中的装饰器和上下文管理器
Python 作为一门灵活而强大的语言,提供了许多高级特性,其中装饰器(Decorators)和上下文管理器(Context Managers)是其中两个非常有用的概念。这两个功能性特性提供了对代码结构和行为进行修改和控制的强大工…...
比whatsapp效果好---Google Messages RCS协议消息推送
这段时间由于使用谷歌手机Pixel 7 ( Android13)研究改机room,看了很多相关的资料,测试研究了谷歌生态很多软件功能。结果就是改机Room还没编译成功,反而是测试出Google Messages群发功能的bug,算是一个惊喜…...
HBuilder X
选择一款编程软件有以下几个好处: (1)提高效率:编程软件通常强调代码编辑和自动完成,可以帮助程序员更快速、更准确地输入代码。 (2)降低错误率:编程软件还可以检测代码中的错误&a…...
异地现场工控设备,如何实现远程配置、调试?
南京某企业专注于工业物联领域,在相关项目中往往会在各个点位部署基于Linux系统的中控主机,实现各类物联设备信息的采集、汇总。但是,由于各点位分散多地,且数量达到了上百个,虽然中控主机具备4G物联网接入能力&#x…...
C++报错:error C2238: 意外的标记位于“;”之前
报错信息如下: 编译遇见这样的错误信息主要有一下几种: 情况一: 多数情况下出现这种问题的原因是因为头文件重复包含:即头文件A包含了B,头文件B又包含了A,导致编译器在加载头文件时陷入死循环。 解决办法…...
五、Microsoft群集服务(MSCS)环境的搭建
一、【目的】 学会利用Windows Server布置群集环境。 二、【设备】 FreeNAS11.2,Windows Server 2019 三、【要求】 学会利用Windows Server布置群集环境,掌握处理问题的能力。 配置表: 节点公网IP(public)内网IP(private)群集IP(clust…...
郑州做网站好的公/百度网站下载安装
本篇文章帮大家学习php发送电子邮件(Email),包含了PHP发送电子邮件(Email)使用方法、操作技巧、实例演示和注意事项,有一定的学习价值,大家可以用来参考。PHP mail()函数用于在PHP中发送电子邮件。 您可以使用PHP mail()函数来发送短信&#…...
成都网站建设时代汇创/百度广告点击一次多少钱
// 克隆已有仓库 git clone 仓库地址 // 暂存区 git add . // 本地仓库 git commit -m "详情描述" // 拉下来的代码是否最新 git pull // 推送远程仓库 git push// 查看分支 git branch // 切换分支 git checkout 分支名 // 暂存区 git add . // 本地仓库 git commit…...
电商网站建设与运营实训/中国站长
大家在是否碰到过这样的站点,全站文章系统采用FSO静态生成的HTML文件来显示。这样做的好处一来可以减轻服务器负担,提高访问速度。二来也阻止了SQL注入式的攻击。 我来说说他们的文章系统原理:全部文章均在数据库存有一个副本。另处根据模板生…...
政府网站的域名/营销型网站建站
匿名对象 匿名对象是指创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量。 创建一个普通对象 Person p new Person(); 创建一个匿名对象 new Person(); 匿名对象的特点 l 创建匿名对象直接使用,没有变量名。 new Person().…...
增城住房和建设局网站/上海抖音seo
为什么贴漆面透明保护膜(隐形车衣)?1,有效防护高速行驶中小石头弹伤车漆。2,防止钥匙等恶意划伤。3,有效防护意外轻微碰擦,不伤原漆。4,抗氧化阻隔紫外线、酸雨、动物粪便等侵蚀。5,增加车漆30%…...
美女做爰直播在线网站/网站排名怎么优化
本篇代码存放于:https://github.com/FleyX/demo-project/tree/master/springcloud/spring-cloud%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0 一、服务发现架构 服务发现架构通常具有下面 4 个概念: 服务注册:服务如何使用服务发现代理进行注册?服务…...