被动式Telnet研究及实现(解决内外网远程维护的困难)-part A

来源:互联网 发布:赵丽颖 知乎 编辑:程序博客网 时间:2024/06/06 02:37

欢迎转载,转载请保留原文链接:http://blog.csdn.net/mikulee/article/details/40149779


项目背景及需求:

最近公司有一个项目:

我们在一个arm主机上实现了一些客户需求,有一个web端和一个后台服务,这个主机放在客户的家里,连接上互联网。

arm主机的后台服务负责与我们公司的linux服务器进行通信,例如发送接收web请求等等。

现实总是残酷的,arm主机上总会出现各种问题,我们需要通过telnet对arm主机进行维护。

这通常有2个方案:

方案1.直接去客户家里用telnet进去查看。

方案2.通过端口映射,把arm主机的端口映射到公网上。


但上面2个方案都是有很大的缺点:

方案1:路途遥远,我们总不能出一点问题就跑到客户家,打扰到客户吧。

方案2:端口映射,只能针对简单网络实现。通常网络环境都十分复杂,很多都是经过好几级路由才到达arm主机,这时映射困难重重。

而且,如果我们需要维护的不仅仅是几个arm主机,而是很多很多,那端口映射就太麻烦了,简直是吃力不讨好。


通常的解决方案是:

在arm主机的服务里,定时检查服务器的状态,如果服务器有命令发过来,则在客户端执行该命令。

这个方案也是麻烦多多,要分别在客户端及服务端就行命令编程,统一协议命令,所支持的命令数量和定义的一致。这个模式是十分繁琐而且可维护性也十分有限。


最终解决方案:

就是在arm主机里,定时查询web服务器是否有维护请求,如果有维护请求,则服务器会把该服务器的ip和监听的端口返回给客户端,从而arm主机用获得ip和端口,先连接本机telnet端口23,然后主动发起一个连接到服务器,如果服务器端用一个经过改造的telnet客户端监听该端口,就能建立起一个连接,从而telnet连接建立成功。接着的事情,就是你想怎样就怎样了,哈哈,太坏了。


下面开始代码部分:


首先要实现的是arm主机端的端口转发程序:

代码中用到的线程池,请参考如下文章:传送门


<span style="font-size:14px;">/* ============================================================================ Name        : TelnetService.c Author      : xr.lee Version     : Description :  Ansi-style ============================================================================ */#include<sys/types.h>#include<sys/socket.h>#include<sys/time.h>#include<sys/file.h>#include<netinet/in.h>#include<arpa/inet.h>#include <net/if.h>#include<string.h>#include<stdio.h>#include<stdlib.h>#include<netdb.h>#include<fcntl.h>#include<time.h>#include<ctype.h>#include<unistd.h>#include<signal.h>#include<errno.h>#include<sys/wait.h>#include<sys/ipc.h>#include<sys/shm.h>#include <sys/ioctl.h>#include<malloc.h>#include<getopt.h>#include<termios.h>// local echo off/on;#include "thread_pool.h"typedef struct NetConf {char ip[20];int port;} NetConf;int getLocalIp(char *in_name, char *buf) {int socket_fd;struct ifreq ifr;if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {return -1;}strcpy(ifr.ifr_name, in_name);if (ioctl(socket_fd, SIOCGIFADDR, &ifr) < 0) {return -1;}strcpy(buf, inet_ntoa(((struct sockaddr_in *) &(ifr.ifr_addr))->sin_addr));return 0;}//--------------------------------------------------------------------------------// Set echo mode OFF/ON like stty proc;//--------------------------------------------------------------------------------static struct termios stored;static int n_term_change = 0;void echo_off(void) {struct termios new;tcgetattr(0, &stored);memcpy(&new, &stored, sizeof(struct termios));new.c_lflag &= (~ECHO);// echo off ;new.c_lflag &= (~ICANON);// set buffer to 1,new.c_cc[VTIME] = 0;// no time-out ;new.c_cc[VMIN] = 1;tcsetattr(0, TCSANOW, &new);n_term_change = 1;return;}void echo_on(void) {if (n_term_change)tcsetattr(0, TCSANOW, &stored);// restore terminal seeting ;n_term_change = 0;return;}int transfer(int fromfd, int tofd) {int readSize = -1;char buf[1024];while ((readSize = read(fromfd, buf, sizeof buf)) > 0) {if (write(tofd, buf, readSize) < 0) {return -1;}}printf("errno:%d\n", errno);if (readSize < 0 && errno != EAGAIN) {return -1;}if (readSize < 0 && errno == EAGAIN) {return 1;}return readSize;}int createSocketToServer(const char *dstIp, int dstPort) {struct sockaddr_in client_addr;bzero(&client_addr, sizeof(client_addr)); //把一段内存区的内容全部设置为0client_addr.sin_family = AF_INET;    //internet协议族client_addr.sin_addr.s_addr = htons(INADDR_ANY);    //INADDR_ANY表示自动获取本机地址client_addr.sin_port = htons(0); //0表示让系统自动分配一个空闲端口 //创建用于internet的流协议(TCP)socket,用client_socket代表客户机int client_socket = socket(AF_INET, SOCK_STREAM, 0);if (client_socket < 0) {printf("Create Socket Failed!\n");return -1;} //把客户机的socket和客户机的socket地址结构联系起来int fdflags = fcntl(client_socket, F_GETFL, 0);if (fcntl(client_socket, F_SETFL, fdflags | O_NONBLOCK) < 0) {printf("set O_NONBLOCK Error!\n");close(client_socket);return -1;}if (bind(client_socket, (struct sockaddr*) &client_addr,sizeof(client_addr))) {printf("Client Bind Port Failed!\n");close(client_socket);return -1;}  //设置一个socket地址结构server_addr,代表服务器的internet地址, 端口struct sockaddr_in server_addr;bzero(&server_addr, sizeof(server_addr));server_addr.sin_family = AF_INET;if (inet_aton(dstIp, &server_addr.sin_addr) == 0) //服务器的IP地址来自程序的参数{printf("Server IP Address Error!\n");close(client_socket);return -1;}server_addr.sin_port = htons(dstPort);printf("Connecting To %s,%d!\n", dstIp, dstPort);socklen_t server_addr_length = sizeof(server_addr); //向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接int ret = 0;if ((ret = connect(client_socket, (struct sockaddr*) &server_addr,server_addr_length)) < 0) {if (errno != EINPROGRESS) {printf("Can Not Connect To %s,%d!\n", dstIp, dstPort);return -1;}}if (ret == 0)goto done;struct timeval tval;tval.tv_sec = 10;tval.tv_usec = 0;fd_set rset;FD_ZERO(&rset);FD_SET(client_socket, &rset);if ((ret = select(client_socket + 1, NULL, &rset, NULL, &tval)) < 0) {printf("Connect To %s,%d EINTR!\n", dstIp, dstPort);close(client_socket);return -1;}if (ret == 0) {printf("Connect To %s,%d timeout!\n", dstIp, dstPort);close(client_socket);return -1;}int error = 0;int len = sizeof(error);getsockopt(client_socket, SOL_SOCKET, SO_ERROR, (void *) &error, &len);if (error) {fprintf(stderr, "Error in connection() %d - %s/n", error,strerror(error));return -1;}done: return client_socket;}void closeSocket(int socketFd) {if (socketFd != -1) {close(socketFd);}}void *spawnNewConnect(void *arg) {NetConf *conf = (NetConf *) arg;char localip[20];memset(localip, 0, 20);getLocalIp("eth0", localip);if (strlen(localip) > 0 && strlen(conf->ip) > 0) {int socket_server = createSocketToServer("192.168.1.101", 23);int socket_client = createSocketToServer(conf->ip, conf->port);if (socket_server != -1 && socket_client != -1) {int n_select = 0;fd_set n_read_fds;int ret = -1;while (1) {FD_ZERO(&n_read_fds);FD_SET(socket_server, &n_read_fds);FD_SET(socket_client, &n_read_fds);n_select =socket_server > socket_client ?socket_server : socket_client;ret = select(n_select + 1, &n_read_fds, NULL, NULL, NULL);perror("SELECT END\n");if (ret < 0) {perror("Select() error. \n");break;}if (ret == 0) {perror("Select() time out. \n");break;}if (FD_ISSET(socket_server, &n_read_fds)) {perror("FD_ISSET(socket_server, &n_read_fds)");ret = transfer(socket_server, socket_client);if (ret <= 0) {printf("transfer(socket_server,socket_client)%s0 err.\n",ret < 0 ? "<" : "==");break;}}if (FD_ISSET(socket_client, &n_read_fds)) {perror("FD_ISSET(socket_client, &n_read_fds)");ret = transfer(socket_client, socket_server);if (ret <= 0) {printf("transfer(socket_client,socket_server)%s0 err.\n",ret < 0 ? "<" : "==");break;}}}}closeSocket(socket_server);closeSocket(socket_client);socket_server = -1;socket_client = -1;}free(conf);}int main(void) {echo_off();if (tpool_create(3) != 0) {printf("tpool_create failed\n");exit(1);}int sockfd;char ack;struct sockaddr_in server_addr;struct sockaddr_in client_addr;struct GuardDev *dev = NULL;if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {fprintf(stderr, "Socket Error\n");exit(1);}bzero(&server_addr, sizeof(struct sockaddr_in));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_port = htons(10109);if (bind(sockfd, (struct sockaddr *) (&server_addr),sizeof(struct sockaddr)) == -1) {fprintf(stderr, "Bind error\n");exit(1);}if (listen(sockfd, 1) == -1) {fprintf(stderr, "listen error\n");exit(1);}char receive[100];while (1) {int sin_size = sizeof(struct sockaddr_in);sleep(1);int client_fd = -1;if ((client_fd = accept(sockfd, (struct sockaddr *) (&client_addr),&sin_size)) == -1) {fprintf(stderr, "Accrpt error\n");exit(1);}printf("Server get connection from %s,clientfd=%d\n",(unsigned char *) inet_ntoa(client_addr.sin_addr), client_fd);memset(receive, 0, 100);int ret = recv(client_fd, receive, 64, 0);if (ret < 0) {printf("receive error: %s", strerror(errno));close(client_fd);continue;}if (strlen(receive) > 0) {char ip[20], port[6];memset(ip, 0, 20);memset(port, 0, 6);sscanf(receive, "ip=%[^&]&port=%[^&]", ip, port);NetConf *conf = (NetConf *) malloc(sizeof(NetConf));memset(conf, 0, sizeof(NetConf));strcpy(conf->ip, ip);conf->port = atoi(port);tpool_add_work(spawnNewConnect, (void*) conf);}close(client_fd);}tpool_destroy();echo_on();return EXIT_SUCCESS;}</span>


编译后,做为开机启动项,添加到arm主机上。


下一篇文章将提供服务端实现及使用方法,源码也一起附上

http://blog.csdn.net/mikulee/article/details/40150791



0 0
原创粉丝点击