C++ Client
//
// main.cpp
// CppSocketDemo
//
// Created by ferry on 2025/4/10.
//
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <atomic>
#include <thread>
class TCPClient {
public:
TCPClient() : sockfd(-1), keepAlive(false) {}
~TCPClient() { disconnect(); }
bool connectToHost(const std::string& ip, int port) {
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) return false;
sockaddr_in serv_addr{};
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
if (inet_pton(AF_INET, ip.c_str(), &serv_addr.sin_addr) <= 0) return false;
return connect(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) >= 0;
}
// 粘包处理:发送带长度头的消息[1,3](@ref)
bool sendWithHeader(const std::string& msg) {
uint32_t len = htonl(msg.size());
std::string header(reinterpret_cast<char*>(&len), 4);
return sendData(header + msg);
}
// 粘包处理:接收带长度头的消息
std::string recvWithHeader() {
char header[4];
if (!recvAll(header, 4)) return "";
uint32_t dataLen = ntohl(*reinterpret_cast<uint32_t*>(header));
std::string data(dataLen, 0);
return recvAll(&data[0], dataLen) ? data : "";
}
// 心跳机制[5,6](@ref)
void startHeartbeat(int interval=15) {
keepAlive = true;
heartbeatThread = std::thread([this, interval]{
while (keepAlive) {
std::this_thread::sleep_for(std::chrono::seconds(interval));
if (!sendWithHeader("HEARTBEAT")) break;
}
});
heartbeatThread.detach();
}
void disconnect() {
keepAlive = false;
if (sockfd != -1) close(sockfd);
sockfd = -1;
}
private:
bool sendData(const std::string& data) {
size_t sent = 0;
while (sent < data.size()) {
int ret = send(sockfd, data.data() + sent, data.size() - sent, 0);
if (ret <= 0) return false;
sent += ret;
}
return true;
}
bool recvAll(char* buf, size_t len) {
size_t received = 0;
while (received < len) {
int ret = recv(sockfd, buf + received, len - received, 0);
if (ret <= 0) return false;
received += ret;
}
return true;
}
int sockfd;
std::atomic<bool> keepAlive;
std::thread heartbeatThread;
};
int main(int argc, const char * argv[]) {
TCPClient client;
if (client.connectToHost("127.0.0.1", 8989)) {
client.startHeartbeat(); // 开启心跳
client.sendWithHeader("Hello Server");
std::cout << "Response: " << client.recvWithHeader() << std::endl;
}
return 0;
}
Python3.x Server
import socket
import struct
import threading
import time
class TCPServer:
def __init__(self, port):
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server.bind(('', port))
self.server.listen(5)
self.clients = {}
print(f"Server started on port {port}")
def start(self):
threading.Thread(target=self._check_heartbeat, daemon=True).start()
while True:
conn, addr = self.server.accept()
self.clients[conn] = (time.time(), b"")
threading.Thread(target=self._handle_client, args=(conn,)).start()
def _handle_client(self, conn):
try:
while True:
buffer = self.clients[conn][1]
while len(buffer) < 4:
data = conn.recv(4 - len(buffer))
if not data: raise ConnectionResetError
buffer += data
data_len = struct.unpack("!I", buffer[:4])[0]
buffer = buffer[4:]
while len(buffer) < data_len:
data = conn.recv(data_len - len(buffer))
if not data: raise ConnectionResetError
buffer += data
msg = buffer[:data_len].decode()
buffer = buffer[data_len:]
self.clients[conn] = (time.time(), buffer)
if msg == "HEARTBEAT":
conn.sendall(struct.pack("!I", 2) + b"OK")
print(f"Received: {msg}")
else:
print(f"Received: {msg}")
response = struct.pack("!I", len(msg)) + msg.encode() + "from server"
conn.sendall(response)
except:
conn.close()
del self.clients[conn]
def _check_heartbeat(self, timeout=30):
while True:
time.sleep(10)
now = time.time()
to_remove = []
for conn, (last_active, _) in self.clients.items():
if now - last_active > timeout:
print(f"Client {conn.getpeername()} timeout")
to_remove.append(conn)
for conn in to_remove:
conn.close()
del self.clients[conn]
if __name__ == "__main__":
server = TCPServer(8989)
server.start()