initial commit
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* @file heartbeat.hpp
|
||||
* @brief heartbeat
|
||||
* @author DeepRobotics
|
||||
* @version 1.0
|
||||
* @date 2026-01-07
|
||||
*
|
||||
* @copyright Copyright (c) 2025 DeepRobotics
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
class Heartbeat {
|
||||
public:
|
||||
Heartbeat() {
|
||||
mIsRunning_ = true;
|
||||
heartbeatCheckThread_ = std::make_unique<std::thread>(&Heartbeat::heartbeatCheckThread, this);
|
||||
}
|
||||
~Heartbeat() {
|
||||
mIsRunning_ = false;
|
||||
heartbeatCheckThread_.get()->join();
|
||||
}
|
||||
|
||||
void wakeUp(const NetInfo& netInfo) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (auto& clientInfo : clientInfoList_) {
|
||||
if (clientInfo.ip == netInfo.ip && clientInfo.port == netInfo.port) {
|
||||
clientInfo.isLive = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ClientInfo clientInfo;
|
||||
clientInfo.ip = netInfo.ip;
|
||||
clientInfo.port = netInfo.port;
|
||||
clientInfo.fromType = netInfo.fromType;
|
||||
clientInfo.isLive = true;
|
||||
clientInfoList_.push_back(clientInfo);
|
||||
}
|
||||
|
||||
void GetLiveList(std::list<NetInfo>& netInfos) {
|
||||
netInfos.clear();
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (auto& clientInfo : clientInfoList_) {
|
||||
NetInfo netInfo;
|
||||
netInfo.ip = clientInfo.ip;
|
||||
netInfo.port = clientInfo.port;
|
||||
netInfo.fromType = clientInfo.fromType;
|
||||
netInfos.push_back(netInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteClient(const NetInfo& netInfo) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (auto it = clientInfoList_.begin(); it != clientInfoList_.end();) {
|
||||
if (it->ip == netInfo.ip && it->port == netInfo.port) {
|
||||
it = clientInfoList_.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void heartbeatCheckThread() {
|
||||
bool flag = false;
|
||||
while (mIsRunning_) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - lastHeartbeatTime_).count();
|
||||
if (elapsed >= 2) {
|
||||
if (!flag) std::cout<<"Heartbeat timeout!"<<std::endl;
|
||||
isConntected_ = false;
|
||||
flag = true;
|
||||
|
||||
clientInfoList_.clear();
|
||||
} else {
|
||||
isConntected_ = true;
|
||||
flag = false;
|
||||
}
|
||||
|
||||
if (clientInfoList_.size() > 0) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (auto it = clientInfoList_.begin(); it != clientInfoList_.end();) {
|
||||
if (it->isLive == false) {
|
||||
it = clientInfoList_.erase(it);
|
||||
} else {
|
||||
it->isLive = false;
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
bool mIsRunning_;
|
||||
std::chrono::time_point<std::chrono::steady_clock> lastHeartbeatTime_;
|
||||
std::unique_ptr<std::thread> heartbeatCheckThread_;
|
||||
bool isConntected_ = false;
|
||||
|
||||
struct ClientInfo : public NetInfo {
|
||||
bool isLive;
|
||||
};
|
||||
|
||||
std::mutex mutex_;
|
||||
std::list<ClientInfo> clientInfoList_;
|
||||
};
|
||||
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* @file udp_server.hpp
|
||||
* @brief udp server
|
||||
* @author DeepRobotics
|
||||
* @version 1.0
|
||||
* @date 2026-01-07
|
||||
*
|
||||
* @copyright Copyright (c) 2025 DeepRobotics
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "common_types.h"
|
||||
#include "gamepad_interface.hpp"
|
||||
#include "thread_pool.hpp"
|
||||
|
||||
#define UDP_PORT 30000
|
||||
#define IP "0.0.0.0"
|
||||
#define UDP_BUFFER_SIZE 1024 * 32
|
||||
|
||||
class ThreadPool;
|
||||
class GamepadInterface;
|
||||
|
||||
class UdpServer {
|
||||
public:
|
||||
UdpServer(GamepadInterface* gp) : mGamepadInterface_(gp) {
|
||||
mThreadPool_ = std::make_unique<ThreadPool>(10);
|
||||
|
||||
if (!Init()) {
|
||||
std::cerr << "[UdpServer] Init Failed!" << std::endl;
|
||||
}
|
||||
|
||||
mIsRunning_ = true;
|
||||
mServerThread_ = std::thread(&UdpServer::RecvLoop, this);
|
||||
std::cout << "UDP Server started, listening on port: " << UDP_PORT << std::endl;
|
||||
}
|
||||
|
||||
~UdpServer() {
|
||||
mIsRunning_ = false;
|
||||
|
||||
if (mServerThread_.joinable()) {
|
||||
mServerThread_.join();
|
||||
}
|
||||
|
||||
if (mFd >= 0) {
|
||||
std::cout << "Closing UDP socket..." << std::endl;
|
||||
close(mFd);
|
||||
mFd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool Init() {
|
||||
mFd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (mFd < 0) {
|
||||
perror("Create socket failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
int bufSize = 1024 * 1024 * 25;
|
||||
setsockopt(mFd, SOL_SOCKET, SO_RCVBUF, &bufSize, sizeof(bufSize));
|
||||
setsockopt(mFd, SOL_SOCKET, SO_SNDBUF, &bufSize, sizeof(bufSize));
|
||||
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 5;
|
||||
timeout.tv_usec = 0;
|
||||
if (setsockopt(mFd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
|
||||
perror("Set timeout failed");
|
||||
close(mFd);
|
||||
return false;
|
||||
}
|
||||
|
||||
int opt = 1;
|
||||
setsockopt(mFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
|
||||
sockaddr_in serverAddr{};
|
||||
serverAddr.sin_family = AF_INET;
|
||||
serverAddr.sin_addr.s_addr = inet_addr(IP);
|
||||
serverAddr.sin_port = htons(UDP_PORT);
|
||||
|
||||
if (bind(mFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
|
||||
perror("Bind failed");
|
||||
close(mFd);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RecvLoop() {
|
||||
struct sockaddr_in client_addr;
|
||||
socklen_t client_addr_len = sizeof(client_addr);
|
||||
|
||||
std::vector<char> buffer(UDP_BUFFER_SIZE);
|
||||
|
||||
while (mIsRunning_) {
|
||||
ssize_t recvLen =
|
||||
recvfrom(mFd, buffer.data(), UDP_BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, &client_addr_len);
|
||||
|
||||
if (recvLen > 0) {
|
||||
auto data = std::make_shared<std::vector<char>>(buffer.begin(), buffer.begin() + recvLen);
|
||||
|
||||
auto headerLen = mGamepadInterface_->ParseHeader(data->data(), recvLen);
|
||||
if (headerLen > 0 && headerLen == recvLen) {
|
||||
dataHandle(mFd, data, headerLen, client_addr);
|
||||
} else {
|
||||
std::cout<<"Invalid header"<<std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dataHandle(int socket, std::shared_ptr<std::vector<char>> data, int dataLen, const sockaddr_in& clientAddr) {
|
||||
mThreadPool_->enqueue([=]() {
|
||||
FromType from_type;
|
||||
bool ret = false;
|
||||
int dataId = -1;
|
||||
std::string res;
|
||||
DataInfo dataInfo;
|
||||
|
||||
if (mGamepadInterface_->ParseHeader(data->data(), dataLen, dataId, data->data(), from_type)) {
|
||||
std::string ip = inet_ntoa(clientAddr.sin_addr);
|
||||
int port = ntohs(clientAddr.sin_port);
|
||||
|
||||
dataInfo.ip = ip;
|
||||
dataInfo.port = port;
|
||||
dataInfo.fromType = from_type;
|
||||
dataInfo.buffer = data->data();
|
||||
dataInfo.len = dataLen;
|
||||
|
||||
ret = mGamepadInterface_->ParseData(dataInfo, res);
|
||||
} else {
|
||||
res = "Protocol header error,error code:0x2";
|
||||
}
|
||||
|
||||
if (res.length() > 0) {
|
||||
std::string Response;
|
||||
mGamepadInterface_->AddDataHeader(res, Response, dataId, dataInfo);
|
||||
sendData(socket, Response.c_str(), Response.length(), clientAddr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void sendData(int socket, const char* data, int dataLen, const sockaddr_in& clientAddr) {
|
||||
if (dataLen > 0) {
|
||||
ssize_t sentBytes = 0;
|
||||
while (sentBytes < dataLen) {
|
||||
ssize_t sendNum = sendto(socket, data + sentBytes, dataLen - sentBytes, 0,
|
||||
(struct sockaddr*)&clientAddr, sizeof(clientAddr));
|
||||
if (sendNum < 0) {
|
||||
std::cout << "Send data failure: " << strerror(errno) << std::endl;
|
||||
return;
|
||||
}
|
||||
sentBytes += sendNum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int mFd = -1;
|
||||
std::atomic<bool> mIsRunning_{false};
|
||||
std::thread mServerThread_;
|
||||
|
||||
std::unique_ptr<ThreadPool> mThreadPool_;
|
||||
std::unique_ptr<GamepadInterface> mGamepadInterface_;
|
||||
};
|
||||
Reference in New Issue
Block a user