解决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;
}
相关推荐
weixin_604236672 小时前
华三 路由器 极简核心配置
运维·服务器·网络·h3c·h3c路由器
换个昵称都难6 小时前
webrtc 音频模块FEC模块
网络·音视频·webrtc
youngerwang6 小时前
【从搬运工到协处理器:网卡芯片架构、算法、验证与边缘演进深度剖析】
网络·算法·架构·芯片
KaMeidebaby7 小时前
卡梅德生物技术快报|纯化重组蛋白实操详解
人工智能·python·tcp/ip·算法·机器学习
zjun10017 小时前
TCP专栏-4.四次挥手
网络协议·tcp/ip
智慧光迅AINOPOL9 小时前
校园在线巡课系统方案:督导全覆盖
网络·全光网解决方案·全光网·校园全光网·校园全光网解决方案
酉鬼女又兒9 小时前
零基础入门计算机网络:网络层核心任务、三大关键问题、两种服务类型与 TCP/IP 网际层协议体系全解析
服务器·网络·网络协议·tcp/ip·计算机网络·php·求职招聘
Urbano9 小时前
工装制作全流程科普:从面料到自动化生产
网络·人工智能
2401_8685347810 小时前
网规笔记 | 真题解析:2018年11月软考网规-网络安全案例分析
网络
Gauss松鼠会10 小时前
【GaussDB】GaussDB重要通信参数汇总
服务器·网络·数据库·sql·性能优化·gaussdb·经验总结