Socket通信学习 (自用笔记)
三、结合前两次的实验,实现linux和windoes下的socket通信
1.跨系统通信;并且结合ROS下的话题通信,在收到相关话题后,通过socket向windows下的服务端发送指定的信息
3.代码
server.cpp (windows)
#include<WinSock2.h>
#include<iostream>
#include<string>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
int main(int arg, char* argv[])
{
cout << "Socket服务端" << endl;
//初始化DLL
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsdata;
if (WSAStartup(sockVersion, &wsdata) != 0)
{
return 1;
}
//创建套接字
SOCKET serversocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//绑定套接字
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(10000);
server_addr.sin_addr.S_un.S_addr = INADDR_ANY;
bind(serversocket, (sockaddr*)&server_addr, sizeof(server_addr));
//建立通信套接字标识符
SOCKET clientsocket;
sockaddr_in client_addr;
int len = sizeof(client_addr);
while (true)
{
if (listen(serversocket, 5) == SOCKET_ERROR)
{
cout << "Bind Error" << endl;
break;
}
//建立连接
clientsocket = accept(serversocket, (sockaddr*)&client_addr, &len);
if (clientsocket != INVALID_SOCKET)
{
cout << "连接到:" << inet_ntoa(client_addr.sin_addr) << endl;
}
//进入接收消息循环
char msg[100];//接收消息缓冲区
string check;
check = "a";
const char *ch;
ch = check.c_str();
while (true)
{
int num = recv(clientsocket, msg, 100, 0);
if (num > 0)
{
msg[num] = '\0';
cout << "客户端:" << msg << endl;
send(clientsocket, ch, strlen(ch), 0);
}
else
{
break;
}
}
}
closesocket(clientsocket);
closesocket(serversocket);
WSACleanup();
return 0;
}
client.cpp (ubuntu)
#include <ros/ros.h>
#include <std_msgs/String.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string>
#include <iostream>
#include <unistd.h>
using namespace std;
#define port 10000
const char* _ip;
int clientsocket;
int i=0;
sockaddr_in client_addr;
string order="ok";
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("listener is receiving [%s]", msg->data.c_str());
//发送指令
send (clientsocket,(char*)order.c_str(),order.length(),0);
}
int main (int argc,char **argv)
{
cout<<"socket客户端"<<endl;
cout <<"请输入主机ip地址:"<<endl;
string ip;
cin>>ip;
_ip=ip.c_str();
//创建套接字
clientsocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
client_addr.sin_family=AF_INET;
client_addr.sin_port=htons(port);
client_addr.sin_addr.s_addr=inet_addr(_ip);
int check=connect(clientsocket,(sockaddr*)&client_addr,sizeof(client_addr));
if(check<0)
{
cout<<"连接失败"<<endl;
cout<<"请先开启服务器"<<endl;
}
else
{
cout<<"连接成功"<<endl;
}
//初始化节点
ros::init(argc,argv,"listener");
ros::NodeHandle n;
//创建订阅者
ros::Subscriber sub=n.subscribe("chatter", 1000, chatterCallback);
ros::spin(); //程序进入循环,直到ros::ok()返回false,进程结束。
close(clientsocket);
return 0;
}
以上订阅的“chatter”话题,时自己写的一个实验话题,只是发送一段字符串
talker.cpp
#include <iostream>
#include "ros/ros.h"
#include "std_msgs/String.h"
int main(int argc, char **argv)
{
//ros节点初始化 "talker"节点名称,在ROS里同一时间不允许出现两个
ros::init(argc,argv,"talker");
//创建节点句柄
ros::NodeHandle h;
//创建一个publisher, topic:chatter,消息类型std_msgs::String
ros::Publisher chatter_pub = h.advertise<std_msgs::String>("chatter",1000); // "chatter"话题名称,消息缓存
//设置单循环的频率
ros::Rate looprate(0.5);
while (ros::ok())
{
std_msgs::String msg;
std::stringstream ss;
ss<<"zzzzzzzz";
msg.data = ss.str();
//发布消息
ROS_INFO("%s",msg.data.c_str());
chatter_pub.publish(msg);
//等待回调函数
ros::spinOnce();
//按照之前设定的进行循环
looprate.sleep();
}
}
4.出现的问题,服务器中的listern()和accept()写在循环中,但是client中的connect写在循环外,最后导致只能收发一次,一直以为是client中的send()函数存在问题,最后检查send()函数的返回值才发现,是服务器的问题。以后调试代码时,要记得检查函数的返回值来判断问题。
5.以上实验成功后,将订阅的话题更改为turtlebot2中的到达目标点,来达到最终的目标
movebase_socket.cpp
#include <ros/ros.h>
#include <std_msgs/String.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string>
#include <iostream>
#include <unistd.h>
#include <move_base_msgs/MoveBaseAction.h>
#include <move_base_msgs/MoveBaseActionGoal.h>
using namespace std;
#define port 10000
const char* _ip;
int clientsocket;
int i=0;
sockaddr_in client_addr;
string order="ok";
void chatterCallback(const move_base_msgs::MoveBaseActionResult& msg)
{
//发送指令
if (msg.status.status==3)
{
cout <<"到达目标点"<<endl;
send (clientsocket,(char*)order.c_str(),order.length(),0);
}
}
int main (int argc,char **argv)
{
cout<<"socket客户端"<<endl;
cout <<"请输入主机ip地址:"<<endl;
string ip;
cin>>ip;
_ip=ip.c_str();
//创建套接字
clientsocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
client_addr.sin_family=AF_INET;
client_addr.sin_port=htons(port);
client_addr.sin_addr.s_addr=inet_addr(_ip);
int check=connect(clientsocket,(sockaddr*)&client_addr,sizeof(client_addr));
if(check<0)
{
cout<<"连接失败"<<endl;
cout<<"请先开启服务器"<<endl;
}
else
{
cout<<"连接成功"<<endl;
}
//初始化节点
ros::init(argc,argv,"get_status");
ros::NodeHandle n;
//创建订阅者
ros::Subscriber sub=n.subscribe("move_base/result", 10, chatterCallback);
ros::spin(); //程序进入循环,直到ros::ok()返回false,进程结束。
close(clientsocket);
return 0;
}
订阅“move_base/result”话题,消息类型为move_base_msgs/MoveBaseActionResult