解决TCP中Bind failed烦恼

文章目录

概要

当第一次运行服务端时正常,第二次运行时出现Bind failed问题。

在实际使用中,如果您尝试启动一个服务端程序并且遇到了 "Bind failed" 的错误信息,这通常意味着尝试绑定(bind)的端口已经被占用。这种情况可能有以下几个常见原因:

  • 端口占用:如果之前启动的服务端实例没有正确关闭,或者其他程序正在使用相同的端口,那么操作系统将不允许您再次绑定到该端口。

  • 未正确释放端口:在服务端程序异常退出或者没有正确关闭套接字的情况下,操作系统可能仍然认为端口正在被使用。

  • TIME_WAIT状态:即使服务端正确关闭了套接字,TCP端口可能会因为TIME_WAIT状态而暂时不可用。TIME_WAIT是TCP连接正常关闭后的一个保留状态,用于确保所有的数据包都正确地完成传输。这个状态通常会持续一段时间(通常是几分钟)。

  • 系统安全策略:某些操作系统可能有安全策略限制了对低端口号(通常是1024以下)的访问。这通常会要求服务端程序以管理员或特权用户身份运行。

为了解决 "Bind failed" 的问题,您可以采取以下几个步骤:

  • 检查端口使用情况:在Unix-like系统中,可以使用 lsof -i :端口号 或 netstat -tulnp | grep 端口号 命令来检查端口占用情况。在Windows系统中,可以使用 netstat -aon | findstr 端口号。

  • 确保正确关闭套接字:在您的代码中,确保在服务端关闭时,您调用了套接字的关闭方法。

  • 设置SO_REUSEADDR套接字选项:这个选项允许重新绑定到正在TIME_WAIT状态的端口。在C/C++中,这可以通过设置套接字选项来完成:

cpp 复制代码
int yes = 1;
if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
    // 错误处理
  • 更改端口号:尝试使用另一个未被占用的端口号。

  • 检查权限:确保您的程序有足够的权限来绑定所需的端口。

  • 重启系统:如果以上方法都无法解决问题,重启系统可以清除所有占用的端口,并重置网络堆栈。

样例代码

头文件

cpp 复制代码
#include <ros/ros.h>
#include "std_msgs/String.h"
#include "std_msgs/Bool.h"
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include "geometry_msgs/Twist.h"
#include <thread>

using namespace std;


class TCPPublisher
{
    public:
        TCPPublisher();
        ~TCPPublisher();
        void cmdVelCallback(const geometry_msgs::Twist::ConstPtr& msg);
        void acceptClients();
        void sendToAllClients(const std::string& message);

    private:
        ros::NodeHandle n, nPrivate;
        ros::Publisher tcpPub;
        ros::Subscriber cmdVelSub;
        // 创建服务器套接字
        int serverSocket;
        // 设置服务器地址结构
        sockaddr_in serverAddr;
        std::vector<int> clientSockets;
        std::mutex clientSocketsMutex;
        int clientSocket;
        std::string topicStatus;
        std::thread acceptThread;
};

main.cpp

cpp 复制代码
#include "./tcp_pub/tcp_pub.h"


TCPPublisher::TCPPublisher():nPrivate("~")
{
    nPrivate.param("topicStatus", topicStatus, std::string("/cmd_vel"));
    
    // 创建套接字
    serverSocket = socket(AF_INET, SOCK_STREAM, 0);

    // 设置套接字选项,允许重新使用本地地址和端口
    int yes = 1;
    if (setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
        std::cerr << "Setsockopt failed." << std::endl;
        close(serverSocket);
        return;
    }

     /*订阅话题*/
    cmdVelSub = n.subscribe(topicStatus.c_str(), 10, &TCPPublisher::cmdVelCallback, this);
    
    // 设置服务器地址结构
    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(8080); // 服务器监听的端口号

    // 绑定套接字
    if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {
        std::cerr << "Bind failed." << std::endl;
        close(serverSocket);
        return;
    }

    // 监听连接
    if (listen(serverSocket, SOMAXCONN) == -1) {
        std::cerr << "Listen failed." << std::endl;
        close(serverSocket);
        return;
    }

    std::cout << "Server is listening for incoming connections..." << std::endl;

    // 在新线程中接受客户端连接
    acceptThread = std::thread(&TCPPublisher::acceptClients, this);

    ROS_INFO("TCPPublisher init successfully!!!");
}

TCPPublisher::~TCPPublisher()
{
    close(serverSocket);
}


void TCPPublisher::cmdVelCallback(const geometry_msgs::Twist::ConstPtr& msg)
{
    float velX = msg->linear.x;
    float angularZ = msg->angular.z;
    // ROS_INFO("velX : %f, angularZ : %f", velX, angularZ);

    // 定义字符数组,用于存储转换后的结果
    char buffer[50]; // 适当调整数组大小以适应你的需求
    snprintf(buffer, sizeof(buffer), "%f,%f", velX, angularZ);
    // ROS_INFO("buffer %s", buffer);

    std::ostringstream ss;
    ss << velX << "," << angularZ;
    sendToAllClients(ss.str());
}

void TCPPublisher::acceptClients() {
    while (ros::ok()) {
        int clientSocket = accept(serverSocket, NULL, NULL);
        if (clientSocket == -1) {
            std::cerr << "Accept failed." << std::endl;
            continue;
        }
        std::cout << "Connection established with a client." << std::endl;

        // 添加到客户端列表
        std::lock_guard<std::mutex> guard(clientSocketsMutex);
        clientSockets.push_back(clientSocket);
    }
}

void TCPPublisher::sendToAllClients(const std::string& message) {
    std::lock_guard<std::mutex> guard(clientSocketsMutex);
    for (auto it = clientSockets.begin(); it != clientSockets.end(); ) {
        if (send(*it, message.c_str(), message.size(), 0) == -1) {
            std::cerr << "Error sending message to client." << std::endl;
            close(*it);
            it = clientSockets.erase(it); // Remove from list if send fails
        } else {
            ++it;
        }
    }
}


int main(int argc, char **argv) {
  //创建节点
  ros::init(argc, argv, "pure_pursuit");
  TCPPublisher tp;  
  ros::spin();
  return 0;
}
相关推荐
AIHE-TECH19 分钟前
PLC实现HTTP协议JSON格式数据上报对接的参数配置说明
网络协议·http·json·url·网页·西门子plc·mes
LLLuckyGirl~44 分钟前
计算机网络之---MAC协议
网络·计算机网络·macos
WoTrusSSL2 小时前
SSL 证书格式和证书文件扩展名:完整指南
网络·网络协议·ssl
张声录12 小时前
【Prometheus】【Blackbox Exporter】深入解析 ProbeTCP 函数:如何实现 Go 中的 TCP/SSL 协议探测
tcp/ip·golang·prometheus
wang09072 小时前
SSL,TLS协议分析
网络·网络协议·ssl
秋说4 小时前
【网络协议】静态路由详解
网络协议·静态路由
说私域5 小时前
优质内容在个人IP运营中的重要性:以开源AI智能名片商城小程序为应用实例的深度探讨
人工智能·tcp/ip·小程序
网络安全-杰克6 小时前
开源靶场1
网络·web安全·开源
Hacker_Nightrain7 小时前
CTF知识点总结(三)
网络·安全·web安全
LLLuckyGirl~7 小时前
计算机网络学习
网络·学习·计算机网络