目录
4、receive2.c---对应的是串口多线程网络发送的执行程序,没有改文件名字
1、查看Ubuntu自己的IP:192.168.92.128
——————————————————————————————————————————————————————————————————————————————————————————————
项目场景:
运行环境:Ubuntu18.04,linux系统,gcc编译器
实现描述:
- 需求:由于目前需要实现网络通信,将串口接收的数据上传做准备,因此需要将客户端服务器收发的程序,和串口数据接收放在一起,用来实现终端的数据接收及转发线程。
- 解决思路:所以通过将客户端的发送部分功能函数和多线程串口收发合并在一起,当串口读取完毕的时候,将指定内容发送到服务器中(此处发给服务器的不是串口收到的,是先用指定内容来测试功能的)。
- 问题:每个串口收发两个线程,有点浪费资源,考虑整合或者构建中间线程。
实现过程:
创建.c文件,编译,按照指定顺序执行。
基于前两篇文章进行修正和增改:
https://mp.csdn.net/console/editor/html/115033576
https://mp.csdn.net/console/editor/html/115017450
1、mkptych.py
注意:在终端里运行“python mkptych.py&”,这样就可以生成一个基于pty(伪终端)的虚拟端口对,两个设备名会显示在终端里。然后就可以利用这两个设备名在本机上进行虚拟串口之类的调试, 使用完后用ps查看这个python进程的pid号,然后kill掉即可。
参考:https://blog.csdn.net/haifengid/article/details/51996613
#! /usr/bin/env python
#coding=utf-8
import pty
import os
import select
def mkpty():
#打开伪终端
master1, slave = pty.openpty()
slaveName1 = os.ttyname(slave)
master2, slave = pty.openpty()
slaveName2 = os.ttyname(slave)
print '\nslavedevice names: ', slaveName1, slaveName2
return master1, master2
if __name__ == "__main__":
master1, master2 = mkpty()
while True:
rl, wl, el = select.select([master1,master2], [], [], 1)
for master in rl:
data = os.read(master, 128)
print "read %d data." % len(data)
if master==master1:
os.write(master2, data)
else:
os.write(master1, data)
1、server1.c
//server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#define SERVPORT 9999
#define BACKLOG 10
#define MAX_CONNECTED_NO 10
#define MAXDATASIZE 5
int main()
{
struct sockaddr_in server_sockaddr,client_sockaddr;
int sin_size,recvbytes;
int sockfd,client_fd;
char buf[MAXDATASIZE];
/*建立 socket 连接*/
if((sockfd = socket(AF_INET,SOCK_STREAM,0))== -1){
perror("socket");
exit(1);
}
printf("socket success!,sockfd=%d\n",sockfd);
/*设置 sockaddr_in 结构体中相关参数*/
server_sockaddr.sin_family=AF_INET;
server_sockaddr.sin_port=htons(SERVPORT);
server_sockaddr.sin_addr.s_addr=INADDR_ANY;
bzero(&(server_sockaddr.sin_zero),8);
/*绑定函数 bind*/
if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct
sockaddr))== -1){
perror("bind");
exit(1);
}
printf("bind success!\n");
/*调用 listen 函数*/
if(listen(sockfd,BACKLOG)== -1){
perror("listen");
exit(1);
}
printf("listening....\n");
/*调用 accept 函数,等待客户端的连接*/
if((client_fd=accept(sockfd,(struct sockaddr *)&client_sockaddr,&sin_size))== -1){
perror("accept");
exit(1);
}
/*调用 recv 函数接收客户端的请求*/
if((recvbytes=recv(client_fd,buf,MAXDATASIZE,0))== -1){
perror("recv");
exit(1);
}
printf("received a connection :%s\n",buf);
close(sockfd);
}
2、send1.c
//send1.c
#include<stdio.h>
#include<string.h>
#include<malloc.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<termios.h>
#define MAX_BUFFER_SIZE 512
#define MODEMDEVICE "/dev/pts/6"
int fd,flag_close;
int open_serial()
{
//这里的/dev/pts/1是使用mkptych.py虚拟的两个串口名字之一
fd = open(MODEMDEVICE,O_RDWR | O_NOCTTY | O_NONBLOCK);
if(fd == -1)
{
perror("open serial porterror!\n");
return -1;
}
printf("Open serial portsuccess!\n");
return 0;
}
int main(int argc, char*argv[])
{
printf("读取设备为:%s\n",MODEMDEVICE);
char sbuf[] = {"Hello, this is a serial port /dev/pts/8 test!\n"};
int retv;
struct termios option;
retv =open_serial();
if(retv <0)
{
perror("open serial porterror!\n");
return -1;
}
printf("Ready for sending data...\n");
tcgetattr(fd,&option);
cfmakeraw(&option);
cfsetispeed(&option,B9600);
cfsetospeed(&option,B9600);
tcsetattr(fd, TCSANOW,&option);
int length =sizeof(sbuf);
retv = write(fd, sbuf,length);
printf("write success. \n");
if(retv == -1)
{
perror("Write dataerror!\n");
return -1;
}
printf("The number of charsent is %d\n", retv);
return 0;
}
3、send1_2.c
//send.c
#include<stdio.h>
#include<string.h>
#include<malloc.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<termios.h>
#define MAX_BUFFER_SIZE 512
#define MODEMDEVICE "/dev/pts/8"
int fd,flag_close;
int open_serial()
{
//这里的/dev/pts/1是使用mkptych.py虚拟的两个串口名字之一
fd = open(MODEMDEVICE,O_RDWR | O_NOCTTY | O_NONBLOCK);
if(fd == -1)
{
perror("open serial porterror!\n");
return -1;
}
printf("Open serial portsuccess!\n");
return 0;
}
int main(int argc, char*argv[])
{
printf("读取设备为:%s\n",MODEMDEVICE);
char sbuf[] = {"Hello, this is a serial port /dev/pts/10 test!\n"};
int retv;
struct termios option;
retv =open_serial();
if(retv <0)
{
perror("open serial porterror!\n");
return -1;
}
printf("Ready for sending data...\n");
tcgetattr(fd,&option);
cfmakeraw(&option);
cfsetispeed(&option,B9600);
cfsetospeed(&option,B9600);
tcsetattr(fd, TCSANOW,&option);
int length =sizeof(sbuf);
retv = write(fd, sbuf,length);
printf("write success. \n");
if(retv == -1)
{
perror("Write dataerror!\n");
return -1;
}
printf("The number of charsent is %d\n", retv);
return 0;
}
4、receive2.c---对应的是串口多线程网络发送的执行程序,没有改文件名字
#include<pthread.h>
#include<stdio.h>
#include<sys/time.h>
#include<string.h>
#include<termios.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define BAUDRATE B115200
#define MODEMDEVICE_1 "/dev/pts/5"
#define MODEMDEVICE_2 "/dev/pts/7"
#define SERVPORT 5001
#define MAXDATASIZE 100
#define R_BUF_LEN (256)
void printtid(void);
int clientAccess();
void* com_read_1(void* pstatu)
{
printtid();
int i=0;
int fd,c=0,num;
struct termios oldtio,newtio;
char buf[R_BUF_LEN];
printf("start...\n");
/*打开PC机的COM1通信端口*/
fd=open(MODEMDEVICE_1,O_RDWR | O_NOCTTY | O_NONBLOCK/*| O_NDELAY*/);
if(fd<0)
{
perror(MODEMDEVICE_1);
exit(1);
}
printf("open ...\n");
/*将目前终端机的结构保存至oldtio结构*/
tcgetattr(fd,&oldtio);
/*清除newtio结构,重新设置通信协议*/
bzero(&newtio,sizeof(newtio));
/*通信协议设为8N1,8位数据位,N没有效验,1位结束位*/
newtio.c_cflag = BAUDRATE |CS8|CLOCAL|CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
/*设置为正规模式*/
newtio.c_lflag=ICANON;
/*清除所有队列在串口的输入*/
tcflush(fd,TCIFLUSH); /*新的termios的结构作为通信端口的参数*/
tcsetattr(fd,TCSANOW,&newtio);
printf("reading...\n");
while(*(int*)pstatu)
{
num = read(fd,buf, R_BUF_LEN);
buf[R_BUF_LEN-1] = 0;
if(num > 0 && num <= R_BUF_LEN)
{
buf[num]=0;
printf("%s", buf);
fflush(stdout);
}
}
int a=clientAccess();
printf("%d",a);
printf("close...\n");
close(fd);
/*恢复旧的通信端口参数*/
tcsetattr(fd,TCSANOW,&oldtio);
}
void* com_send_1(void* p)
{
printtid();
int fd,c=0;
struct termios oldtio,newtio;
char ch;
static char s1[20];
printf("Start...\n");
/*打开arm平台的COM1通信端口*/
fd=open(MODEMDEVICE_1,O_RDWR | O_NOCTTY);
if(fd<0)
{
perror(MODEMDEVICE_1);
exit(1);
}
printf(" Open...\n");
/*将目前终端机的结构保存至oldtio结构*/
tcgetattr(fd,&oldtio);
/*清除newtio结构,重新设置通信协议*/
bzero(&newtio,sizeof(newtio));
/*通信协议设为8N1*/
newtio.c_cflag =BAUDRATE |CS8|CLOCAL|CREAD; //波特率 8个数据位 本地连接 接受使能
newtio.c_iflag=IGNPAR; //忽略奇偶校验错误
newtio.c_oflag=0;
/*设置为正规模式*/
newtio.c_lflag=ICANON; //规范输入
/*清除所有队列在串口的输出*/
tcflush(fd,TCOFLUSH);
/*新的termios的结构作为通信端口的参数*/
tcsetattr(fd,TCSANOW,&newtio);
printf("Writing...\n");
while(*(char*)p != 0)
{
int res = 0;
res = write(fd,(char*)p, 1);
if(res != 1) printf("send %c error\n", *(char*)p);
else printf("%c", *(char*)p);
++p;
}
printf("\nClose...\n");
close(fd);
/*还原旧的通信端口参数*/
tcsetattr(fd,TCSANOW,&oldtio);
printf("leave send thread\n");
}
void* com_read_2(void* pstatu)
{
printtid();
int i=0;
int fd,c=0,num;
struct termios oldtio,newtio;
char buf[R_BUF_LEN];
printf("start...\n");
/*打开PC机的COM1通信端口*/
fd=open(MODEMDEVICE_2,O_RDWR | O_NOCTTY | O_NONBLOCK/*| O_NDELAY*/);
if(fd<0)
{
perror(MODEMDEVICE_2);
exit(1);
}
printf("open ...\n");
/*将目前终端机的结构保存至oldtio结构*/
tcgetattr(fd,&oldtio);
/*清除newtio结构,重新设置通信协议*/
bzero(&newtio,sizeof(newtio));
/*通信协议设为8N1,8位数据位,N没有效验,1位结束位*/
newtio.c_cflag = BAUDRATE |CS8|CLOCAL|CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
/*设置为正规模式*/
newtio.c_lflag=ICANON;
/*清除所有队列在串口的输入*/
tcflush(fd,TCIFLUSH); /*新的termios的结构作为通信端口的参数*/
tcsetattr(fd,TCSANOW,&newtio);
printf("reading...\n");
while(*(int*)pstatu)
{
num = read(fd,buf, R_BUF_LEN);
buf[R_BUF_LEN-1] = 0;
if(num > 0 && num <= R_BUF_LEN)
{
buf[num]=0;
printf("%s", buf);
fflush(stdout);
}
}
printf("close...\n");
close(fd);
/*恢复旧的通信端口参数*/
tcsetattr(fd,TCSANOW,&oldtio);
}
void* com_send_2(void* p)
{
printtid();
int fd,c=0;
struct termios oldtio,newtio;
char ch;
static char s1[20];
printf("Start...\n");
/*打开arm平台的COM1通信端口*/
fd=open(MODEMDEVICE_2,O_RDWR | O_NOCTTY);
if(fd<0)
{
perror(MODEMDEVICE_2);
exit(1);
}
printf(" Open...\n");
/*将目前终端机的结构保存至oldtio结构*/
tcgetattr(fd,&oldtio);
/*清除newtio结构,重新设置通信协议*/
bzero(&newtio,sizeof(newtio));
/*通信协议设为8N1*/
newtio.c_cflag =BAUDRATE |CS8|CLOCAL|CREAD; //波特率 8个数据位 本地连接 接受使能
newtio.c_iflag=IGNPAR; //忽略奇偶校验错误
newtio.c_oflag=0;
/*设置为正规模式*/
newtio.c_lflag=ICANON; //规范输入
/*清除所有队列在串口的输出*/
tcflush(fd,TCOFLUSH);
/*新的termios的结构作为通信端口的参数*/
tcsetattr(fd,TCSANOW,&newtio);
printf("Writing...\n");
while(*(char*)p != 0)
{
int res = 0;
res = write(fd,(char*)p, 1);
if(res != 1) printf("send %c error\n", *(char*)p);
else printf("%c", *(char*)p);
++p;
}
printf("\nClose...\n");
close(fd);
/*还原旧的通信端口参数*/
tcsetattr(fd,TCSANOW,&oldtio);
printf("leave send thread\n");
}
int clientAccess()
{
int sockfd,numbytes;
char buff[100]="1234567";
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("192.168.92.128"); //本次设置的是本地连接
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);
printf("%s",buff);
//客户端开始写入数据,*****此处buff需要和服务器中接收
//if(send(sockfd,buff,sizeof(buff),0) == -1)
if((send(sockfd,"hello",5,0))== -1){
{
perror("send error \n");
exit(1);
}
}
close(sockfd);
return 0;
}
}
/*
开始线程
thread_fun 线程函数
pthread 线程函数所在pthread变量
par 线程函数参数
COM_STATU 线程函数状态控制变量 1:运行 0:退出
*/
int start_thread_func(void*(*func)(void*), pthread_t* pthread, void* par, int* COM_STATU)
{
*COM_STATU = 1;
memset(pthread, 0, sizeof(pthread_t));
int temp;
/*创建线程*/
if((temp = pthread_create(pthread, NULL, func, par)) != 0)
printf("线程创建失败!\n");
else{
int id = pthread_self();
printf("线程%lu被创建\n", *pthread);
}
return temp;
}
/*
结束线程
pthread 线程函数所在pthread变量
COM_STATU 线程函数状态控制变量 1:运行 0:退出
*/
int stop_thread_func(pthread_t* pthread, int* COM_STATU)
{
printf("prepare stop thread %lu\n", *pthread);
*COM_STATU = 0;
if(*pthread !=0)
{
pthread_join(*pthread, NULL);
}
printf("线程%lu退出!\n", *pthread);
}
void printtid(void)
{
int id = pthread_self();
printf("in thread %u\n", id);
}
int main()
{
pthread_t thread[4];
printtid();
const int READ_THREAD_ID_1 = 0;
const int SEND_THREAD_ID_1= 1;
const int READ_THREAD_ID_2 = 2;
const int SEND_THREAD_ID_2= 3;
int COM_READ_STATU_1 = 0;
int COM_SEND_STATU_1 = 0;
int COM_READ_STATU_2 = 0;
int COM_SEND_STATU_2 = 0;
//1
printf("读取设备为:%s\n",MODEMDEVICE_1);
if(start_thread_func(com_read_1, &thread[READ_THREAD_ID_1], &COM_READ_STATU_1, &COM_READ_STATU_1) != 0)
{
printf("error to leave\n");
return -1;
}
printf("wait 3 sec\n");
sleep(3);
printf("wake after 3 sec\n");
if(start_thread_func(com_send_1, &thread[SEND_THREAD_ID_1],"FF CC 03 A3 A0", &COM_SEND_STATU_1) != 0)
{
printf("error to leave\n");
return -1;
}
//printtid();
printf("wait 6 sec\n");
sleep(6);
printf("wake after 6 sec\n");
//2
printf("读取设备为:%s\n",MODEMDEVICE_2);
if(start_thread_func(com_read_2, &thread[READ_THREAD_ID_2], &COM_READ_STATU_1, &COM_READ_STATU_2) != 0)
{
printf("error to leave\n");
return -1;
}
printf("wait 3 sec\n");
sleep(3);
printf("wake after 3 sec\n");
if(start_thread_func(com_send_2, &thread[SEND_THREAD_ID_2],"FF C7 03 A3 A0", &COM_SEND_STATU_2) != 0)
{
printf("error to leave\n");
return -1;
}
//printtid();
printf("wait 6 sec\n");
sleep(6);
printf("wake after 6 sec\n");
stop_thread_func(&thread[READ_THREAD_ID_1], &COM_READ_STATU_1);
stop_thread_func(&thread[SEND_THREAD_ID_1], &COM_SEND_STATU_1);
stop_thread_func(&thread[READ_THREAD_ID_2], &COM_READ_STATU_2);
stop_thread_func(&thread[SEND_THREAD_ID_2], &COM_SEND_STATU_2);
return 0;
}
执行结果:
结果图分析:
注意:
- 用Python虚拟两对串口,每次关机会丢失这两个虚拟设备文件,如果需要重新创建的话,可能产生的配对号就不是之前配对好的那两对了。先暂且自己创建着吧,不要关机,就挂起,还没验证过有没有影响。
- 首先打开服务器接收(server1)程序运行,然后运行串口收发程序(receive2),然后看到串口接收程序执行到了reding的时候,运行第一个创建设备所配对号的那个send(也就是说程序中的设备文件和接收程序中首先接收的设备是配对的),等到下一个reding,运行第二对设备中的模拟发送程序(send1_2)。然后就会出现理想的结果。
- 可以看到串口接收数据并发送到服务器的这个程序:刚开始创建了两个线程,一个是读取,一个是发送;接着又创建了另一对线程,实现数据的接收发送;最后结束四个线程。可以看到两个设备的发送数据并不相同,这个是程序中写好的,用来验证程序正确性,同时模拟向不同串口设备发送不同启动命令。
问题及解决方案:
1、查看Ubuntu自己的IP:192.168.92.128
- 在设置界面中查看IP:
- 通过命令查看:
linux查看本机ip的命令是“ifconfig”命令和“ip address”命令;linux查看端口号的命令是“lsof -i:port”命令。貌似有些命令需要先进行安装才可以执行,所以我是通过界面查看的IP,为使用命令。
2、函数未定义等低级错误及解决方式。
头文件未添加导致函数未定义:Sleep未定义 | #include<unistd.h> |
warning: incompatible implicit declaration of built-in function 'exit' | #include <stdlib.h> |
incompatible implicit declaration of built-in function ‘bzero’ | #include <strings.h> |
printf的输出类型问题 | https://blog.csdn.net/u011425939/article/details/71272474 printf的线程pthread_t类型,应该是“%lu”; 输出读取设备的时候,程序中的宏定义显示的不是int类型,所以用%d存在输出类型匹配问题,%s : 输出字符串,参数为char指针,显示字符串所有的字符。 |
其他调用该函数printtid的函数,会出现printtid未定义的错误。 | 对其进行提前声明或者直接把函数定义放在最开始即可。 |
出现未定义的引用:对‘pthread_cond_destory’未定义的引用 | 是程序中的destroy这个单词的顺序写错了,真的绝了 |
出现未定义的引用:对‘pthread_cond_create’未定义的引用(相关线程函数未定义) | 编译语句中,添加-lpthread进行解决。(gcc a.c -lpthread -o a) |
显示现在在运行的所有线程 | ps -ef |
杀死对应线程 | kill -9 pid,并按回车,来根据pid值来将某个进程关闭 |
端口冲突(Address already in use) | sudo lsof -i:(port) ,然后sudo kill pid |
warning: implicit declaration of function ‘inet_addr’; did you mean ‘si_addr’? | #include <arpa/inet.h> |
出现“核心已转储”的错误。 | 输出的变量,一定要记得加类型(%d之类的,一一对应的值和类型,而不是直接输出)字符串可以直接输出 |
3、VSCode折叠代码快捷键
1.首先F1查看帮助。
2.搜索fold,找到你需要的命令,按照提示的快捷键操作即可。
折叠全部代码的快捷键为:Ctrl+K、Ctrl+0(不是字母O而是数字0)。
未解决问题:
1、这个send1的程序,需要看着serial_Test1这边,当读的那几秒,抓紧时间进行发送,否则接收的线程就不接收了,这个bug还没解决。
2、程序为何会自动结束。明明已经注释掉了join,并且没有找到逻辑上的控制变量的原因。
3、用Python虚拟两对串口,每次关机会丢失这两个虚拟设备文件,如果需要重新创建的话,可能产生的配对号就不是之前配对好的那两对了。
简单的记录如上,稍后有其他相关问题会更新的。