comtcp简介
comtcp程序实现linux平台下串口网口数据转发功能,将串口与网口数据透明转发,应用该程序可以用网络TCP访问串口设备;
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h> /*文件控制定义*/
#include <termios.h> /*PPSIX终端控制定义*/
#include <string.h>
#include <sys/ioctl.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/epoll.h> /* epoll function */
#include <sys/sysinfo.h>
#define MAXEPOLLSIZE 5 //表示MAXEPOLLSIZE-1个
#define MAXLINE 256
#define MAXEVENTS 10
#define FALSE 0
#define TRUE 1
struct sPrivateReg
{
int fd;
unsigned int lastaccesstime;
}__attribute__((packed));
int connfd,fcom,comindex,tcpindex;
pthread_mutex_t rs485busdata_mut;
unsigned char volatile rs485busyflag;
void set_speed(int fd,int speed)
{
struct termios Opt;
tcgetattr(fd, &Opt);
cfsetispeed(&Opt, speed);
cfsetospeed(&Opt, speed);
tcsetattr(fd, TCSANOW, &Opt);
}
int set_Parity(int fd,int databits,int stopbits,int parity)
{
struct termios options;
if ( tcgetattr( fd,&options) != 0)
{
perror("SetupSerial 1");
return(FALSE);
}
options.c_iflag &= ~IXOFF;
options.c_iflag &= ~IXON;
options.c_cflag &= ~CSIZE;
switch (databits) /*设置数据位数*/
{
case 7:
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
printf("Unsupported data size\n");
return (FALSE);
}
switch (parity)
{
case 'n':
case 'N':
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E':
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD; /* 转换为偶效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'S':
case 's': /*as no parity*/
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
default:
printf("Unsupported parity\n");
return (FALSE);
}
/* 设置停止位*/
switch (stopbits)
{
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
printf("Unsupported stop bits\n");
return (FALSE);
}
/* Set input parity option */
if (parity != 'n')
options.c_iflag |= INPCK;
tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */
options.c_cc[VTIME] = 1; //100ms
options.c_cc[VMIN] = 255;
options.c_iflag &= ~(IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXOFF|IXANY);
options.c_oflag &= ~OPOST;
options.c_lflag &= ~(ECHO|ECHONL|ISIG|IEXTEN|ICANON);
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return (FALSE);
}
return (TRUE);
}
int OpenDev(char *Dev)
{
int fd = open( Dev, O_RDWR ); //| O_NOCTTY | O_NDELAY
if (-1 == fd)
{
/*设置数据位数*/
perror("Can't Open Serial Port");
return -1;
}
else
return fd;
}
void Uart_set_send(int fd)
{
int status;
ioctl(fd, TIOCMGET, &status);
status &= ~TIOCM_RTS;
ioctl(fd, TIOCMSET, &status);
}
void Uart_set_get(int fd)
{
int status;
ioctl(fd, TIOCMGET, &status);
status |= TIOCM_RTS;
ioctl(fd, TIOCMSET, &status);
}
void Chekbusy(int fd)
{
int status;
ioctl(fd, TIOCSERGETLSR, &status);
while(((status&0x0001)==0))
{
ioctl(fd, TIOCSERGETLSR, &status);
}
usleep(20);
Uart_set_get(fd);
}
void Uart_send(int fd,char *data ,int longer)
{
int nread;
Uart_set_send(fd);
nread = write(fd, data ,longer);
Chekbusy(fd);
}
int Uart_read(int fd,unsigned char *rcv_buf,int num,int sec,int usec)
{
int retval;
fd_set rfds;
struct timeval tv;
int ret=-1;
tv.tv_sec = sec;//set the rcv wait time
tv.tv_usec = usec;//100000us = 0.1s
while(1)
{
FD_ZERO(&rfds);
FD_SET(fd,&rfds);
retval = select(fd+1,&rfds,NULL,NULL,&tv);
if(retval ==-1)
{
perror("select()");
break;
}
else if(retval)
{
ret= read(fd,rcv_buf,num);
}
else
{
break;
}
}
return ret;
}
void DoComEvent(int *index)
{
int rwnum;
unsigned char RTU_RXBuf[255];
while(1)
{
rwnum=Uart_read(fcom,RTU_RXBuf,255,0,500000);
if(rwnum>0){send(connfd,RTU_RXBuf , rwnum, 0);rs485busyflag=0;}
usleep(1000);
}
}
int setnonblocking(int sockfd)
{
if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1) {
return -1;
}
return 0;
}
int getavailablefd(unsigned int nowdatetime,struct sPrivateReg Private_Reg_Buf[])
{
unsigned int oversecmax = 0;
int ret = -1;
int i;
for(i = 0; i< MAXEPOLLSIZE; i++)
{
if(Private_Reg_Buf[i].fd == -1)
return i;
else if(oversecmax<nowdatetime-Private_Reg_Buf[i].lastaccesstime)
{
oversecmax = nowdatetime-Private_Reg_Buf[i].lastaccesstime;
ret = i;
}
}
if(oversecmax>60)
{
printf("close_fd(overtime): %d\n", Private_Reg_Buf[ret].fd);
Private_Reg_Buf[ret].fd = -1;
shutdown(Private_Reg_Buf[ret].fd, SHUT_RDWR);
close(Private_Reg_Buf[ret].fd);
}
else
ret = -1;
return ret;
}
struct sPrivateReg * findfd(int fd,struct sPrivateReg Private_Reg_Buf[])
{
int i;
for(i = 0; i< MAXEPOLLSIZE; i++)
if(Private_Reg_Buf[i].fd == fd) return &Private_Reg_Buf[i];
return NULL;
}
unsigned int gettickcount()
{
struct sysinfo info;
sysinfo(&info);
return info.uptime;
}
void initPrivate_Reg_Buf(int index,struct sPrivateReg Private_Reg_Buf[])
{
memset(&Private_Reg_Buf[index], 0, sizeof(struct sPrivateReg));
Private_Reg_Buf[index].fd = -1;
}
void close_fd(int fd,struct sPrivateReg Private_Reg_Buf[])
{
int i;
for(i = 0; i< MAXEPOLLSIZE; i++)
if(Private_Reg_Buf[i].fd == fd)
{
printf("close_fd(normal): %d\n", fd);
Private_Reg_Buf[i].fd = -1;
shutdown(fd, SHUT_RDWR);
close(fd);
return;
}
}
int handle(int confd,int *index)
{
int rtcpnum;
struct timeval tm,tm1;
unsigned char buf[MAXLINE];
rtcpnum=read(confd, buf, MAXLINE);
if (rtcpnum == 0) {
printf("client close the connection\n");
close(confd);
return -1;
}
if (rtcpnum < 0) {
perror("read error");
close(confd);
return -1;
}
pthread_mutex_lock(&rs485busdata_mut);
rs485busyflag=1;
connfd=confd;
Uart_send(fcom,(char *)buf,rtcpnum);
gettimeofday(&tm, NULL);
while(rs485busyflag){
gettimeofday(&tm1, NULL);
if(((tm1.tv_sec-tm.tv_sec)*1000+(tm1.tv_usec-tm.tv_usec)/1000)>=500)
{rs485busyflag=0;break;}
else usleep(2000);
}
pthread_mutex_unlock(&rs485busdata_mut);
return 0;
}
void DoTcpEvent1(int *index)
{
int i,fd_index;
int count=0;
int servPort =502+(*index);
int listenq = 6;//监听队列
int listenfd, connect_fd, kdpfd, nfds, n, curfds;
struct sockaddr_in servaddr, cliaddr;
socklen_t socklen = sizeof(struct sockaddr_in);
struct epoll_event ev;
struct epoll_event events[MAXEVENTS];
struct sPrivateReg Private_Reg_Buf[MAXEPOLLSIZE];
unsigned int datetime;
for(i=0;i<MAXEPOLLSIZE;i++)//初始化在线列表
Private_Reg_Buf[i].fd = -1;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
servaddr.sin_port = htons (servPort);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd == -1) {
perror("can't create socket file");
}
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if (setnonblocking(listenfd) < 0) {
perror("setnonblock error");
}
if (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) == -1)
{
perror("bind error");
}
if (listen(listenfd, listenq) == -1)
{
perror("listen error");
}
/* 创建 epoll 句柄,把监听 socket 加入到 epoll 集合里 */
kdpfd = epoll_create(MAXEPOLLSIZE);
ev.events = EPOLLIN | EPOLLHUP | EPOLLERR;
ev.data.fd = listenfd;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listenfd, &ev) < 0)
{
fprintf(stderr, "epoll set insertion error: fd=%d\n", listenfd);
}
curfds = 1;
printf("transparent comtcp startup,port %d, max client is %d, backlog is %d\n", servPort, MAXEPOLLSIZE, listenq);
while(1)
{
/* 等待有事件发生 */
nfds = epoll_wait(kdpfd, events, MAXEVENTS, -1);
datetime = gettickcount();
if (nfds == -1)
{
perror("epoll_wait");
continue;
}
/* 处理所有事件 */
for (n = 0; n < nfds; ++n)
{
if (events[n].data.fd == listenfd)
{
connect_fd = accept(listenfd, (struct sockaddr *)&cliaddr,&socklen);
if (connect_fd < 0)
{
perror("accept error");
exit(1);
continue;
}
else
{
printf("accept success %d\n",count++);
}
fd_index = getavailablefd(datetime,Private_Reg_Buf);
if(fd_index>=0)
{
initPrivate_Reg_Buf(fd_index,Private_Reg_Buf);
Private_Reg_Buf[fd_index].fd = connect_fd;
Private_Reg_Buf[fd_index].lastaccesstime = datetime;
ev.events = EPOLLIN | EPOLLHUP | EPOLLERR;
ev.data.fd = connect_fd;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, connect_fd, &ev) < 0)
{
fprintf(stderr, "epoll set insertion error: fd=%d\n",connect_fd);
}
}
else
{
shutdown(connect_fd, SHUT_RDWR);
close(connect_fd);
printf("full tcp pool!\n");
}
}
else if(events[n].events&EPOLLHUP)//#
{
epoll_ctl(kdpfd, EPOLL_CTL_DEL, events[n].data.fd, &ev) ;
close_fd(events[n].data.fd,Private_Reg_Buf);
printf("EPOLLHUP fd: %d\n",events[n].data.fd);
}
else if(events[n].events&EPOLLERR)
{
/* read data from client */
epoll_ctl(kdpfd, EPOLL_CTL_DEL, events[n].data.fd, &ev) ;
close_fd(events[n].data.fd,Private_Reg_Buf);
printf("EPOLLERR fd: %d\n",events[n].data.fd);
}
else if(events[n].events&EPOLLIN)
{
//recive_form_client(events[n].data.fd);
struct sPrivateReg *pPrivate_Reg_Buf = findfd(events[n].data.fd,Private_Reg_Buf);
if(pPrivate_Reg_Buf)
{
pPrivate_Reg_Buf->lastaccesstime = gettickcount();
if (handle(events[n].data.fd,index) < 0)
{
close_fd(pPrivate_Reg_Buf->fd,Private_Reg_Buf);
}
}
}
else
{
/* read data from client */
printf("UNKNOWN POLL MESSAGE fd: %d\n",events[n].data.fd);
}
}
}
close(listenfd);
}
int main(int argc, char **argv)
{
pthread_t com,tcp;
int baud;
char dev[10],comdev[20];
comdev[0]=0;
//串口初始化
if(argc!=3)
{
fprintf(stderr,"Usage:comtcp baud dev\nexample:comtcp 9600 ttySAC0\n");
exit(1);
}
sscanf(argv[1],"%d",&baud);
sscanf(argv[2],"%s",dev);
strcat(comdev,"/dev/");
strcat(comdev,dev);
fcom= OpenDev(comdev);
if(fcom)
{
switch(baud)
{
case 2400:set_speed(fcom,B2400);break;
case 4800:set_speed(fcom,B4800);break;
case 9600:set_speed(fcom,B9600);break;
case 19200:set_speed(fcom,B19200);break;
case 38400:set_speed(fcom,B38400);break;
case 57600:set_speed(fcom,B57600);break;
case 115200:set_speed(fcom,B115200);break;
default:set_speed(fcom,B9600);printf("daufalt baud:B9600\n");break;
}
}
else
{
printf("Can not open COM %s!\n",dev);
exit(1);
}
if (set_Parity(fcom,8,1,'N')== FALSE)
{
printf("Set Parity Error\n");
exit(1);
}
comindex=0;
tcpindex=0;
pthread_create(&com,NULL,(void*)&DoComEvent,(void *)&comindex);
pthread_create(&tcp,NULL,(void*)&DoTcpEvent1,(void *)&tcpindex);
while (1)
{
sleep(1);
}
close(connfd);//关闭设备
close(fcom);
return 0;
}
使用说明
配置系统
编译源码将comtcp复制到系统某个路径下;
运行程序,执行命令
测试结果
准备下面两个调试软件:
打开软件,用网络调试软件以TCP客户端连接设备IP(例如192.168.1.15),502端口,串口调试软件连接到设备串口;
网络调试软件发送数据会在串口调试软件中收到,串口调试软件发送数据会在网络调试软件中收到;实现了串口与网口的数据互相转发;
下图为测试结果: