基于socket,实现其他进程到ros内部通信,socket客户端可以多次打开关闭

经过一周的努力,终于实现啦如题所示的功能。

ros节点到基础使用ros节点发布与订阅的第一个例程,也就是learning_communication功能包,使用了listener 和 talker两个节点。其中使用talker节点作为socket的服务端,当talker接受到其他进程来到数据的时候,发布出去,listener订阅这个数据。

下面直接上代码:

listener.cpp

/**
 * 该例程将订阅chatter话题,消息类型String
 */
 
#include "ros/ros.h"
#include "std_msgs/String.h"
 
// 接收到订阅的消息后,会进入消息回调函数
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
  // 将接收到的消息打印出来
  ROS_INFO("I heard: [%s]", msg->data.c_str());
}
 
int main(int argc, char **argv)
{
  // 初始化ROS节点
  ros::init(argc, argv, "listener");
 
  // 创建节点句柄
  ros::NodeHandle n;
 
  // 创建一个Subscriber,订阅名为chatter的topic,注册回调函数chatterCallback
  ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
 
  // 循环等待回调函数
  ros::spin();
 
  return 0;
}

上述代码有点ros基础,应该都没有问题。接下来是talker.cpp

/**
 * 该例程将发布chatter话题,消息类型String
 */
 
#include
#include "ros/ros.h"
#include "std_msgs/String.h"
//socket need xx.h
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFFER_SIZE 100

//服务器读写方法
void listen_data(int fd,int id)
{

    //ros::init(argc, argv, "talker");
    // 创建节点句柄
    ros::NodeHandle n;  
    // 创建一个Publisher,发布名为chatter的topic,消息类型为std_msgs::String
    ros::Publisher chatter_pub = n.advertise("chatter", 1000);
    // 设置循环的频率
    ros::Rate loop_rate(10);
    int count = 0;


    char buffer[BUFFER_SIZE];
    while(1) //一直处于监听客户端信息状态,知道客户端断开或客户端发出退出信息
    {
        memset(buffer,0,BUFFER_SIZE);
        int len = recv(fd,buffer,BUFFER_SIZE,0);
        if(len > 0) //如果接受到数据,则判断是否为退出信息
        {
            if(strcmp(buffer,"exit\n") == 0)
            {
                printf("id %d exited.\n",id);
                break;
            }
            printf("ID_%d:%s\n",id,buffer); //输出第N 个用户,输出的信息
            
        
            if (ros::ok())
            {
            // 初始化std_msgs::String类型的消息
            std_msgs::String msg;
            std::stringstream ss;
            ss << "hello world " << count;
            //msg.data = ss.str();
            msg.data = buffer;

            // 发布消息
            ROS_INFO("%s", msg.data.c_str());
            chatter_pub.publish(msg);

            // 循环等待回调函数
            ros::spinOnce();

            // 按照循环频率延时
            loop_rate.sleep();
            ++count;
            }
            
            
            
            
        }
        else //接收数据小于0,或等于0 则退出
        {
            printf("clinet %d close!\n",id);
            break;
        }
    //如果服务端需要发送信息,此处添加发送信息
    //memset(buffer,0,BUFFER_SIZE);
    //scanf("%s",buffer);
    //send(fd,buffer,BUFFER_SIZE,0);
    }
    close(fd); //关闭此客户端的连接的socket
}

 

int main(int argc, char **argv)
{

    ros::init(argc, argv, "talker");

    int sockfd,new_fd;
    struct sockaddr_in my_addr; //本地连接socked
    struct sockaddr_in their_addr; //客户端连接socked
    unsigned int numbytes,sin_size;
    char buffer[BUFFER_SIZE];
    static int i=0; //记录连接客户端数目,可以使用数组,结构体等数据类型记录客户端信息(IP,端口等)
    //记录本地信息
    my_addr.sin_family = AF_INET; //IPV4
    my_addr.sin_port = htons(9999); //绑定端口9999,并将其转化为网络字节
    my_addr.sin_addr.s_addr = INADDR_ANY; //指定接收IP为任意(如果指定IP,则只接收指定IP)
    bzero(&(my_addr.sin_zero),0); //最后位置赋0,补位置
    //设置socked
    if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
    {
        perror("socket error!\n");
        exit(1);
    }
    //绑定socked
    if((bind(sockfd,(struct sockaddr*)&my_addr,sizeof(my_addr))) < 0)
    {
        perror("bind error!\n");
        exit(1);
    }

    //开启监听,可连接客户端最大为10个
    if(listen(sockfd,10) == -1)
    {
        perror("listen error!\n");
        exit(1);
    }

    //服务端一直运行,等待客户端连接
    while(1)
    {
        sin_size = sizeof(struct sockaddr_in);
        //等待客户端连接,连接后their_addr接收客户端的IP等信息,没有客户端连接,则一直等待
        if((new_fd = accept(sockfd,(struct sockaddr*)(&their_addr),&sin_size)) < 0)
        {
            perror("accept error!\n");
            exit(1);
        }
        //连接成功后,连接客户端数+1
        i++;
        //开启进程运行客户端
        pid_t childid;
        childid = fork(); //fork()函数有两个返回值,0为子进程,-1为错。子进程运行客户端
        if(childid == 0)
        {
            close(sockfd); //子进程中不再需要sockfd去监听,这里释放它,只需要new_fd即可
            listen_data(new_fd,i);
            exit(0);
        }
        //父进程继续执行while,在accept()等待客户端。父进程的socked此时还在运行,没有关闭
        //此处没有设置父进程退出的代码,因为假设服务器一直运行,如果需要服务器自动退出,可设置服务器
        //等待连接的时间,如果一定时间没有客户端连接,可以退出等待,结束
    }
    //所有客户端
    close(sockfd);
    printf("server-------closed.\n");

    //return 0 ;

 


/*
  // ROS节点初始化
  ros::init(argc, argv, "talker");
  // 创建节点句柄
  ros::NodeHandle n;  
  // 创建一个Publisher,发布名为chatter的topic,消息类型为std_msgs::String
  ros::Publisher chatter_pub = n.advertise("chatter", 1000);
  // 设置循环的频率
  ros::Rate loop_rate(10);
  int count = 0;
  while (ros::ok())
  {
    // 初始化std_msgs::String类型的消息
    std_msgs::String msg;
    std::stringstream ss;
    ss << "hello world " << count;
    msg.data = ss.str();
 
    // 发布消息
    ROS_INFO("%s", msg.data.c_str());
    chatter_pub.publish(msg);
 
    // 循环等待回调函数
    ros::spinOnce();
    
    // 按照循环频率延时
    loop_rate.sleep();
    ++count;
  }
 */
  return 0;
}

上述代码基本流程是,使用ros::init(argc, argv, "talker");  向rosmaster注册节点信息,然后建立一个socket的服务器,保持服务器一直运行,

 if((new_fd = accept(sockfd,(struct sockaddr*)(&their_addr),&sin_size)) < 0)
        {
            perror("accept error!\n");
            exit(1);
        }

使用accept()函数,此处会阻塞,直到有客户端建立了链接。当客户端打开的时候,使用childid = fork(); 也是就fork()函数,来复制一份当前进程的数据,等价于开辟一个新的进程,具体原理,查阅fork函数。

当fork()函数成功之后,在子进程中,会返回0,在父进程中(也就是调用这个函数的进程)返回端口值,失败的话,会返回一个负值。

然后进入到void listen_data(int fd,int id)中,在这里,接受数据,并且建立一个消息发布器,每次接收到消息之后,就把数据发布出去。当客户端那边ctrl+C 之后,退出子进程,返回到父进程中继续等待客户端的到来。

下面是socket客户端到代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

int main()
{
    int sockfd,numbytes;
    char buff[100];
    struct sockaddr_in their_addr;
    int i=0;
    
    their_addr.sin_family = AF_INET;
    their_addr.sin_port = htons(9999);
    their_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //本次设置的是本地连接
    bzero(&(their_addr.sin_zero),0);

    if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
    {
        perror("socket error!\n");
        exit(1);
    }

    // 使用connect连接服务器,their_addr获取服务器信息
    if(connect(sockfd,(struct sockaddr*)&their_addr,sizeof(struct sockaddr)) == -1)
    {
        perror("connect error!\n");
        exit(1);
    }

    while(1)
    {
        //连接成功后
        memset(buff,0,sizeof(buff));
        printf("clinet----:");
        scanf("%s",buff);
        //客户端开始写入数据,*****此处buff需要和服务器中接收
        if(send(sockfd,buff,sizeof(buff),0) == -1)
        {
            perror("send error \n");
            exit(1);
        }
    }
    close(sockfd);
    return 0;

}


基本就这些,功能实现啦。

 

你可能感兴趣的:(ros学习笔记,进程通信)