为了避免大家说我是标题党,先说明背景。前段时间,领导和我说了一个项目,需要开发一些无线设备。要求:
问题就来了,在公司已有的资源下,是没有哪一款能够达到以上的性能。我很肯定的和领导说:“我们的设备性能达不到。如果要做的话,短期内做不出来的”。但是领导可不管,就说:“小谢,你想想办法,功能简单点也没关系。这个项目利润不错,你好好干,年底少不了奖金的”。听到奖金二字,我就有点激动,回复道:好好,我试试。
时间短,重新画板子开发肯定不行了。于是我想到了包装方案。所谓的包装方案,就是将别的公司的产品改为自己的产品,进行出售。ODM就是这样的生产方式。但是ODM的需要投入的资金也比较多,我们自己有软件团队,为什么将软件开发费给别人呢?于是我想到了一个类似ODM的方式:
选择能够满足性能要求的设备M,再找一个千兆带宽的ARM板 L,利用L设备 telent到M设备上,进行参数设置:SSID,PASSWORD,Mode等基本参数。通过访问L设备进行参数设置,当然,L设备上的web页面都是本公司的页面。
有了想法之后,就在网上找了一些设备,验证了能够telnet登陆并修改参数。并将这个方案和领导说了一下,领导听了之后,感觉能够满足客户需求,并且能够节约成本,并且能够在规定时间内完成,便愉快的答应了。通过一个多月的开发和调试,最终项目得以完美交付。
ps:可能有人会有疑惑,为什么客户不找其它厂家,非要找你们,并且还贵。那是因为我们公司销售厉害啊-。-
其实在我们工作中,我相信遇到相似的情况并不少。希望能够给遇到朋友一些思路和建议。说不定就会有柳暗花明又一村的醒悟。
以下内容主要分享一下,在开发过程中遇到的一些问题或思路。希望给有相同想法的小伙伴一些帮助。
在这个方案中,L设备和M设备的通信都是通过telent作为桥梁,因此,它的输入输出极为重要。
方案一:刚开始我通过shell的输入输出重定向到文件中
通过调试一段时间后,发现不可取。总会遇到这样或那样的问题,可能是因为我对shell的标准输出和标准输入理解不够充分。
方案二:修改telnet.c源码
我们知道telent的输入输出都是终端。因此我修改了源码,将输入和输出分别通过read /tmp/input
和write /tmp/output
文件实现。在功能实现上,我们只需要将设置参数的命令写入 tmp/input
,获取设备参数从tmp/output
中获取即可。这里发一下初版修改内容:
int telnet_main(int argc UNUSED_PARAM, char **argv)
{
char *host;
int port;
int len;
struct pollfd ufds[2];
INIT_G();
#if ENABLE_FEATURE_TELNET_WIDTH
get_terminal_width_height(0, &G.win_width, &G.win_height);
#endif
#if ENABLE_FEATURE_TELNET_TTYPE
G.ttype = getenv("TERM");
#endif
if (tcgetattr(0, &G.termios_def) >= 0) {
G.do_termios = 1;
G.termios_raw = G.termios_def;
cfmakeraw(&G.termios_raw);
}
#if ENABLE_FEATURE_TELNET_AUTOLOGIN
if (1 == getopt32(argv, "al:", &G.autologin)) {
/* Only -a without -l USER picks $USER from envvar */
G.autologin = getenv("USER");
}
argv += optind;
#else
argv++;
#endif
if (!*argv)
bb_show_usage();
host = *argv++;
port = bb_lookup_port(*argv ? *argv++ : "telnet", "tcp", 23);
if (*argv) /* extra params?? */
bb_show_usage();
xmove_fd(create_and_connect_stream_or_die(host, port), netfd);
setsockopt_keepalive(netfd);
signal(SIGINT, record_signo);
#if 0
ufds[0].fd = STDIN_FILENO;
ufds[0].events = POLLIN;
ufds[1].fd = netfd;
ufds[1].events = POLLIN;
#else
ufds[1].fd = STDIN_FILENO;
ufds[1].events = POLLIN;
ufds[0].fd = netfd;
ufds[0].events = POLLIN;
int fdin = open("/tmp/input",O_RDWR|O_CREAT);
int fdout = open("/tmp/output",O_RDWR|O_CREAT);
#endif
while (1) {
if (poll(ufds, 1, 500) < 0) {
/* error, ignore and/or log something, bay go to loop */
if (bb_got_signal)
con_escape();
else
sleep(1);
continue;
}
// FIXME: reads can block. Need full bidirectional buffering.
#if 0
if (ufds[0].revents) {
len = safe_read(STDIN_FILENO, G.buf, DATABUFSIZE);
if (len <= 0)
doexit(EXIT_SUCCESS);
TRACE(0, ("Read con: %d\n", len));
handle_net_output(len);
}
#else
len = safe_read(fdin,G.buf,DATABUFSIZE);
if(len < 0)
doexit(EXIT_SUCCESS);
else if(len == 0)
{
//sleep(1);
}
else
{
handle_net_output(len);
}
#endif
if (ufds[0].revents) {
len = safe_read(netfd, G.buf, DATABUFSIZE);
if (len <= 0) {
full_write1_str("Connection closed by foreign host\r\n");
doexit(EXIT_FAILURE);
}
TRACE(0, ("Read netfd (%d): %d\n", netfd, len));
handle_net_input(fdout,len);
}
} /* while (1) */
}
很明显文本的内容对于我们而言就是乱码,但是终端能够实现对这些ansi转义序列进行解释,故能过优美的解释。但是对于我们web端,它的属性时text,是不能进行解释的,显示出来的就是乱码。
方案一: 让web也能够识别转义序列
这种解决方案是最好的,我尝试使用比较火的xterm.js插件,的确可以识别部分的转义序列,比如可以识别字体颜色。但是还是有部分不能识别,会造成部分乱码。由于我对前端也不是很了解,所以我也没有深究
方案二:过滤文本中的转义序列
这个解决方案也是无奈之举,经过好几天的纠结还是选择了这个方式。通过二进制查看文本内容,发现了一些规律,就写了一个过滤demo。没想到效果还不错,就选择的这一方案。
下面为过滤demo:
#include
#include
#include
#define MAXLEN 1024*8
int main()
{
char *input = "./output";
char buff[MAXLEN] = {0};
char out[MAXLEN] = {0};
int fd = open(input,O_RDWR);
if(fd < 0 )
printf("open output is error");
int len = read(fd,buff,MAXLEN);
char* p = buff;
int i = 0;
while (len--)
{
if(*p == 0x00 || *p == 0x1b)
{
p++;
}
else if(*p == '[' && *(p-1) == 0x1b)
{
while(1)
{
if(*p == 'm' || *p=='K')
{
p++;
break;
}
else
p++;
}
}
else if(*p == 0x0d && *(p+1) == '[')
{
p++;
while(1)
{
if(*p == 0x0d)
{
p++;
break;
}
else
p++;
}
}
else if(*p == 0x0d)
{
p++;
}
else
{
out[i++]=*p;
p++;
}
}
write(fd,out,i);
close(fd);
return 0;
以上便是项目中可能比较麻烦的地方,其它的工作基本没有什么难度。也正因为难度不大,所以不失为一个好方案啊,希望能够给你带来帮助。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
在之前的web显示中,我曾尝试使用xterm.js来实现,但由于了解不深,遇到问题就没有继续。采用了textarea
,但事实证明偷懒的效果就是丑,并且效果使用也不方便。咱也不多说,直接上图。
这样看起是不是觉得很low?并且使用起来很麻烦,你需要在上面的input框中输入命令,再点击submit才能正常交互。要是一个前端工作者看到,肯定要把我凌迟。(太不人性化了)。
这两天没啥事(主要是因为我的效率比较高,哈哈),再捣鼓捣鼓一下xterm.js,通过查看官网的文档,进行修改,也就能正常使用了,并且使用效果还不错。(遇到困难还是要坚持一下呢)
上图:
看起来肯定比上面的要简洁,并且使用起来更加人性化。主要修改的代码就是htm文件,后台没有修改。仅供参考:
<%#
Copyright 2008 Steven Barth
Copyright 2008-2015 Jo-Philipp Wich
Licensed to the public under the Apache License 2.0.
-%>
<%+header%>
Telnet
This is a web telnet.
<%+footer%>