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

跨城TCP Socket通信失败求助(本地测试正常)

异地TCP Socket通信失败排查与修复

问题描述

我编写了TCP Socket通信程序,本地测试(同一机器开两个终端运行服务端和客户端)完全正常,但和异地朋友通信时,输入消息后服务端毫无响应。运行时已正确指定IP地址和端口,请问问题出在哪?

相关代码

服务端代码(server.cpp)

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h> 
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <string> 
using namespace std;

int main(int argc, char* argv[]){
 if (argc != 3){
 cout << "Usage: ./server <CLIENT_IP_ADDRESS> <PORT>\n";
 return -1;
 }

char* client_ip_address = argv[1];
int port = stoi(argv[2]);

// Create a socket for server
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == -1) { // If the socket creation is unsuccessful, it returns -1
    cout << "Socket creation has failed.\n";
    return -1;
}

// Address and Port informations
sockaddr_in serverAddress; //sockaddr_in: It is the data type that is used to store the address of the socket.
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(port); //This function is used to convert the unsigned int from machine byte order to network byte order.
serverAddress.sin_addr.s_addr = inet_addr(client_ip_address);


// Socket binding
bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)); 
   

if (listen(serverSocket, 10) == -1) {
    cout << "Connection listening error: " <<endl;
    close(serverSocket);
    return -1;
}

std::cout << "The server has been started. Listening for connections...\n";

sockaddr_in clientAddr;
socklen_t clientSize = sizeof(clientAddr);
int clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientSize);

while (true) {
    
    char messageBuffer[1024] = {0};
    recv(clientSocket, messageBuffer, sizeof(messageBuffer), 0);
    cout << "Message from client: " << messageBuffer << endl;

    if (strcmp(messageBuffer, "Fin") == 0) {
        close(serverSocket);
        break;
    }
    // Sending a message to the client.
    char buffer[1024] = {0};
    cout << "Enter a message: ";
    cin.getline(buffer, sizeof(buffer));
    send(clientSocket, buffer, strlen(buffer), 0);
    if (strcmp(buffer, "Fin") == 0) {
        close(serverSocket);
        break;
    }
}

std::cout << "(Fin) message received. Server is shutting down...\n";

return 0;
}

客户端代码(client.cpp)

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h> 
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <string> 
using namespace std;

int main(int argc, char* argv[]){
 if (argc != 3){
 cout << "Usage: ./client <SERVER_IP_ADRES> <PORT>\n";
 return -1;
 }


char* server_ip_address = argv[1];
int port = stoi(argv[2]);

// Create a socket for client
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == -1) {
    cout << "Socket creation has failed.\n";
    return -1;
}

// Address and Port informations
sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port);
server_address.sin_addr.s_addr = inet_addr(server_ip_address);


// Connecting server
connect(clientSocket, (struct sockaddr*)&server_address, sizeof(server_address));

while (true) {
    // 从用户处获取消息
    char messageBuffer[1024] = {0};
    std::cout << "Enter a message: ";
    cin.getline(messageBuffer, sizeof(messageBuffer));
    // 发送消息到服务端
    send(clientSocket, messageBuffer, strlen(messageBuffer), 0);
    if (string(messageBuffer)== "Fin"){
        close(clientSocket);
        break;
    }
    
    char buffer[1024] = {0};
    ssize_t bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0);
    cout << "Message from server: " << string(buffer, bytesReceived) << std::endl;
    if (string(buffer)== "Fin"){
        close(clientSocket);
        break;
    }
}
std::cout << "(Fin) message received. Client is shutting down...\n";

return 0;
}

问题排查与修复

1. 服务端绑定逻辑错误(核心问题)

你的服务端代码将绑定IP设置为客户端的IP地址,这完全不符合TCP服务端的设计逻辑。服务端应该绑定INADDR_ANY(即0.0.0.0),这样才能接收来自任意IP的客户端连接。

本地测试时,客户端和服务端共用本地IP,所以绑定本地IP能正常工作;但异地通信时,服务端网卡根本不具备客户端的IP,bind调用实际已失败,但代码未检查返回值,导致后续流程看似正常但无法监听连接。

修复方法:
修改服务端的IP绑定代码:

// 替换原有的inet_addr(client_ip_address)
serverAddress.sin_addr.s_addr = INADDR_ANY;

同时修改服务端启动参数提示与参数检查:

if (argc != 2){
    cout << "Usage: ./server <PORT>\n";
    return -1;
}
// 移除client_ip_address相关变量,直接使用port = stoi(argv[1])

2. 缺乏系统调用错误检查

代码未检查bind、connect、recv、send的返回值,这些系统调用失败时会返回-1,添加错误检查才能快速定位问题:

  • 检查bind返回值:
if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
    perror("Bind failed");
    close(serverSocket);
    return -1;
}
  • 检查connect返回值:
if (connect(clientSocket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) {
    perror("Connect failed");
    close(clientSocket);
    return -1;
}
  • 检查recv返回值:当返回0表示客户端断开连接,返回-1表示读取错误,需终止循环。

3. 异地通信额外配置

  • 端口映射与公网IP:如果服务端在家庭内网,需要在路由器上设置端口映射,将外部请求的目标端口转发到服务端机器的对应端口;客户端需连接服务端的公网IP,而非内网IP。
  • 防火墙设置:服务端机器的防火墙需开放指定端口,允许外部TCP连接进入。
  • NAT穿透:若双方都在家庭内网,直接通信需要NAT穿透技术(如STUN/TURN),或其中一方拥有公网IP并配置端口映射。

4. 其他细节修复

  • 服务端退出时,需同时关闭serverSocket和clientSocket;
  • cin.getline可能因输入缓冲区残留换行符读取空行,可在读取前添加cin.ignore()清除缓冲区;
  • 当recv返回0时,说明客户端已主动断开,服务端应终止循环并关闭套接字。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 06:34:56