主要功能
- 支持串口收TCP发
- 支持TCP收串口发
- 支持断线重连
- 更多的细节及实现不在此做过多讨论。
代码
注:如串口收到数据时网络断开需保证数据在下次连接TCP时发出不在该实现内,如有兴趣可根据此框架自行修改。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <time.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <stdbool.h>
#include <stdint.h>
#define MAX_COMM_LEN 1024
uint8_t uartrecv_buf[MAX_COMM_LEN]; //串口收发缓冲区
uint8_t netrecv_buf[MAX_COMM_LEN]; //网络收发缓冲区
static int uart_dev;
typedef struct {
int speed;
int vtime;
int vmin;
char ipaddr[16];
int port;
}Uart2TcpConfig;
static void set_opt(int fd, int nSpeed, int nBits, char nEvent, int nStop, int vtime, int vmin)
{
struct termios newtio,oldtio;
if(tcgetattr(fd,&oldtio) != 0) {
printf("Failed to call tcgetattr.");
return;
}
memset(&newtio, 0, sizeof(newtio));
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
switch( nBits )
{
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;
break;
}
switch( nEvent )
{
case 'O':
newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
newtio.c_iflag |= (INPCK | ISTRIP);
break;
case 'E':
newtio.c_iflag |= (INPCK | ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
break;
case 'N':
newtio.c_cflag &= ~PARENB;
break;
}
switch( nSpeed )
{
case 2400:
cfsetispeed(&newtio, B2400);
cfsetospeed(&newtio, B2400);
break;
case 4800:
cfsetispeed(&newtio, B4800);
cfsetospeed(&newtio, B4800);
break;
case 9600:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
case 115200:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
break;
case 460800:
cfsetispeed(&newtio, B460800);
cfsetospeed(&newtio, B460800);
break;
default:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
}
if( nStop == 1 )
newtio.c_cflag &= ~CSTOPB;
else if ( nStop == 2 )
newtio.c_cflag |= CSTOPB;
newtio.c_cc[VTIME] = vtime;
newtio.c_cc[VMIN] = vmin;
tcflush(fd, TCIOFLUSH);
if((tcsetattr(fd, TCSANOW, &newtio))!=0)
{
printf("COM set error");
return;
}
}
static bool uart_init(char *uart, int speed, int vtime, int vmin)
{
if((uart_dev = open(uart, O_RDWR | O_NOCTTY | O_NDELAY)) < 0){
printf("Failed to open %s\n", uart);
return false;
}
set_opt(uart_dev, speed, 8, 'N', 1, vtime, vmin);
fcntl(uart_dev, F_SETFL, O_NONBLOCK);
printf("Uart init success.\n");
return true;
}
static int set_nonblocking(int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
return -1;
}
flags |= O_NONBLOCK;
return fcntl(fd, F_SETFL, flags);
}
static bool try_to_reconnect(int sockfd, struct sockaddr_in *serv_addr)
{
printf("try to reconnect ...\n");
close(sockfd);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
int ret = connect(sockfd, (struct sockaddr *)serv_addr, sizeof(struct sockaddr_in));
if(ret == 0){
set_nonblocking(sockfd);
printf("Reconnect success.\n");
return true;
}
return false;
}
static bool net_status(int sockfd)
{
if(sockfd <= 0)
return false;
struct tcp_info info;
int len = sizeof(info);
getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
if((info.tcpi_state == TCP_ESTABLISHED))
return true;
else
return false;
}
static int data_send(int fd, uint8_t *buf, int len)
{
int size, count = 0;
while(len > 0){
size = write(fd, buf+count, len - count);
if(size < 1) {
break;
}
count += size;
len -= size;
}
return count;
}
static int data_recv(int fd, uint8_t *buf, int len)
{
int size = 0, count = 0;
do {
size = read(fd, buf+count, len - count);
if(size < 1) {
break;
}
count += size;
} while (size > 0);
return count;
}
int main (int argc, char *argv[])
{
if(argc != 7) {
printf("Params error.\n");
printf("eg. %s /dev/ttyS2 115200 192.168.4.100 6800 1(vtime) 0(vmin)\n", argv[0]);
return -1;
}
Uart2TcpConfig config;
config.speed = atoi(argv[2]);
strcpy(config.ipaddr, argv[3]);
config.port = atoi(argv[4]);
config.vtime = atoi(argv[5]);
config.vmin = atoi(argv[6]);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1) {
perror("sockfd failed!\n");
return -1;
}
if(!uart_init(argv[1], config.speed, config.vtime, config.vmin)) {
printf("Uart init failed.\n");
return -1;
}
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(config.ipaddr);
serv_addr.sin_port = htons(config.port);
int ret = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
if(ret != 0) {
while(1){
if(try_to_reconnect(sockfd, &serv_addr))
break;
sleep(1);
}
}
printf("Connect to [%s:%d] success.\n", config.ipaddr, config.port);
set_nonblocking(sockfd);
fd_set fds_read, fds_tmp;
int maxfd = -1;
FD_ZERO(&fds_tmp);
FD_ZERO(&fds_read);
FD_SET(uart_dev, &fds_read);
maxfd = uart_dev + 1;
FD_SET(sockfd, &fds_read);
if(sockfd >= maxfd) {
maxfd = sockfd + 1;
}
while(1) {
if(!net_status(sockfd)){
while(1){
if(try_to_reconnect(sockfd, &serv_addr))
break;
sleep(1);
}
}
fds_tmp = fds_read;
if (select(maxfd+1, &fds_tmp, NULL, NULL, NULL) == -1) {
continue;
}
if(FD_ISSET(uart_dev, &fds_tmp)) {
// UART 2 TCP
memset(uartrecv_buf, 0, MAX_COMM_LEN);
int uartrecv_len = data_recv(uart_dev, uartrecv_buf, MAX_COMM_LEN - 1);
if (uartrecv_len > 0) {
int len = data_send(sockfd, uartrecv_buf, uartrecv_len);
if (len != uartrecv_len) {
printf("tcp write data error \n");
continue;
}
}
}
if(FD_ISSET(sockfd, &fds_tmp)) {
// TCP 2 UART
memset(netrecv_buf , 0, MAX_COMM_LEN);
int netrecv_len = data_recv(sockfd, netrecv_buf, MAX_COMM_LEN - 1);
if(netrecv_len > 0) {
int len = data_send(uart_dev, netrecv_buf, netrecv_len);
if(len != netrecv_len) {
printf("uart write data error \n");
continue;
}
}
}
}
close(sockfd);
close(uart_dev);
return 0;
}