被动式Telnet研究及实现(解决内外网远程维护的困难)-part A
欢迎转载。转载请保留原文链接:http://blog.csdn.net/mikulee/article/details/40149779
项目背景及需求:
近期公司有一个项目:
我们在一个arm主机上实现了一些客户需求。有一个web端和一个后台服务,这个主机放在客户的家里。连接上互联网。
arm主机的后台服务负责与我们公司的linuxserver进行通信,比如发送接收web请求等等。
现实总是残酷的,arm主机上总会出现各种问题,我们须要通过telnet对arm主机进行维护。
这通常有2个方案:
方案1.直接去客户家里用telnet进去查看。
方案2.通过port映射。把arm主机的port映射到公网上。
但上面2个方案都是有非常大的缺点:
方案1:路途遥远。我们总不能出一点问题就跑到客户家。打搅到客户吧。
方案2:port映射,仅仅能针对简单网络实现。通常网络环境都十分复杂。非常多都是经过好几级路由才到达arm主机,这时映射困难重重。
并且,假设我们须要维护的不不过几个arm主机,而是非常多非常多,那port映射就太麻烦了,简直是吃力不讨好。
通常的解决方式是:
在arm主机的服务里,定时检查server的状态,假设server有命令发过来,则在client运行该命令。
这个方法也是麻烦多多。要分别在client及服务端即可命令编程。统一协议命令,所支持的命令数量和定义的一致。这个模式是十分繁琐并且可维护性也十分有限。
终于解决方式:
就是在arm主机里,定时查询webserver是否有维护请求,假设有维护请求。则server会把该server的ip和监听的port返回给client。从而arm主机用获得ip和port,先连接本机telnetport23,然后主动发起一个连接到server,假设server端用一个经过改造的telnetclient监听该port。就能建立起一个连接,从而telnet连接建立成功。
接着的事情。就是你想如何就如何了。哈哈,太坏了。
以下開始代码部分:
首先要实现的是arm主机端的port转发程序:
代码中用到的线程池。请參考例如以下文章:传送门
<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)); //把一段内存区的内容所有设置为0
client_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
被动式Telnet研究及实现(解决内外网远程维护的困难)-part A的更多相关文章
- 解决ArcGIS API for Silverlight 加载地图的内外网访问问题
原文:解决ArcGIS API for Silverlight 加载地图的内外网访问问题 先上一个类,如下: public class BaseClass { public static string ...
- route命令详解与使用实例 ,同时访问内外网
route命令详解与使用实例 2011-10-18 12:19:41| 分类: 其他 | 标签:route |字号 订阅 1. 使用背景 需要接入两个网络,一个是部署环境所在内 ...
- win7系统实现内外网同时连接图文教程
解决方案:修改路由表 在工作中,经常会遇到切换内外网的网络情况,通常情况下都是断开/连接网络,很麻烦.我们可以使用route命令来解决此类问题,route add.route delete.route ...
- deepin(debian)中双网卡上内外网的设置方法(通过NetworkManager运行脚本)
国产良心操作系统deepin,界面好看,反应速度快,开箱即用,深度商店里有非常多好用的linux.windows软件,其windows软件通过crossover进行运行,还可以运行一些安卓的apk程序 ...
- Flume消费内外网分流配置的Kafka时遇到的坑
网上有铺天盖地的文章,介绍如何将Kafka同时配置成公网地址.内网地址,以实现内外网分流,看着都很成功. 但我们通过Flume消费一个配置了内外网分流的Kafka(版本0.10.1)集群时遇到了坑,却 ...
- Windows Server 2008 双网卡同时上内外网 不能正常使用
Windows server 2008 32位下,双网卡同时上内外网,并提供VPN服务,遇见的奇怪问题 1.服务器配置 2.网络配置 以太网适配器 内部连接: 连接特定的 DNS 后缀 . . . . ...
- 《搭建DNS内外网的解析服务》RHEL6
首先说下: 搭建的这个dns内外网的解析,是正向解析,反向解析自己根据正向解析把文件颠倒下就ok了 第一步我们先搭建一个DNS的正反向解析(参考上篇DNS正反向解析,这是上篇做过的) 第二部才是搭建内 ...
- 通俗语言解释内外网IP与端口映射
IP:分为外网IP和内网IP 也就是我们说的外网IP属于实体IP 实体IP,它是独一无二的,在网络的世界里,每一部计算机的都有他的位置,一个 IP 就好似一个门牌!例如,你要去百度的网站的话,就要去『 ...
- 一个切换内外网IP地址的批处理BAT
做了一个切换内外网的小脚本.没想到这个老的没剩几颗牙的DOS竟然功能如此强大.盛名之下名副其实啊!不亏是想当年叱咤风云的操作系统啊! 脚本内容1.建立两个TXT文件,分别按行存储内外网的IP,MASK ...
随机推荐
- robotframework自动化系列:新增流程
刚接手项目的时候,要求所有流程在上线之前必须确保正向操作是正确的:这个时候又有新的模块需要测试,所以引入自动化测试是非常有必要的!通过对比,尝试使用RF进行自动化的回归测试. 测试中最常见的操作就是增 ...
- Python+selenium打开网页
东西都安装好了,是不是都迫不及待的想要运行一个程序呢? 不过不幸的是,在正式编程打开网页之前,我们还需要做一件事:下载驱动. 据说,在很久之前的selenium1和2中,驱动是被内嵌在selenium ...
- C# 链接MySql数据库
C# 链接MySql数据库只得注意的几点: 1.C#链接MySql数据库要在网上下载一个mysql-connector-net-6.0.4-noinstall.rar 这里面放的都是一堆dll .将 ...
- 转-Gitorious搭建步骤
先标记一下,后续手动验证 http://blog.csdn.net/king_sundi/article/details/7457475 安装Gitorious Git是一个分布式的版本控制系统,用于 ...
- ssh远程登录,禁止root登录
1,useradd xiaobingpasswd xiaobing (设置密码) 2,禁止root登陆,修改 /etc/ssh/sshd_configPermitRootLogin yes 改为 Pe ...
- python win32 简单操作
源由 刚开始是帮朋友做一个按键精灵操作旺信的脚本,写完后各种不稳定:后来看到python可以操作win32相关的api,恰好这一段时间正在学习python,感觉练手的时候到了~~~ 下载 要注意Pyt ...
- LKD: Chapter 8 Bottom Halves and Deferring Work
In 2.6.x, there are 3 mechanisms for implementing a bottom half: softirqs, tasklets and work queues. ...
- 【Java入门提高篇】Day3 抽象类与接口的比较
抽象类跟接口都讲完了,现在来做一个比较. 其实说实话,没有多大的可比较性,它们是完全不同的两个东西,它们的抽象不在同一个层级上.但是为了让大家更好的理解,还是做一个比较吧,毕竟它们都很抽象(233). ...
- ubuntu下mysql提示Changed limits: max_open_files:1024解决办法
在配置我的md5解密网站cmd5.la的时候,mysql5.7出现了max_open_files: 1024, max_connections: 214,warning: Changed limits ...
- cardview和Palette,ActionBar颜色随图改变
CardView是一个控件,Palette是取色工具(工具类),本文会对他们进行比较细致的介绍,相信机制的各位看完一定轻而易举地实现ActionBar随图改变的特效. 首先看一下效果图: Gradle ...