微信建网站服务,中国最好的建筑公司,课程设计模板,企业网络营销继续接上篇 《半导体#xff1a;Gem/Secs基本协议库的开发#xff08;3#xff09;》#xff0c;本篇我们分享的比较简单#xff0c;windows系统下tcp和串口通讯。这也是我们协议开发比较重要的一部分#xff0c;不过我们在此把它封装程一个单独的通讯库#xff0c;毕竟…继续接上篇 《半导体Gem/Secs基本协议库的开发3》本篇我们分享的比较简单windows系统下tcp和串口通讯。这也是我们协议开发比较重要的一部分不过我们在此把它封装程一个单独的通讯库毕竟它的作用也只是收发消息而已。so easy~
[codes]
// Commucation.proTEMPLATE lib
DEFINES COMMUCATION_LIBRARY
TARGET JC_Commucation
CONFIG c11 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; // 1Passive, 0Active__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 Nuint32_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.hclass 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.hCommucationBase::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 mapclass 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_castSerialportCommucationObject*(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 QByteArrayclass 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_castuint8_t(m_buffer.at(0));m_messageLength (m_messageLength 8) static_castuint8_t(m_buffer.at(1));m_messageLength (m_messageLength 8) static_castuint8_t(m_buffer.at(2));m_messageLength (m_messageLength 8) static_castuint8_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 QByteArrayclass 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 nNetTimeout1000;//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_castuint8_t(m_buffer.at(0));m_messageLength (m_messageLength 8) static_castuint8_t(m_buffer.at(1));m_messageLength (m_messageLength 8) static_castuint8_t(m_buffer.at(2));m_messageLength (m_messageLength 8) static_castuint8_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);}}}/// its import here,dont 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会有更高效率~~ 有机会再进一步优化吧。