近期接到一款新的硬件设备需要实现PC端与安卓终端实现COM口通信,以往都是厂家封装了通信模块或者采用Windows的SCARD读卡器设备,调用SCardConnectA、SCardTransmit等方法来实现通信。

这一次比较惨,需要写个比较原始一点的。串口的连接和读写参考了如下地址:C++ 串口通讯(含所有源代码)_vivisl的博客-CSDN博客_串口通信代码

本人在创建和数据读写上做了些许优化,采用新增写线程、读线程、和令牌回令机制来保证一应一答方便上层操作。秉着程序员天下为公,一切开源的念想,特将源码贴出望能给需要帮助之人提供些许的帮助。

-- -- -- -- --

头文件 DataTransmit.h

#ifndef __DATATRANSMIT__H__
#define __DATATRANSMIT__H__
#include<windows.h>
#include<deque>
#include<map>
/*
此文件实现com口端口通信操作
作者:王邵华
时间:2021/9/17
*///int reqid = (fd & 0xFF )<< 24 | (uid & 0x00FFFFFF)
//口令
typedef struct _SShibboleth
{int buffsize;const unsigned char *buff;_SShibboleth(const unsigned char *b,int s){buff=b;buffsize=s;}_SShibboleth(){buffsize = 0;buff = nullptr;}~_SShibboleth(){if(buff){delete buff;buff = nullptr;}}
}SShibboleth,*pSShibboleth;//回令
typedef struct _SBackcross
{long long timestamp;int buffsize;unsigned char* buff;_SBackcross(unsigned char *b,int s,long long t){buff=b;buffsize=s;timestamp=t;}_SBackcross(){}~_SBackcross(){if(buff){delete buff;buff = nullptr;}}
}SBackcross,*pSBackcross;typedef struct _SComBuffer
{_SShibboleth shibboleth;_SBackcross  backcross;int shibbolethRet;int backcrossRet;
}SComBuffer,*pSComBuffer;class CCOM
{
public:CCOM(int com,UINT  baud = 9600, char  parity = 'N', UINT  databits = 8, UINT  stopsbits = 1);virtual ~CCOM();HANDLE GetComHandle();/*WriteBuffer: 从PC端写入buff的buffsize个数据到终端params:in:const unsigned char *buff:写入数据指针,上传分配空间int buffsize:写入数据的长度out:int &reqid:此次请求的唯一ID,用于后续操作的令牌return:int:成功写入的长度,写入失败返回负数*/int WriteBuffer(const unsigned char *buff, int buffsize, int &reqid);/*ReadBufferLen: 获取终端回应该令牌的数据长度params:in:const int reqid:WriteBuffer调用时得到的令牌IDout:return:int:回令的长度,失败返回-1*/int ReadBufferLen(const int reqid);/*ReadBuffer: 从终端读取该令牌的回令数据params:in:const int reqid:WriteBuffer调用时得到的令牌IDout:unsigned char *buff:写入数据指针,上传分配空间int buffsize:写入数据的长度return:int:成功写入的长度,写入失败返回负数备注:回令只能被读取一次,自动失效,超时未回令也会自动失效*/int ReadBuffer(const int reqid, unsigned char *buff, int buffsize);/*SetTimeout: 设置超时时间params:in:int time:超时时间,单位毫秒.时间不合法[20-300000]20毫秒-5分钟按默认6秒out:return:bool:成功true,失败false备注:回令只能被读取一次,自动失效,超时未回令也会自动失效*/bool SetTimeout(int time){time = time * 1000;if(time>=20 && time <= 300000)timeout = time;else timeout = 60000;return true;}private:CCOM(const CCOM&){};CCOM(const  CCOM&&){};CCOM& operator=(const CCOM&){};CCOM& operator=(const CCOM&&){};static unsigned int __stdcall ListenReadThread(void* pParam);static unsigned int __stdcall ListenWriteThread(void* pParam);bool WriteComData(int reqid, const unsigned char* pData, unsigned int length);bool ReadComData(int reqid, unsigned char* pData, unsigned int length);void deleteReqidFront();unsigned int GetBytesInCOM();bool openPort(UINT portNo);bool InitPort(UINT portNo, const LPDCB& plDCB);/** 初始化串口函数**  @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);
private:HANDLE  m_hComm;int m_comPort;int m_reqid;/** 线程句柄 */volatile HANDLE    m_hListenReadThread;volatile HANDLE    m_hListenWriteThread;unsigned int m_ListenReadThreadId;unsigned int m_ListenWriteThreadId;volatile HANDLE hEventListenReadThread; //开启读数据事件volatile HANDLE hEventListenWriteThread; //开启写数据事件volatile HANDLE hEventExit;char szEventListenReadThread[128], szEventListenWriteThread[128], szEventExit[128];bool isRun;int timeout;/** 同步互斥,临界区保护 */CRITICAL_SECTION   m_csSync;       //!< 互斥操作串口 std::deque<int> m_Reqid;std::map<int,SComBuffer> m_SComBuffer;
};class CComHelper
{public:HANDLE CreatePort(UINT  portNo = 1, UINT  baud = CBR_9600, char  parity = 'N', UINT  databits = 8, UINT  stopsbits = 1);bool RemovePort(HANDLE h);int Write(HANDLE h,const unsigned char *buff, int buffsize, int &reqid);int ReadLen(HANDLE h, const int reqid);int Read(HANDLE h, const int reqid, unsigned char *buff, int buffsize);bool SetTimeout(HANDLE h,int time);static CComHelper* getInstance();virtual ~CComHelper(){m_mapPortCom.erase(m_mapPortCom.begin(),m_mapPortCom.end());}
private:CComHelper(){};CComHelper(const CComHelper&){};CComHelper(const  CComHelper&&){};CComHelper& operator=(const CComHelper&){};CComHelper& operator=(const CComHelper&&){};std::map<UINT,CCOM*> m_mapPortCom;std::map<HANDLE,UINT> m_mapHandlePort;/*HANDLE h = CreatePort(3);unsigned char writebuff[5] = {0xAA,0AA,0XAA,0X99,0X96};int reqid = 0;int len = 0;int ret = 0;if(0 < Write(h, writebuff,5,reqid)){if(0 < (len =  ReadBufferLen(h,reqid))){unsigned char *readbuff = new unsigned char[len];if(0 < Read(h,reqid,readbuff,len)){//    do your things//...}if(readbuff){delete[] readbuff; readbuff=nullptr;}}}if(INVALID_HANDLE_VALUE != h){RemovePort(h);}*/
};
#endif

源文件 DataTransmit.cpp

#include "DataTransmit.h"
#include <stdio.h>
#include <process.h>#define ERROR_DEFAULT -1
#define ERROR_NOEXECUTE -9999
#define ERROR_INVALID_HANDLE_VALUE -9988
#define ERROR_READORWRITE -9977
#define ERROR_UNCONNECTION -9966
#define ERROR_PARAMS -2#define MAX_TIMEOUT 60000CCOM::CCOM(int com, UINT  baud, char  parity, UINT  databits, UINT  stopsbits)
{m_comPort = com;m_reqid = 0;m_hComm = INVALID_HANDLE_VALUE;m_hListenReadThread = INVALID_HANDLE_VALUE;m_hListenWriteThread = INVALID_HANDLE_VALUE;hEventListenReadThread = INVALID_HANDLE_VALUE;hEventListenWriteThread  = INVALID_HANDLE_VALUE;hEventExit = INVALID_HANDLE_VALUE;//开启端口通信句柄if(InitPort(com,baud,parity,databits,stopsbits)){//创建读写线程m_hListenReadThread = (HANDLE)_beginthreadex(NULL, 0, ListenReadThread, this, 0, &m_ListenReadThreadId);m_hListenWriteThread = (HANDLE)_beginthreadex(NULL, 0, ListenWriteThread, this, 0, &m_ListenWriteThreadId);sprintf_s(szEventListenReadThread, "EventListenReadThread_COM%d_%d",com,m_hComm);sprintf_s(szEventListenWriteThread, "EventListenWriteThread_COM%d_%d",com,m_hComm);sprintf_s(szEventExit, "szEventExit_COM%d_%d",com,m_hComm);hEventListenReadThread = ::CreateEventA(NULL,true,false,szEventListenReadThread);hEventListenWriteThread = ::CreateEventA(NULL,true,false,szEventListenWriteThread);hEventExit =  ::CreateEventA(NULL,true,false,szEventExit);}::InitializeCriticalSection(&m_csSync);timeout = MAX_TIMEOUT;
}bool CCOM::InitPort(UINT portNo, const LPDCB& plDCB)
{/** 打开指定串口,该函数内部已经有临界区保护,上面请不要加保护 */if (!openPort(portNo)){return false;}/** 配置串口参数 */if (!SetCommState(m_hComm, plDCB)){return false;}/**  清空串口缓冲区 */PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);return true;
}bool CCOM::openPort(UINT portNo)
{/** 把串口的编号转换为设备名 */char szPort[50];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){return false;}  return true;
}bool CCOM::InitPort(UINT portNo /*= 1*/, UINT baud /*= CBR_9600*/, char parity /*= 'N'*/,UINT databits /*= 8*/, UINT stopsbits /*= 1*/, DWORD dwCommEvents /*= EV_RXCHAR*/){/** 临时变量,将制定参数转化为字符串形式,以构造DCB结构 */char szDCBparam[50];sprintf_s(szDCBparam, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopsbits);/** 打开指定串口,该函数内部已经有临界区保护,上面请不要加保护 */if (!openPort(portNo)){return false;}/** 是否有错误发生 */BOOL bIsSuccess = TRUE;/** 在此可以设置输入输出的缓冲区大小,如果不设置,则系统会设置默认值.*  自己设置缓冲区大小时,要注意设置稍大一些,避免缓冲区溢出*/if (bIsSuccess ){bIsSuccess = SetupComm(m_hComm,1024*1024,1024*1024);}/** 设置串口的超时时间,均设为0,表示不使用超时限制 */COMMTIMEOUTS  CommTimeouts;CommTimeouts.ReadIntervalTimeout = MAX_TIMEOUT;CommTimeouts.ReadTotalTimeoutMultiplier = MAX_TIMEOUT;CommTimeouts.ReadTotalTimeoutConstant = MAX_TIMEOUT;CommTimeouts.WriteTotalTimeoutMultiplier = MAX_TIMEOUT;CommTimeouts.WriteTotalTimeoutConstant = MAX_TIMEOUT;if (bIsSuccess){bIsSuccess = SetCommTimeouts(m_hComm, &CommTimeouts);}DCB  dcb;if (bIsSuccess){/** 获取当前串口配置参数,并且构造串口DCB参数 */bIsSuccess = GetCommState(m_hComm, &dcb) && BuildCommDCB(szDCBparam, &dcb);/** 开启RTS flow控制 */dcb.fRtsControl = RTS_CONTROL_ENABLE;}if (bIsSuccess){/** 使用DCB参数配置串口状态 */bIsSuccess = SetCommState(m_hComm, &dcb);}/**  清空串口缓冲区 */PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);return bIsSuccess == TRUE;
}#define CLOSEHANDLE(x) \if((x) != INVALID_HANDLE_VALUE)\{\::CloseHandle((x));\(x) = INVALID_HANDLE_VALUE;\}CCOM::~CCOM()
{m_SComBuffer.erase(m_SComBuffer.begin(),m_SComBuffer.end());::SetEvent(hEventExit);::Sleep(200);::DeleteCriticalSection(&m_csSync);CLOSEHANDLE(m_hComm)CLOSEHANDLE(m_hListenReadThread)CLOSEHANDLE(m_hListenWriteThread)CLOSEHANDLE(hEventListenReadThread)CLOSEHANDLE(hEventListenWriteThread)CLOSEHANDLE(hEventExit)
}HANDLE CCOM::GetComHandle()
{return m_hComm;
}unsigned int __stdcall CCOM::ListenReadThread(void* pParam)
{CCOM *pSerialPort = reinterpret_cast<CCOM*>(pParam);while(true){HANDLE handles[2] = {pSerialPort->hEventListenReadThread,pSerialPort->hEventExit};int waitobject = ::WaitForMultipleObjects(2,handles,false,INFINITE); //int errorno = ::GetLastError();switch (waitobject){case WAIT_OBJECT_0:  //读数据{auto reqid = pSerialPort->m_Reqid.front();pSerialPort->deleteReqidFront();auto &itr = pSerialPort->m_SComBuffer.find(reqid);if(itr == pSerialPort->m_SComBuffer.end()) break;long start = ::GetTickCount();long end = 0;unsigned int size = 0;//bug:大量数据可能size只获取部分长度//采用间隔20ms,3次长度未变化则默认已接受完unsigned int readNoChangeCount = 0;while(true){unsigned int bytesize = pSerialPort->GetBytesInCOM();if(size && size == bytesize){if(++readNoChangeCount > 3)break;::Sleep(20);}else{size = bytesize;readNoChangeCount = 0;}end = ::GetTickCount();if(end - start > pSerialPort->timeout) break; //超时未应答}if(size <= 0) {pSerialPort->m_SComBuffer[reqid].backcrossRet = ERROR_UNCONNECTION;break;}pSerialPort->m_SComBuffer[reqid].backcross.buff = new unsigned char[size];memset(pSerialPort->m_SComBuffer[reqid].backcross.buff,0x00,size);pSerialPort->m_SComBuffer[reqid].backcross.buffsize = size;pSerialPort->m_SComBuffer[reqid].backcross.timestamp = ::GetTickCount();pSerialPort->ReadComData(reqid,pSerialPort->m_SComBuffer[reqid].backcross.buff,pSerialPort->m_SComBuffer[reqid].backcross.buffsize);break;}case WAIT_OBJECT_0 + 1:  //结束{return 0;break;}default:break;}::ResetEvent(pSerialPort->hEventListenReadThread);}return 0;}unsigned int __stdcall CCOM::ListenWriteThread(void* pParam)
{CCOM *pSerialPort = reinterpret_cast<CCOM*>(pParam);while(true){HANDLE handles[2] = {pSerialPort->hEventListenWriteThread,pSerialPort->hEventExit};int waitobject = ::WaitForMultipleObjects(2,handles,false,INFINITE); //int errorno = ::GetLastError();switch (waitobject){case WAIT_OBJECT_0:  //写数据{auto reqid = pSerialPort->m_Reqid.front();//auto itr = pSerialPort->m_SComBuffer.find(reqid);if(itr != pSerialPort->m_SComBuffer.end()){if(pSerialPort->WriteComData(reqid, itr->second.shibboleth.buff,itr->second.shibboleth.buffsize)){::SetEvent(pSerialPort->hEventListenReadThread); //触发令牌获取回令}}//break;}case WAIT_OBJECT_0 + 1:  //结束{return 0;break;}default:break;}::ResetEvent(pSerialPort->hEventListenWriteThread);}return 0;
}int CCOM::WriteBuffer(const unsigned char *buff, int buffsize, int &reqid)
{//m_Shibbolethif(nullptr == buff || 0 >= buffsize)return ERROR_PARAMS;::EnterCriticalSection(&m_csSync);reqid = (m_comPort & 0xFF )<< 24 | (++m_reqid & 0x00FFFFFF);m_Reqid.push_back(reqid);SComBuffer combuffer;combuffer.shibboleth.buff = buff;combuffer.shibboleth.buffsize = buffsize;combuffer.shibbolethRet = ERROR_NOEXECUTE;combuffer.backcrossRet = ERROR_NOEXECUTE;combuffer.backcross.buff = nullptr;combuffer.backcross.buffsize = 0;combuffer.backcross.timestamp = 0;m_SComBuffer.insert(std::make_pair<int,SComBuffer>(reqid,combuffer));::LeaveCriticalSection(&m_csSync);::SetEvent(hEventListenWriteThread);//触发写int ret = ERROR_DEFAULT;auto itr = m_SComBuffer.find(reqid);if(itr != m_SComBuffer.end()){long start = ::GetTickCount();long end = 0;while(true){ //等待发送完成通知ret = m_SComBuffer[reqid].shibbolethRet;if(ERROR_NOEXECUTE != ret) return ret; //未知值::Sleep(200);end = ::GetTickCount();if(end - start > timeout) break; //6秒超时}}return  ret;
}bool CCOM::WriteComData(int reqid, const unsigned char* pData, unsigned int length)
{bool   bResult = TRUE;DWORD  BytesToSend = 0;auto &itr = m_SComBuffer.find(reqid);if(itr == m_SComBuffer.end()){return false;}if (m_hComm == INVALID_HANDLE_VALUE){itr->second.shibbolethRet = ERROR_INVALID_HANDLE_VALUE;return false;}printf("object[%X] reqid[%X] WRITE\n",this,reqid);for(int i=0;i<length;++i)printf("%02X",pData[i]);printf("\n");/** 向缓冲区写入指定量的数据 */bResult = (bool)WriteFile(m_hComm, pData, length, &BytesToSend, NULL);if (!bResult){DWORD dwError = GetLastError();/** 清空串口缓冲区 */PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);m_SComBuffer[reqid].shibbolethRet = ERROR_READORWRITE;return false;}m_SComBuffer[reqid].shibbolethRet = BytesToSend;return true;
}void CCOM::deleteReqidFront()
{::EnterCriticalSection(&m_csSync);if(!m_Reqid.empty())m_Reqid.pop_front();::LeaveCriticalSection(&m_csSync);
}unsigned int CCOM::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 CCOM::ReadComData(int reqid, unsigned char* pData, unsigned int length)
{bool  bResult = true;DWORD BytesRead = 0;auto itr = m_SComBuffer.find(reqid);if(itr == m_SComBuffer.end()){return false;}if (m_hComm == INVALID_HANDLE_VALUE){m_SComBuffer[reqid].backcrossRet = ERROR_INVALID_HANDLE_VALUE;return false;}/** 从缓冲区读取一个字节的数据 */bResult = (bool)ReadFile(m_hComm, pData, length, &BytesRead, NULL);printf("object[%X] reqid[%X] READ\n",this,reqid);for(int i=0;i<BytesRead;++i)printf("%02X",pData[i]);printf("\n");if ((!bResult)){/** 获取错误码,可以根据该错误码查出错误原因 */DWORD dwError = GetLastError();/** 清空串口缓冲区 */PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);m_SComBuffer[reqid].backcrossRet  = ERROR_READORWRITE;return false;}/** 离开临界区 */m_SComBuffer[reqid].backcrossRet =  BytesRead;return true;
}int CCOM::ReadBufferLen(const int reqid)
{auto itr = m_SComBuffer.find(reqid);if(itr == m_SComBuffer.end()){return ERROR_PARAMS;}int ret = ERROR_DEFAULT;long start = ::GetTickCount();long end = 0;while(true){ //等待发送完成通知ret = m_SComBuffer[reqid].backcrossRet;if(ERROR_NOEXECUTE != ret) return ret; //未知值::Sleep(200);end = ::GetTickCount();if(end - start > timeout) break; //6秒超时}return ret;
}int CCOM::ReadBuffer(const int reqid, unsigned char *buff, int buffsize)
{auto itr = m_SComBuffer.find(reqid);if(itr == m_SComBuffer.end()){return ERROR_PARAMS;}if(buffsize < itr->second.backcross.buffsize)return ERROR_PARAMS;int ret = ERROR_DEFAULT;memcpy(buff,itr->second.backcross.buff,itr->second.backcross.buffsize);ret = itr->second.backcross.buffsize;m_SComBuffer.erase(itr);//口令 回令 销毁return ret;
}CComHelper* CComHelper::getInstance()
{static CComHelper comHelper;return &comHelper;
}HANDLE CComHelper::CreatePort(UINT  portNo, UINT  baud, char  parity, UINT  databits, UINT  stopsbits)
{if(0 < m_mapPortCom.count(portNo))return m_mapPortCom[portNo]->GetComHandle();CCOM* com = new CCOM(portNo,baud,parity,databits,stopsbits);m_mapPortCom[portNo] = com;m_mapHandlePort[com->GetComHandle()] = portNo;return com->GetComHandle();
}bool CComHelper::RemovePort(HANDLE h)
{if(0 >= m_mapHandlePort.count(h))return false;auto itr = m_mapPortCom.find(m_mapHandlePort[h]);if(m_mapPortCom.end() != itr){if(itr->second){delete  itr->second;itr->second=nullptr;}m_mapPortCom.erase(itr);}auto itr2 = m_mapHandlePort.find(h);if(m_mapHandlePort.end() != itr2)m_mapHandlePort.erase(itr2);return true;
}int CComHelper::Write(HANDLE h,const unsigned char *buff, int buffsize, int &reqid)
{if(0 >= m_mapHandlePort.count(h))return -1;auto itr = m_mapPortCom.find(m_mapHandlePort[h]);if(m_mapPortCom.end() != itr)return itr->second->WriteBuffer(buff,buffsize,reqid);return -1;
}int CComHelper::ReadLen(HANDLE h,const int reqid)
{if(0 >= m_mapHandlePort.count(h))return -1;auto itr = m_mapPortCom.find(m_mapHandlePort[h]);if(m_mapPortCom.end() != itr)return itr->second->ReadBufferLen(reqid);return -1;
}int CComHelper::Read(HANDLE h,const int reqid, unsigned char *buff, int buffsize)
{if(0 >= m_mapHandlePort.count(h))return -1;auto itr = m_mapPortCom.find(m_mapHandlePort[h]);if(m_mapPortCom.end() != itr)return itr->second->ReadBuffer(reqid,buff,buffsize);return -1;
}bool CComHelper::SetTimeout(HANDLE h,int time)
{if(0 >= m_mapHandlePort.count(h))return false;auto itr = m_mapPortCom.find(m_mapHandlePort[h]);if(m_mapPortCom.end() != itr)return itr->second->SetTimeout(time);return false;
}

使用案例:

获取端口数据长度和获取端口数据2个接口,内部实现了超时机制,上传只需阻塞式调用。

啥也不说了,一切都在代码里。如果有用记得一键三连。笔芯!!!

2021-11-02 修复关闭端口内存泄漏问题。

Windows下实现COM口通信c++源代码(验证可行)相关推荐

  1. 关于linux下网络服务器和windows下Qt客户端的通信(图片文件显示)

    关于最近比较火的物联网视频监控项目,在这里给大家分享下图片传输这一块,首先客户端显示是在Windows下Qt中,服务器是在linux系统ubuntu中,服务端打开本地两张图片循环发送给客户端Qt进行显 ...

  2. and型变量哲学家问题C语言,利用AND型信号量解决哲学家进餐问题,要windows下的C/C++的完整源代码程序。(五个哲学家五只筷子)...

    满意答案 XY_P8q 2013.08.24 采纳率:58%    等级:12 已帮助:5848人 // 哲学家进餐问题 #define WIN32_LEAN_AND_MEAN #include #i ...

  3. oracle11关闭账户验证,Windows下Oracle11g中使用外部操作系统账户验证

    在windows2008R2下安装Oracle11g,在安装oracle的服务器上可以使用OS的账户登陆oracle,远程OS,比如win7也是可以使用本地OS的账户登陆的. 需要设置多个参数. 以d ...

  4. Windows下安装nacos2与springboot服务注册验证

    本文章是nacos2服务注册发现入门级文章,重点介绍了如何安装部署nacos2,如何选择spring boot版本.spring cloud版本,如何开发spring boot服务,如何注册到naoc ...

  5. 【学习笔记】在windows下进行基于TCP的本地客户端和服务端socket通信

    文章目录 socket介绍 java中使用socket 基于tcp的socket通信 使用ServerSocket类创建一个web服务器:(java) windows下的基于tcp的socket编程( ...

  6. windows下libmodbus使用攻略

    libmodbus libmodbus使用起来很方便,支持modbus的多种模式,提供了很简单的函数供使用,并且可以支持多个平台.本文主要讲述在windows平台下使用libmodbus的方法,不需要 ...

  7. 【FFMPEG系列】windows下编译FFMPEG篇----之一(MingW)

    目录描述 序 1.环境准备 1.1 c99toc89安装 1.2 c99头文件inttypes.h和stdint.h安装 1.3 修改link文件 1.4 修改configure 2.编译 2.1 编 ...

  8. Windows下的TCP通信

    刚好最近在做tcp.udp通信的实验就顺便写了这篇,方便以后查阅 文章目录 环境 步骤 流程图 源代码 环境 windows 10 visual studio2013 步骤 使用WSAStartup( ...

  9. Windows下的Powerlink主从站通信-(现场总线作业——NJIT)

    Windows下的Powerlink主从站通信-(现场总线作业--NJIT) 一.设计任务(要求) 二.Powerlink工作原理 1.Powerlink简介: 2.Powerlink设备类型及设备模 ...

最新文章

  1. 百度地图API公交检索示例 - 标绘结果路线、返回结果集
  2. java抽象和接口的理解_Java接口实现与抽象类的区别理解 | 彬菌
  3. oracle别名作用范围,在Oracle的Where子句子查询中使用别名或表名,
  4. 【C++提高班】c++数组遍历比较相邻的数值
  5. 博客园屏蔽广告CSS
  6. 全国计算机等级考试题库二级C操作题100套(第80套)
  7. leetcode 1018. 可被 5 整除的二进制前缀
  8. java8 lambda表达式Stream对List常用操作总结
  9. 计算理论笔记 9月27日
  10. VC运行库合集下载,含VC2005/2008/2010/2012/2013
  11. 境外显示手机无服务器,手机卡在国外无服务怎么办
  12. 如何清除计算机开机密码,电脑开机密码忘了怎么办?教你用PE工具清除开机密码...
  13. ubuntu16.04 安装为知笔记
  14. 火狐浏览器屏蔽百度热榜的方法
  15. Perl/Tk入门学习(上)
  16. 【预测模型】基于最小二乘法算法实现股票预测matlab代码
  17. 计算机校本研究题目,信息技术小课题研究题目大全
  18. Android-Q显示白平衡
  19. unity中获取FPS
  20. jpg转换pgm(其他图片格式转换类似)

热门文章

  1. 有刘海儿的MacBook Pro,苹果迄今最强芯片,风雨无阻的AirPods 3,这场发布会够“炸”吗?
  2. 618千元机该怎么选
  3. 7款值得你心动的HTML5动画和游戏
  4. Java面试相关文章及Github学习资料
  5. 思维导图mindmap的使用
  6. 华为ICT数通逻辑笔试
  7. 华为手机什么时候用上鸿蒙系统,华为手机要用鸿蒙系统吗?华为手机什么时候用鸿蒙系统?...
  8. VC Preprocessor definitions
  9. 一文带你了解android的root原理
  10. 这个感恩节,有三句话要说