串口服务器是为RS-232/485/422串口到TCP/IP网络之间完成数据转换的通讯接口转换器。提供RS-232/485/422终端串口与TCP/IP网络的数据双向透明传输,提供串口转网络功能,RS-232/485/422转网络的解决方案,可以让串口设备立即联接网络。
MOXA工业级串口服务器
应用领域
门禁系统、考勤系统、售饭系统、 POS 系统、楼宇自控系统、自助银行系统电信机房监控,电力监控等。
产品特点
·体积小,火柴盒大小
·支持RS232,RS485
·10/100M以太网口 ,软件可升级
·一个开关量输入,一个开关量输出
·可作为TCP Server 或TCP Client ,UDP数据
·内置WEB服务器,支持Java
·支持多种异步串口格式
·无需修改原有应用软件就可在网络环境下使用
·将COM口定向至IP地址,每台计算机可同时拥有256个串口
工作方式
服务器方式:在该工作方式下,串口联网服务器作为TCP服务器端, 转换器在指定的TCP端口上监听平台程序的连接请求,该方式比较适合于一个转换器与多个平台程序建立连接(一个转换器不能同时与多个平台程序建立连接)。
客户端方式:在该工作方式下,串口联网服务器 作为 TCP 客户端,转换器上电时主动向平台程序请求连接,该方式比较适合于多个转换器同时向一个平台程序建立连接。
通讯模式
点对点通讯模式:该模式下,转换器成对的使用,一个作为服务器端,一个作为客户端,两者之间建立连接,实现数据的双向透明传输。该模式适用于将两个串口设备之间的总线连接改造为 TCP/IP 网络连接。
使用虚拟串口通讯模式:该模式下,一个或者多个转换器与一台电脑建立连接,实现数据的双向透明传输。由电脑上的虚拟串口软件管理下面的转换器,可以实现一个虚拟串口对应多个转换器, N 个虚拟串口对应 M 个转换器( N<=M )。该模式适用于串口设备由电脑控制的 485 总线或者 232 设备连接。
基于网络通讯模式: 该模式下,电脑上的应用程序基于SOCKET 协议编写了通讯程序,在转换器设置上直接选择支持 SOCKET 协议即可。
实现原理
ARM开发板
一般的开发板都可使用,只要带有网口(有线/无线)、串口。
开发板串口服务端源码
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <termios.h> /*PPSIX终端控制定义*/
#include <errno.h> /*错误号定义*/
#define COM_FILE "/dev/ttySAC0" //串口设备文件
#define PORT 8848
#define BACKLOG 5
#define MAXDATASIZE 128
#define BUFSIZE 512
void* net_proc(void* arg);//网络侦听服务
void* net_recv(void* arg);//接收网络客户端数据
void* net_send(void* arg);//发送数据到网络客户端数据
void* com_recv(void* arg);//串口接收处理
void* com_send(void* arg);//串口发送处理
int init_com(void);//初始化串口参数
struct ARG {
int connfd;
struct sockaddr_in client;
};
static char net_buff[BUFSIZE];
static char com_buff[BUFSIZE];
int wr_index = 0;
int re_index = 0;
/**********************************
主函数
**********************************/
int main(void)
{
int fd = 0;
pthread_t net_th, com_r,com_s;
memset(net_buff,0,BUFSIZE);
memset(com_buff,0,BUFSIZE);
if((fd = init_com())<=0){
perror("init_com() error");
exit(1);
}
//网络服务器线程
if(pthread_create(&net_th, NULL, net_proc, NULL)){
perror("pthread_creat() error");
exit(1);
}
//串口接收线程
if(pthread_create(&com_r, NULL, com_recv, (void*)&fd)){
perror("pthread_creat() error");
exit(1);
}
//串口发送线程
if(pthread_create(&com_s, NULL, com_send, (void*)&fd)){
perror("pthread_creat() error");
exit(1);
}
pthread_join(net_th,NULL);
pthread_join(com_r,NULL);
pthread_join(com_s,NULL);
}
/**********************************
网络服务端线程
**********************************/
void* net_proc(void* arg)
{
int listenfd, connectfd;
pthread_t thread; //id of thread
struct ARG *acc_arg;
struct sockaddr_in server; //server's address info
struct sockaddr_in client; //client's
int sin_size;
// int opt = SO_REUSEADDR;
//create tcp socket
printf("socket.... ");
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("creating socket failed.");
exit(1);
}
// setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&server,sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = htonl(INADDR_ANY);
printf("bind.... ");
if(bind(listenfd,(struct sockaddr *)&server,sizeof(struct sockaddr)) == -1){
perror("bind error.");
exit(1);
}
printf("listen.... ");
if(listen(listenfd,BACKLOG) == -1) {
perror("listen() error ");
exit(1);
}
sin_size = sizeof(struct sockaddr_in);
sleep(1);
while(1){
printf("accepting....\n ");
if((connectfd = accept(listenfd,(struct sockaddr *)&client,(socklen_t*)&sin_size)) == -1) {
perror("accept() error ");
exit(1);
}
acc_arg = malloc(sizeof(struct ARG));
acc_arg->connfd = connectfd;
memcpy((void *)&acc_arg->client, &client, sizeof(client));
// 服务端接收客户端数据
if(pthread_create(&thread, NULL, net_recv, (void*)acc_arg)){
perror("pthread_creat() error");
exit(1);
}
// 服务端向客户端发送数据
if(pthread_create(&thread, NULL, net_send, (void*)acc_arg)){
perror("pthread_creat() error");
exit(1);
}
}
close(listenfd);
return (0);
}
/**********************************
网络数据接收线程
**********************************/
void* net_recv(void* arg)
{
int connectfd;
int nbyte;
char recvbuf[MAXDATASIZE];
struct sockaddr_in client;
struct ARG * n_arg = arg;
connectfd = n_arg->connfd;
client = n_arg->client;
printf("IP: %s. \n",inet_ntoa(client.sin_addr) );
//get client's name from client
while(1){
memset(recvbuf,0,MAXDATASIZE);
nbyte = recv(connectfd, recvbuf, MAXDATASIZE, 0);
if(nbyte== 0 || nbyte == -1) {
close(connectfd);
perror("Client disconnected. ");
break;
}
recvbuf[nbyte] = '\0';
printf("fd=%d nbyte=%d net recv: %s \n", connectfd,nbyte,recvbuf );
// send(connectfd, recvbuf, strlen(recvbuf), 0); //回传到客户端
//网络收到的数据,存到全局缓冲区
strcat(net_buff,recvbuf);
}
}
/**********************************
网络数据发送线程
**********************************/
void* net_send(void* arg)
{
int connectfd;
int nbyte;
struct ARG * n_arg = arg;
connectfd = n_arg->connfd;
while(1){
//发送串口收到的数据到网络客户端
if(strlen(com_buff)>0)
{
nbyte = send(connectfd, com_buff, strlen(com_buff), 0);
printf("net send nbyte=%d\n",nbyte);
memset(com_buff, '\0', BUFSIZE);
}
}
}
/**********************************
串口初始化
**********************************/
int init_com(void)
{
int fd_com;
struct termios initial_settings, new_settings;
fd_com = open(COM_FILE, O_RDWR | O_NOCTTY );
if(-1 == fd_com)
{
perror("Can't Open Serial Port");
return -1;
}
//获取终端属性
tcgetattr(fd_com,&initial_settings);
new_settings = initial_settings;
//设置速度
cfsetispeed(&new_settings, B115200);//波特率115200
cfsetospeed(&new_settings, B115200);
/*控制模式设置*/
// new_settings.c_cflag &= ~CSIZE; //数据位8
new_settings.c_cflag |= CS8; //数据位8
new_settings.c_cflag &= ~CSTOPB; //停止位1
new_settings.c_cflag &= ~PARENB; //无校验
/*输入模式设置*/
new_settings.c_iflag &= ~INPCK; //取消校验
new_settings.c_iflag |= IGNPAR; //忽略奇偶校验错误的字符
new_settings.c_iflag |= ICRNL; //收到的回车符转成换行符
/*输出模式设置*/
new_settings.c_oflag = 0;
/*本地模式设置*/
new_settings.c_lflag &= ~ICANON; //启用非标准输入
// new_settings.c_lflag &= ~ECHO; //关闭回显功能
new_settings.c_lflag &= ~ISIG; //禁用信号
/*控制字符设置*/
new_settings.c_cc[VMIN] = 1; //有字符立即读取
new_settings.c_cc[VTIME] = 0;
//启用新的终端设置
if(tcsetattr(fd_com, TCSANOW, &new_settings) != 0) {
fprintf(stderr,"could not set attributes\n");
}
// tcsetattr(fd_com,TCSANOW,&initial_settings); //恢复原有设置
return fd_com;
}
/**********************************
串口数据发送
**********************************/
void* com_send(void* arg)
{
int fd;
int nbyte;
fd = *(int*)arg;
while(1)
{
if(strlen(net_buff)>0)
{
nbyte = write(fd,net_buff,strlen(net_buff));
printf("com send nbyte=%d\n",nbyte);
memset(net_buff, '\0', sizeof(net_buff));
}
}
}
/**********************************
串口数据接收
**********************************/
void* com_recv(void* arg)
{
int fd;
int nbyte = 0;
fd = *(int*)arg;
char recvbuf[MAXDATASIZE];
while(1)
{
//读取串口数据到缓冲区
memset(recvbuf,'\0',MAXDATASIZE);
nbyte = 0;
nbyte = read(fd,recvbuf,MAXDATASIZE);
recvbuf[nbyte] = '\0';
printf("nbyte=%d com recv:%s\n",nbyte,recvbuf);
strcat(com_buff,recvbuf);
}
}
PC客户端源程序
/* cthread.c */
#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>
#define PORT 8848
#define MAXDATASIZE 128
void* net_recv(void* arg);
void* net_send(void* arg);
int get_line(char *msg);
int init_net(int argc, char *argv[]);
/**********************************
主函数
**********************************/
int main(int argc, char *argv[])
{
int net_fd;
pthread_t recv_th,send_th;
net_fd = init_net(argc,argv);
pthread_create(&send_th,NULL,net_send,(void*)&net_fd);
pthread_create(&recv_th,NULL,net_recv,(void*)&net_fd);
pthread_join(recv_th,NULL);
pthread_join(send_th,NULL);
return 0;
}
/**********************************
网络接收线程
**********************************/
void* net_recv(void* arg)
{
char recvline[MAXDATASIZE];
int *sockfd;
int numbytes;
sockfd = (int*)arg;
while(1){
printf("@\n");
if((numbytes = recv(*sockfd, recvline, MAXDATASIZE, 0)) == 0){
printf("serial not data. \n");
}
//recevie message form server echo
recvline[numbytes] = '\0';
printf("[com data] %s\n", recvline);
//clean output buffer
memset(recvline, '0', strlen(recvline)-1);
}
}
/**********************************
网络初始化,连接到服务端
**********************************/
int init_net(int argc, char *argv[])
{
int fd;
struct hostent *he;
struct sockaddr_in server; //server's address info
char *defaultIP = "127.0.0.1";
if(argc != 2) {
if((he = gethostbyname(defaultIP)) == NULL){
perror("gethostbyname() error");
exit(1);
}
}else{
if((he = gethostbyname(argv[1])) == NULL){
perror("gethostbyname() error");
exit(1);
}
}
if((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket() error");
exit(1);
}
bzero(&server , sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr = *((struct in_addr *)he->h_addr);
if(connect(fd, (struct sockaddr *)&server,sizeof(struct sockaddr)) == -1){
perror("connect() error");
exit(1);
}
return fd;
}
/**********************************
网络发送数据线程
**********************************/
void* net_send(void* arg)
{
char sendline[MAXDATASIZE],recvline[MAXDATASIZE];
int numbytes;
int len;
int * sock_fd;
sock_fd = (int*)arg;
printf("connected to server. \n");
//send message to server
//when the string is not NULL , send another!
while(1){
while((len =get_line(sendline)) != 0){
sendline[len] = '\0';
send(*sock_fd, sendline, strlen(sendline), 0);
}
}
}
/**********************************
接收命令行输入的数据
**********************************/
int get_line(char *msg)
{
int i=0;
char temp;
printf("Input message:");
fflush(stdout);
while (1) {
temp = getchar();
if (temp == '\r' || temp == '\n') {return i ;}
msg[i]=temp;
if(msg[i]==13){
msg[i]=0;
break;
}
fflush(stdout);
i++;
}
}