C/S WebSocket 通信 Demo

111 阅读2分钟

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

#-*- coding=utf- -*-
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 = {}  # {conn: (last_active, buffer)}
        print(f"Server started on port {port}")

    def start(self):
        # 心跳检测线程[2,6](@ref)
        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:
                # 粘包处理[1,3](@ref)
                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)
                
                # 心跳处理[5,6](@ref)
                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()