瞎谈 Socket 编程(一)

  从项目中一个实例说起,我们的目标是将决策层做出的控制小车的决策(主要是速度velocity还有角速度angular),通过TCP网络传输的方式传递到与控制小车的FPGA相连接的RaspberryPi。也许你会问,为毛要通过网络传输,不能通过其他方式,比如串口,不是会更快点吧?
  Maybe…不过,这是有原因的。
  其一,我觉得应该通过以太网线直接将运行决策的PC机网卡与树莓派上的网卡直接相连接,再用网络传输,应该比串口快,或者说,更稳定高效。遗憾的是,我们没办法通过以太网线直接相连,而是通过WiFi,这是因为,其二,PC机唯有的一个以太网卡,拿去插激光雷达了,而激光雷达作为一个sensor,是决策所必须的。
  其三,整个体系运行在ROS之上,而,把ROS体系中做出来的决策送到控制端的树莓派上,我们得自己想办法或者说Coding。刚好,找到有一个开源的ros通讯,恰好人家是通过TCP传输的,所以,就没想那么多,直接用上了。
  可想而知,这个开源的ros通讯要做的,就是把决策层做出来的velocity和angular,通过tcp传送到运行在树莓派上的程序进而控制FPGA控制小车。搞过ROS的应该知道,在ROS中节点都是通过消息进行通讯的,那么,这个ros通讯节点要做的肯定就是订阅决策层做出来的关于控制的消息,然后通过网络传输出去,这些,你可以阅读 这个开源项目 的源码进行了解。

python实现的客户端

  下面是修改的该开源项目 sender.py 中一些关于 socket 编程部分的代码,显然用的是python下的socket库,那些API还是大同小异。像我这种不会python的都可以试着改改,加上本人对python无可奉告,这里重点还是放在 socket编程 上,关注点仍是编程的流程、具体使用哪些API,需要注意哪些细节上,下面会着重介绍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env python

import socket
import sys
import rospy
import rostopic

class Sender():
def __init__(self):
#...
RECEIVER_IP = ...
PORT = ...
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# RECEIVER_IP and PORT should be the same as Server configuration
self.sock.connect((RECEIVER_IP, PORT))
except Exception as e:
self.sock.close()
sys.exit()

def callback(self, topic_message):
self.send_msg("velocity:"
+ str(topic_message.linear.x)
+ ":oriention:"
+ str(topic_message.angular.z)
+ ":ok:")

def send_msg(self, msg):
try:
self.sock.sendall(msg)
except Exception as e:
self.sock.close()
rospy.loginfo("SENDER ERROR")
rospy.loginfo(e)
sys.exit()

if __name__ == "__main__":
Sender()

C实现的运行在RaspberryPi上的服务器端

  用了人家的客户端,那自己实现的必须是服务器端啦。下面的代码就是Linux下的纯C编写的服务器端有关Socket的关键代码。下面会着重关联这份代码,详细介绍 Socket编程 的主要流程,此外,了解Linux下和Windows下编程主要区别,以及涉及的网络传输、进程间通信等杂七杂八的细节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <sys/time.h>
// tcp configure parameters
#define DEFAULT_PORT 13000
#define MAXLINE 4096
// used for get current ms, print process time.
long long getCurrentTime(void){
struct timeval tv;
gettimeofday(&tv, NULL);
return (long long)tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

int main(int argc, char** argv){
int socket_fd, connect_fd;
struct sockaddr_in servaddr;
char buff[MAXLINE];
int recv_bytes;
//init Server Socket
if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY for automatically get host's IP
servaddr.sin_port = htons(DEFAULT_PORT);   //set port as DEFAULT_PORT
//bind local ip to the Server's socket
if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
//start to listen for Client to connect
if( listen(socket_fd, 10) == -1){
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}

printf("%lld(ms): ======waiting for client's request======\n", getCurrentTime());
while(1){
//blocked until a client connects for saving CPU
if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
float velocity;
float oriention;
//receive data from client(move_base output, velocity:...:oriention:...:ok)
while((recv_bytes = recv(connect_fd, buff, MAXLINE, 0)) > 0){
buff[recv_bytes] = '\0';

if(!strcmp("velocity", strtok(buff, ":"))){
velocity = atof(strtok(NULL, ":"));

if(!strcmp("oriention", strtok(NULL, ":"))){
oriention = atof(strtok(NULL, ":"));

if(!strcmp("ok", strtok(NULL, ":"))){
if(velocity != 0.0 || oriention != 0.0){
printf(" %lld(ms): ", getCurrentTime());
//...
}
//...
}
}
}
}
close(connect_fd);
}
close(socket_fd);
return 0;
}

文章目录
  1. 1. python实现的客户端
  2. 2. C实现的运行在RaspberryPi上的服务器端