You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++即时通讯系统Socket数据无法发送至服务器问题排查

问题诊断与修复方案

先直接说最致命的问题:你的客户端代码错误地调用了bind()函数,这是导致客户端显示连接成功但服务端完全没反应的核心原因。


核心错误分析

  1. 客户端无需手动绑定端口:
    客户端发起TCP连接时,完全不需要调用bind()——操作系统会自动为客户端分配一个可用的临时端口。你在客户端里绑定了127.0.0.1:9999,这和服务端监听的端口直接冲突了!虽然客户端的connect()返回了成功,但实际上这个异常的绑定操作让连接处于异常状态,服务端根本无法正确接收到你的连接请求。

  2. 服务端的连接处理逻辑缺陷:
    服务端在accept到一个客户端后,直接调用recv读取一次消息就回到循环等待下一个连接,这会导致两个问题:

    • 第一个客户端只能发送一条消息,后续消息服务端完全不会处理
    • 无法同时处理多个客户端的连接请求

修正后的代码

客户端修正(移除错误的bind调用)

#include <stdio.h>
#include <tchar.h>
#include <winsock2.h>
#include <stdlib.h>
#include <iostream>
#include <thread>
#include <vector>
#include <Ws2tcpip.h>
#include <string>
#define _WINSOCK_DEPRECATED_NO_WARNINGS

void sendMessage(SOCKET s);
void recvMessage(SOCKET s);

int main() {
    std::locale::global(std::locale("fr-FR"));
    WSAData wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cout << "Error initializing winsock";
        return -1;
    }

    SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (clientSocket == INVALID_SOCKET) {
        std::cout << "Error initializing the socket ";
        return -2;
    }

    sockaddr_in addr;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9999);

    // 移除客户端的bind调用!这是核心错误
    if (connect(clientSocket, (const sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
        std::cout << "Erreur when connecting : " << WSAGetLastError() << std::endl;
        return -4;
    }

    std::cout << "You are connected to server" << std::endl;

    std::thread sending(sendMessage, clientSocket);
    std::thread receiving(recvMessage, clientSocket);

    // 用join替代detach,确保线程执行完毕再退出程序
    sending.join();
    receiving.join();

    // 记得释放资源
    closesocket(clientSocket);
    WSACleanup();
    return 0;
}

void sendMessage(SOCKET s) {
    while (true) {
        std::string buff;
        std::cout << "Type your message :" << std::endl;
        std::getline(std::cin, buff);
        
        // 发送实际长度的消息(包含字符串结束符),不要固定发1024字节
        int Bytes = send(s, buff.c_str(), buff.size() + 1, 0);
        if (Bytes <= 0) {
            std::cout << "Send failed or connection closed" << std::endl;
            break;
        }
    }
}

void recvMessage(SOCKET s) {
    while (true) {
        char buffer[1025] = {0}; // 初始化缓冲区,避免打印垃圾数据
        int Bytes = recv(s, buffer, 1024, 0);
        if (Bytes <= 0) {
            std::cout << "Connection closed by server" << std::endl;
            break;
        }
        std::cout << "The server sent : " << buffer << std::endl;
    }
}

服务端修正(支持多客户端连接)

#include <stdio.h>
#include <tchar.h>
#include <winsock2.h>
#include <stdlib.h>
#include <iostream>
#include <thread>
#include <vector>
#include <Ws2tcpip.h>
#include <string>
#include <mutex>

// 维护客户端列表,加锁保证线程安全
std::vector<SOCKET> clients;
std::mutex clientsMutex;

// 单独线程处理每个客户端的消息
void handleClient(SOCKET clientSocket) {
    char buffer[1025];
    while (true) {
        int Bytes = recv(clientSocket, buffer, 1024, 0);
        if (Bytes <= 0) {
            std::cout << "Client disconnected" << std::endl;
            closesocket(clientSocket);
            
            // 从客户端列表移除
            std::lock_guard<std::mutex> lock(clientsMutex);
            auto it = std::find(clients.begin(), clients.end(), clientSocket);
            if (it != clients.end()) {
                clients.erase(it);
            }
            break;
        }
        std::cout << "Message received from client : " << buffer << std::endl;
        
        // 回发消息给客户端,发送实际接收的字节数
        send(clientSocket, buffer, Bytes, 0);
    }
}

int main() {
    std::locale::global(std::locale("fr-FR"));
    WSAData wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cout << "Error initializing winsock";
        return -1;
    }

    SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (server == INVALID_SOCKET) {
        std::cout << "Error initializing the socket ";
        return -2;
    }

    const unsigned short port = 9999;
    sockaddr_in addr;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);
    addr.sin_family = AF_INET;

    int res = bind(server, (sockaddr*)&addr, sizeof(addr));
    if (res != 0) {
        std::cout << "Error when binding";
        return -3;
    }

    res = listen(server, SOMAXCONN);
    if (res != 0) {
        std::cout << "Error on calling listen";
        return -4;
    }

    std::cout << "Server successfully launched" << std::endl;

    while (true) {
        sockaddr_in from = { 0 };
        int addrlen = sizeof(from);
        SOCKET newClient = accept(server, (SOCKADDR*)(&from), &addrlen);
        
        if (newClient != INVALID_SOCKET) {
            std::cout << "Client connected successfully" << std::endl;
            
            // 添加到客户端列表
            std::lock_guard<std::mutex> lock(clientsMutex);
            clients.push_back(newClient);
            
            // 启动线程处理这个客户端
            std::thread clientThread(handleClient, newClient);
            clientThread.detach();
        }
    }

    // 释放服务端socket资源(实际这里循环不会退出,只是规范写法)
    closesocket(server);
    WSACleanup();
    return 0;
}

额外注意事项

  • 消息长度控制:不要固定发送1024字节,应该发送实际的消息长度(包括字符串结束符\0),避免传输多余的垃圾数据。
  • 缓冲区初始化:接收缓冲区一定要初始化,否则可能打印出内存中的随机垃圾数据。
  • 线程与资源管理:客户端用join()替代detach(),确保线程执行完毕再退出;服务端要注意客户端断开后的资源清理,避免内存泄漏。
  • 资源释放:程序退出时必须调用closesocket()和WSACleanup(),释放Socket相关的系统资源。

内容的提问来源于stack exchange,提问作者Soufiane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:49:04