最近碰到一個狀況, 只有 Python 有提供工具包函式庫,但我的主程式是 C 語言,因此用 Socket 方式,在 Python 與 C 程式間作溝通,傳遞資料。
Socket 實作
Socket 不是新鮮的東西,所以本篇只是記錄跟備份網路找到的優質開源程式碼,並保存自己做的修改調整,參考來源是:
https://gist.github.com/marcom04/22860f1168330605cac3c448982b0393
因為我是本地端通訊,不是不同機器,所以用 AF_UNIX 就可以,傳輸速率也優於 AF_INET,更適合我正在開發的 GNSS + IMU 的演算法。我是用 Python Server 對接 C Client ,以 Python Server 程式碼為例,只需要修改下面註解 # Modified 的部分:
#!/usr/bin/env python3
""" server.py - Echo server for sending/receiving C-like structs via socket
References:
- Ctypes: https://docs.python.org/3/library/ctypes.html
- Sockets: https://docs.python.org/3/library/socket.html
"""
import socket
import sys
import random
from ctypes import *
""" This class defines a C-like struct """
class Payload(Structure):
_fields_ = [("id", c_uint32),
("counter", c_uint32),
("temp", c_float)]
def main():
# Modified
server_addr = ('./FILE_NAME')
ssock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
print("Socket created")
try:
# bind the server socket and listen
ssock.bind(server_addr)
print("Bind done")
ssock.listen(3)
print("Server listening on port {:d}".format(PORT))
while True:
csock, client_address = ssock.accept()
print("Accepted connection from {:s}".format(client_address[0]))
buff = csock.recv(512)
while buff:
print("\nReceived {:d} bytes".format(len(buff)))
payload_in = Payload.from_buffer_copy(buff)
print("Received contents id={:d}, counter={:d}, temp={:f}".format(payload_in.id,
payload_in.counter,
payload_in.temp))
print("Sending it back.. ", end='')
nsent = csock.send(payload_in)
print("Sent {:d} bytes".format(nsent))
buff = csock.recv(512)
print("Closing connection to client")
print("----------------------------")
csock.close()
except AttributeError as ae:
print("Error creating the socket: {}".format(ae))
except socket.error as se:
print("Exception on socket: {}".format(se))
except KeyboardInterrupt:
ssock.close()
finally:
print("Closing socket")
ssock.close()
if __name__ == "__main__":
main()
搭配的 C Client 端程式碼如下,修改下面註解 # Modified 的部分:
/* client.c */
#include <sys/socket.h>
#include <arpa/inet.h> //inet_addr
#include <unistd.h> //write
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma pack(1)
typedef struct payload_t {
uint32_t id;
uint32_t counter;
float temp;
} payload;
#pragma pack()
void sendMsg(int sock, void* msg, uint32_t msgsize)
{
if (write(sock, msg, msgsize) < 0)
{
printf("Can't send message.\n");
close(sock);
exit(1);
}
printf("Message sent (%d bytes).\n", msgsize);
return;
}
int main()
{
# Modified
int BUFFSIZE = sizeof(payload);
char buff[BUFFSIZE];
int sock;
int nread;
float mintemp = -10.0;
float maxtemp = 30.0;
time_t t;
srand((unsigned) time(&t));
# Modified
struct sockaddr_un server_address;
server_address.sun_family = AF_UNIX;
strcpy(server_address.sun_path, "./FILE_NAME");
if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
printf("ERROR: Socket creation failed\n");
return 1;
}
if (connect(sock, (struct sockaddr*)&server_address, sizeof(server_address)) < 0) {
printf("ERROR: Unable to connect to server\n");
return 1;
}
payload data;
for(int i = 0; i < 5; i++) {
data.id = 1;
data.counter = i;
data.temp = mintemp + rand() / (RAND_MAX / (maxtemp - mintemp + 1.0) + 1.0);
printf("\nSending id=%d, counter=%d, temp=%f\n", data.id, data.counter, data.temp);
sendMsg(sock, &data, sizeof(payload));
bzero(buff, BUFFSIZE);
nread = read(sock, buff, BUFFSIZE);
printf("Received %d bytes\n", nread);
payload *p = (payload*) buff;
printf("Received id=%d, counter=%d, temp=%f\n",
p->id, p->counter, p->temp);
}
// close the socket
close(sock);
return 0;
}
至此就完成一個 Python-C Socket 互傳 Payload 資料的架構,再按自己需要替換 Payload的內容,或是刪掉回傳部分,改成單向傳輸。
另外備份參考來源的 Client 端 Python 程式碼以及 Server 端 C 程式碼。
Client 端 Python 語言程式碼:
#!/usr/bin/env python3
""" client.py - Echo client for sending/receiving C-like structs via socket
References:
- Ctypes: https://docs.python.org/3/library/ctypes.html
- Sockets: https://docs.python.org/3/library/socket.html
"""
import socket
import sys
import random
from ctypes import *
""" This class defines a C-like struct """
class Payload(Structure):
_fields_ = [("id", c_uint32),
("counter", c_uint32),
("temp", c_float)]
def main():
server_addr = ('localhost', 2300)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect(server_addr)
print("Connected to {:s}".format(repr(server_addr)))
for i in range(5):
print("")
payload_out = Payload(1, i, random.uniform(-10, 30))
print("Sending id={:d}, counter={:d}, temp={:f}".format(payload_out.id,
payload_out.counter,
payload_out.temp))
nsent = s.send(payload_out)
# Alternative: s.sendall(...): coontinues to send data until either
# all data has been sent or an error occurs. No return value.
print("Sent {:d} bytes".format(nsent))
buff = s.recv(sizeof(Payload))
payload_in = Payload.from_buffer_copy(buff)
print("Received id={:d}, counter={:d}, temp={:f}".format(payload_in.id,
payload_in.counter,
payload_in.temp))
except AttributeError as ae:
print("Error creating the socket: {}".format(ae))
except socket.error as se:
print("Exception on socket: {}".format(se))
finally:
print("Closing socket")
s.close()
if __name__ == "__main__":
main()
Server 端 C 語言程式碼:
/* server.c */
#include <sys/socket.h>
#include <arpa/inet.h> //inet_addr
#include <unistd.h> //write
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma pack(1)
typedef struct payload_t {
uint32_t id;
uint32_t counter;
float temp;
} payload;
#pragma pack()
int createSocket(int port)
{
int sock, err;
struct sockaddr_in server;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("ERROR: Socket creation failed\n");
exit(1);
}
printf("Socket created\n");
bzero((char *) &server, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(port);
if (bind(sock, (struct sockaddr *)&server , sizeof(server)) < 0)
{
printf("ERROR: Bind failed\n");
exit(1);
}
printf("Bind done\n");
listen(sock , 3);
return sock;
}
void closeSocket(int sock)
{
close(sock);
return;
}
void sendMsg(int sock, void* msg, uint32_t msgsize)
{
if (write(sock, msg, msgsize) < 0)
{
printf("Can't send message.\n");
closeSocket(sock);
exit(1);
}
printf("Message sent (%d bytes).\n", msgsize);
return;
}
int main()
{
int PORT = 2300;
int BUFFSIZE = 512;
char buff[BUFFSIZE];
int ssock, csock;
int nread;
struct sockaddr_in client;
int clilen = sizeof(client);
ssock = createSocket(PORT);
printf("Server listening on port %d\n", PORT);
while (1)
{
csock = accept(ssock, (struct sockaddr *)&client, &clilen);
if (csock < 0)
{
printf("Error: accept() failed\n");
continue;
}
printf("Accepted connection from %s\n", inet_ntoa(client.sin_addr));
bzero(buff, BUFFSIZE);
while ((nread=read(csock, buff, BUFFSIZE)) > 0)
{
printf("\nReceived %d bytes\n", nread);
payload *p = (payload*) buff;
printf("Received contents: id=%d, counter=%d, temp=%f\n",
p->id, p->counter, p->temp);
printf("Sending it back.. ");
sendMsg(csock, p, sizeof(payload));
}
printf("Closing connection to client\n");
printf("----------------------------\n");
closeSocket(csock);
}
closeSocket(ssock);
printf("bye");
return 0;
}
另外 C 語言程式碼可以參考另外一個來源,寫的比較簡單:
https://blog.csdn.net/weixin_39258979/article/details/80931464
Server 端:
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/un.h> //define the sockaddr_un structure
int num_reverse(int num);
int main()
{
/* 断开之前的socket文件 */
unlink("server_socket");
/* 创建一个socket */
int server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un server_addr;
server_addr.sun_family = AF_UNIX;
strcpy(server_addr.sun_path, "server_socket");
/* 与本地文件进行绑定 */
bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
/* 监听 */
if(listen(server_sockfd, 5)<0);
{
perror("Listen failed");
}
int client_sockfd;
struct sockaddr_un client_addr;
socklen_t len = sizeof(client_addr);
while(1)
{
printf("server waiting:\n");
/* 接受一个客户端的链接 */
client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len);
/*数据交换 */
read(client_sockfd, &num, 4);
printf("get an integer from client: %d\n", num);
num=num_reverse(num);
write(client_sockfd, &num, 4);
/* 关闭socket */
close(client_sockfd);
}
return 0;
}
int num_reverse(int num)
{
int S=0,sum=0;
while(num)
{
S=num%10;
sum=10*sum+S;
num = num / 10;
}
return sum;
}
Client 端:
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/un.h>
int main()
{
/* 创建一个socket */
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un address;
address.sun_family = AF_UNIX;
strcpy(address.sun_path, "server_socket");
/*从键盘读取需要转置的整数*/
int num;
printf("Please enter the num to reverse:\n");
/* 链接至服务端 */
int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
if(result == -1)
{
perror("connect failed: ");
exit(1);
}
/* 数据处理 */
write(sockfd, &num, 4);//一个int 4个字节
read(sockfd, &num, 4);
printf("get an integer from server: %d\n", num);
/* 关闭socket */
close(sockfd);
return 0;
}
應用
實際應用可以參考下面這兩篇文章,例如你需要使用 Python NMEA 函式庫時,可以用 Python-C Socket把處理完的 GNSS 資料送往 IMU 的 C 主程式 (因為 IMU 頻率高,故以它為主體),當然在下面的文章內也有 C 語言直接處理的教學,所以直接用 IMU 作主程序,另起一個 Thread 處理 GNSS 數據也可以,全部在 C 語言環境完成。
我的案例是還要另外接收車子 CANBUS 的車速與檔位數據,但解碼 CANBUS protocol 與處理 DBC 檔案的函式庫 Python 比較好用,所以需要一個 Python-C 的資料共享架構,關於 CANBUS 的處理待以後再記錄。
在〈Socket AF_UNIX 實作 -【C 語言學習筆記】〉中有 1 則留言
留言功能已關閉。