2025-09-16 00:02:42 +08:00
|
|
|
|
#include "CVrTCPClient.h"
|
|
|
|
|
|
#include <sstream>
|
|
|
|
|
|
#include <thread>
|
|
|
|
|
|
#include <chrono>
|
|
|
|
|
|
#include "VrError.h"
|
|
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
#include <winsock2.h>
|
|
|
|
|
|
#include <ws2tcpip.h>
|
|
|
|
|
|
#else
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
#include <sys/select.h>
|
|
|
|
|
|
#include <netinet/tcp.h>
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2025-09-16 00:02:42 +08:00
|
|
|
|
#define MAX_DATA_LEN 1024
|
|
|
|
|
|
|
|
|
|
|
|
#define PROTOCL_START 0x09060002
|
|
|
|
|
|
#define PROTOCL_END 0x09060003
|
|
|
|
|
|
|
|
|
|
|
|
IVrTCPClient* IVrTCPClient::CreateInstance()
|
|
|
|
|
|
{
|
|
|
|
|
|
return new CVrTCPClient();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void IVrTCPClient::DestroyInstance(IVrTCPClient* pInstance)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (pInstance != nullptr) {
|
|
|
|
|
|
delete pInstance;
|
|
|
|
|
|
pInstance = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CVrTCPClient::CVrTCPClient()
|
|
|
|
|
|
: m_nSocket(INVALID_SOCKET_VALUE)
|
|
|
|
|
|
, m_bRecv(false)
|
|
|
|
|
|
, m_bRecvWorking(false)
|
|
|
|
|
|
, m_fRecvCallback(nullptr)
|
|
|
|
|
|
, m_fLinkcCallback(nullptr)
|
|
|
|
|
|
, m_bLink(false)
|
|
|
|
|
|
{
|
|
|
|
|
|
_Init();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CVrTCPClient::~CVrTCPClient()
|
|
|
|
|
|
{
|
|
|
|
|
|
CloseDevice();
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
WSACleanup();
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-09-18 23:49:32 +08:00
|
|
|
|
int CVrTCPClient::LinkDevice(const std::string sDevIP, int nPort, bool bReLink, LinkEventFunc linkFunc, void* pParam)
|
2025-09-16 00:02:42 +08:00
|
|
|
|
{
|
|
|
|
|
|
m_fLinkcCallback = linkFunc;
|
2025-09-18 23:49:32 +08:00
|
|
|
|
m_pLinkParam = pParam;
|
2025-09-16 00:02:42 +08:00
|
|
|
|
m_sIp = sDevIP;
|
|
|
|
|
|
m_nPort = nPort;
|
|
|
|
|
|
|
|
|
|
|
|
int nRet = _ExecLinkDev(sDevIP, nPort);
|
|
|
|
|
|
|
|
|
|
|
|
if (SUCCESS == nRet)
|
|
|
|
|
|
{
|
|
|
|
|
|
m_bLink = true;
|
2025-09-18 23:49:32 +08:00
|
|
|
|
if (m_fLinkcCallback) m_fLinkcCallback(this, true, pParam);
|
2025-09-16 00:02:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (bReLink)
|
|
|
|
|
|
{
|
|
|
|
|
|
std::thread linkThread(&CVrTCPClient::_ReLinkDevThread, this);
|
|
|
|
|
|
linkThread.detach();
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return nRet;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-18 23:49:32 +08:00
|
|
|
|
int CVrTCPClient::StartWork(TCPRecvFunc fRecvFunc, void* pParam)
|
2025-09-16 00:02:42 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (m_bRecv) return SUCCESS;
|
|
|
|
|
|
|
|
|
|
|
|
m_bRecv = true;
|
2025-09-18 23:49:32 +08:00
|
|
|
|
m_fRecvCallback = fRecvFunc;
|
|
|
|
|
|
m_pWorkParam = pParam;
|
2025-09-16 00:02:42 +08:00
|
|
|
|
std::thread recvThread(&CVrTCPClient::_RecvData, this);
|
|
|
|
|
|
recvThread.detach();
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int CVrTCPClient::CloseDevice()
|
|
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
m_bLink = false; // 先设置连接状态为false
|
|
|
|
|
|
|
2025-09-16 00:02:42 +08:00
|
|
|
|
if (m_bRecv)
|
|
|
|
|
|
{
|
|
|
|
|
|
m_bRecv = false;
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 通知重连线程退出
|
|
|
|
|
|
m_condRelink.notify_one();
|
|
|
|
|
|
|
2025-09-16 00:02:42 +08:00
|
|
|
|
while (m_bRecvWorking)
|
|
|
|
|
|
{
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (m_nSocket != INVALID_SOCKET_VALUE)
|
|
|
|
|
|
{
|
|
|
|
|
|
closesocket_func(m_nSocket);
|
|
|
|
|
|
m_nSocket = INVALID_SOCKET_VALUE;
|
|
|
|
|
|
}
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool CVrTCPClient::SendData(const char* pData, const int nLen)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool bRet = true;
|
|
|
|
|
|
int nSendLen = 0;
|
|
|
|
|
|
do
|
|
|
|
|
|
{
|
|
|
|
|
|
int nCount = 0;
|
|
|
|
|
|
if ((nCount = send(m_nSocket, pData + nSendLen, nLen - nSendLen, 0)) < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
printf("send faile");
|
|
|
|
|
|
bRet = false;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
nSendLen += nCount;
|
|
|
|
|
|
}
|
|
|
|
|
|
} while (nSendLen < nLen);
|
|
|
|
|
|
return bRet;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool CVrTCPClient::_Init()
|
|
|
|
|
|
{
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
WORD wVersionRequested;
|
|
|
|
|
|
WSADATA wsaData;
|
|
|
|
|
|
int err;
|
|
|
|
|
|
wVersionRequested = MAKEWORD(2, 2);
|
|
|
|
|
|
err = WSAStartup(wVersionRequested, &wsaData);
|
|
|
|
|
|
if (err != 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CVrTCPClient::_RecvData()
|
|
|
|
|
|
{
|
|
|
|
|
|
m_bRecvWorking = true;
|
|
|
|
|
|
char recvData[MAX_BUF_LEN];
|
|
|
|
|
|
int recvLen = 0;
|
|
|
|
|
|
while (m_bRecv)
|
|
|
|
|
|
{
|
|
|
|
|
|
timeval waitTime = {0, 1000};
|
|
|
|
|
|
fd_set rfd;
|
|
|
|
|
|
FD_ZERO(&rfd);
|
|
|
|
|
|
FD_SET(m_nSocket, &rfd);
|
|
|
|
|
|
|
|
|
|
|
|
int nCount = select(m_nSocket + 1, &rfd, NULL, NULL, &waitTime);
|
2025-09-21 22:20:24 +08:00
|
|
|
|
if (nCount <= 0){
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(!m_bLink)
|
2025-09-16 00:02:42 +08:00
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
memset(recvData, 0, MAX_BUF_LEN);
|
|
|
|
|
|
|
|
|
|
|
|
//recv
|
|
|
|
|
|
if ((recvLen = recv(m_nSocket, recvData, MAX_BUF_LEN, 0)) <= 0)
|
|
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 立即更新连接状态
|
|
|
|
|
|
m_bLink = false;
|
|
|
|
|
|
if (m_fLinkcCallback) m_fLinkcCallback(this, false, m_pLinkParam);
|
|
|
|
|
|
|
|
|
|
|
|
// 通知重连线程
|
2025-09-16 00:02:42 +08:00
|
|
|
|
m_condRelink.notify_one();
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (m_fRecvCallback)
|
|
|
|
|
|
{
|
2025-09-18 23:49:32 +08:00
|
|
|
|
m_fRecvCallback(this, recvData, recvLen, m_pWorkParam);
|
2025-09-16 00:02:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
m_bRecvWorking = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-18 23:49:32 +08:00
|
|
|
|
// 重新连接线程
|
2025-09-16 00:02:42 +08:00
|
|
|
|
void CVrTCPClient::_ReLinkDevThread()
|
|
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
while (m_bRecv) // 改为基于接收状态而非工作状态
|
2025-09-16 00:02:42 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (m_bLink)
|
|
|
|
|
|
{
|
|
|
|
|
|
std::unique_lock<std::mutex> lck(m_mutexRelink);
|
|
|
|
|
|
m_condRelink.wait(lck);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 如果已停止接收,退出重连线程
|
|
|
|
|
|
if (!m_bRecv) break;
|
|
|
|
|
|
|
|
|
|
|
|
int nLinkRet = _ExecLinkDev(m_sIp, m_nPort);
|
|
|
|
|
|
|
|
|
|
|
|
m_bLink = ( 0 == nLinkRet );
|
2025-09-18 23:49:32 +08:00
|
|
|
|
if (m_fLinkcCallback) m_fLinkcCallback(this, m_bLink, m_pLinkParam);
|
2025-09-21 22:20:24 +08:00
|
|
|
|
|
|
|
|
|
|
// 如果连接失败,等待3秒再重试,避免频繁重连占用CPU
|
|
|
|
|
|
if (!m_bLink && m_bRecv) {
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(3));
|
|
|
|
|
|
}
|
2025-09-16 00:02:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int CVrTCPClient::_ExecLinkDev(std::string sIP, int nPort)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (INVALID_SOCKET_VALUE != m_nSocket)
|
|
|
|
|
|
{
|
|
|
|
|
|
closesocket_func(m_nSocket);
|
|
|
|
|
|
}
|
|
|
|
|
|
m_nSocket = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
|
|
if (INVALID_SOCKET_VALUE == m_nSocket)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ERR_CODE(NET_ERR_CREAT_INIT);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 设置socket为非阻塞模式
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
u_long mode = 1;
|
|
|
|
|
|
ioctlsocket(m_nSocket, FIONBIO, &mode);
|
|
|
|
|
|
#else
|
|
|
|
|
|
int flags = fcntl(m_nSocket, F_GETFL, 0);
|
|
|
|
|
|
fcntl(m_nSocket, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2025-09-16 00:02:42 +08:00
|
|
|
|
sockaddr_in sSockAddr;
|
|
|
|
|
|
sSockAddr.sin_family = AF_INET;
|
|
|
|
|
|
sSockAddr.sin_port = htons(nPort);
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
sSockAddr.sin_addr.S_un.S_addr = inet_addr(sIP.c_str());
|
|
|
|
|
|
#else
|
|
|
|
|
|
inet_pton(AF_INET, sIP.c_str(), &sSockAddr.sin_addr);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
int nRet = connect(m_nSocket, (sockaddr*)&sSockAddr, sizeof(sockaddr_in));
|
2025-09-21 22:20:24 +08:00
|
|
|
|
if (nRet == SOCKET_ERROR_VALUE)
|
2025-09-16 00:02:42 +08:00
|
|
|
|
{
|
2025-09-21 22:20:24 +08:00
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
int error = WSAGetLastError();
|
|
|
|
|
|
if (error != WSAEWOULDBLOCK)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ERR_CODE(NET_ERR_CONNECT);
|
|
|
|
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
|
|
int error = 0;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
// 使用select等待连接完成,设置3秒超时
|
|
|
|
|
|
fd_set writefds;
|
|
|
|
|
|
FD_ZERO(&writefds);
|
|
|
|
|
|
FD_SET(m_nSocket, &writefds);
|
|
|
|
|
|
|
|
|
|
|
|
struct timeval timeout;
|
|
|
|
|
|
timeout.tv_sec = 3; // 3秒超时
|
|
|
|
|
|
timeout.tv_usec = 0;
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
int selectRet = select(0, NULL, &writefds, NULL, &timeout); // Windows下第一个参数被忽略
|
|
|
|
|
|
#else
|
|
|
|
|
|
int selectRet = select(m_nSocket + 1, NULL, &writefds, NULL, &timeout);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
if (selectRet <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ERR_CODE(NET_ERR_CONNECT);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查连接是否成功
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
int len = sizeof(error);
|
|
|
|
|
|
if (getsockopt(m_nSocket, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0 || error != 0)
|
|
|
|
|
|
#else
|
|
|
|
|
|
socklen_t len = sizeof(error);
|
|
|
|
|
|
if (getsockopt(m_nSocket, SOL_SOCKET, SO_ERROR, &error, &len) < 0 || error != 0)
|
|
|
|
|
|
#endif
|
|
|
|
|
|
{
|
|
|
|
|
|
return ERR_CODE(NET_ERR_CONNECT);
|
|
|
|
|
|
}
|
2025-09-16 00:02:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-21 22:20:24 +08:00
|
|
|
|
// 恢复socket为阻塞模式
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
u_long blockMode = 0;
|
|
|
|
|
|
ioctlsocket(m_nSocket, FIONBIO, &blockMode);
|
|
|
|
|
|
#else
|
|
|
|
|
|
flags = fcntl(m_nSocket, F_GETFL, 0);
|
|
|
|
|
|
fcntl(m_nSocket, F_SETFL, flags & ~O_NONBLOCK);
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2025-09-16 00:02:42 +08:00
|
|
|
|
int flag = 1;
|
|
|
|
|
|
int ret = setsockopt(m_nSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag));
|
|
|
|
|
|
if (ret == -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
printf("Couldn't setsockopt(TCP_NODELAY)\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
|
|
}
|