地图导航

导航软件作为我们技术进步可夸耀的成果,似乎给我们的生活提供了巨大的便利。我并没有怎么用过,在仅有的几次使用感受来看,目前还有很多问题。

走高速导航并不算方便,因为高速是主干道,可以在地图上清楚的看到遍布全国的高速网络,平时走也就简单的几条,一般走的比较远,先看地图规划下,在路上的路牌标志也很明显,很远之前就提示前面哪里转可以到哪里,有足够的反映思考时间。

但是在各种大路小道密集的城市中,去一个自己不太知道的地方,就很麻烦了,看地图要半天,而且因为距离短,路边标志等看清楚的时候,就已经错过了,所以可以看到分叉路口的白线上经常会有车停下来在那里看路标。这种情况下,用一下导航也是很方便的。

依赖导航有一个很不好的后果,就是自己很少主动思考,就像被人牵着鼻子走的,必须严格按照描述的方法前进,这种感觉让人很不喜欢。

地图数据不及时更新的话,很多道路信息就是错误的,把人引导到错误的道路上,浪费很多时间。特别是中国现在城市更新速度很快,地图数据,一定要跟得上才行。我就遇到过一次这种情况,当时急着赶路,就开了下导航试试,结果绕了一大圈,后来在路边买的纸质地图上,发现跟导航软件上的路是不一样的。

道路计算的方法上,也有很多问题,很多时候找到的路径并不是最优的,这个不太好解决。不过可以人为的给出一些提示信息,比如只走高速,或者避开小路,或者最近距离。最重要的还是道路信息的完善,每条路是否经常堵车,或者某个地方到某个地方,走哪条路会近一些,这些本地人比较清楚,要是通过某种方式,能够让司机集体参与更新地图,就会好很多。

我以前喜欢骑自行车,也呆了好几年,对深圳的大小道路基本了如指掌,所以平时根本没怎么借助过导航软件,出去玩的时候,基本都是看地图,到城市以后,也是随意的开,到处走走,毕竟是玩,没有急着去特定地点的需求。在仅有的几次需要使用场景中,印象一点都不好,除了添麻烦,没有带来什么便利。有一次在防城港快没有油了,着急加油,就打开找一个加油站,绕过各种小路土路,来到一个小加油站边,却发现没人营业。最后还是顺着大路一直往前走,看到了个加油站。

我有段时间曾经仔细的思考过地图导航软件如何的实现,感觉虽然不算复杂,但也不算简单。

首先是地图数据的表示,如何表示一条道路?现实世界的道路弯弯曲曲,需要把这些细节都记录下来,这意味着需要每隔一小段距离就记录一下,可以每隔五米十米,记录下这条道路的中心位置的经度纬度,以及道路的宽度,或者直接记录道路两边的经纬度,这样宽度就可以计算出来。这样每一条道路都是一个位置点的数组,然后再有一个结构描述道路的各种信息,名字,长度,限速,摄像头,等等。

另外需要单独记录道路结构的拓扑信息,交叉路口,某条道路跟另一条道路在那里相连,距离多少,这些拓扑信息,构成了一个有向图,这个图上进行广度优先搜索,或者使用dijkstra最短路径算法,就能计算出来某个位置到某个位置的最短路径了。这跟游戏里的自动寻路算法基本没多大出入。

地图上的位置景点这些可以单独出来,建一个位置列表,某某小区,某某学校,某某酒店等,这些位置点记录下坐标位置,在道路拓扑信息中也要加入他们的连接结构。

查询的时候,首先输入终点,从位置列表中查询出来这个点的坐标,然后从道路拓扑图中找到相应的节点,通过GPS得到当前的位置坐标,查询到相应的道路位置节点,然后运行寻路算法计算出来最短路径。

对于巨大的地图信息,一定要分层优化才行,可以按找地理区域划分为不同层次,或者把地图切割成若干级别的小块,每个小块下面又有更加细分的深层次小块列表。

在mmog游戏中寻路,一般是一个个小的场景地图为级别的,如果跨地图,或者跨主城,另外有单独的这些大区域连接信息,寻路的时候就是二层,先看下是否是跨主城,如果是的话,先寻找主城间的道路,然后在每个小的场景地图上单独寻路。

导航软件也是一样,比如国家的道路信息网单独列出来构成一个拓扑信息,,如果目的地跨度比较大的话,比如深圳某某地方到上海某某地方的道路,可以先在一个大的层次上,找到连接这两个城市的高速公路信息,各自的入口点,然后在城市级别,寻找当前位置到高速入口的道路。

另外的提示信息,像当前道路限速多少,前方多少米有摄像头,则是在道路描述信息的结构列表中读出来根据当前位置相应提示。这些信息也可以单独记录,记录下属于那个道路,执行的时候查询加载出来就行。

语音识别和语音合成播报,则是另外专门的技术了,有很多现成的库和模块可以使用。

如果实现一个导航软件,并不只是我们看到的这一部分,另外还要实现一套道路信息的采集和编辑系统,这个就先不讨论了吧。

其实真的实现这样一个软件,很多细节都是要考虑的,需要花很多时间精力才行,国内的这些做导航软件的团队,都是至少几十人上百人,不光技术,产品,UI,运营销售,数据采集等都需要一些人。

导航软件的未来究竟走向何方,似乎很难说,大致看来,可能跟生活结合的更紧密一些,包括周边各种吃喝玩乐景点,以及提供生活服务的设施场所结合起来,总的来说应该是个地图综合类应用,导航只是其中的一个小部分。

车载导航,长远看来淘汰的命运比较大一些,不同汽车厂商提供不同的服务,但是用户体验做的都很一般,毕竟汽车行业不是专注做IT的,而且各自为政,竞争压力以及更新速度上都比较慢。手机上的应用反而更好一些,最起码更新速度很快。不管是地图数据,还是应用体验,都在不停的发新版本。

如果在汽车上加一个中控台,类似于iphone的那种可以放到的外置音响上的底座,汽车中间的显示屏通过某个协议和接口跟手机通信,可以在上面显示信息,这样就可以直接用智能手机进行导航听音乐之类的了,没有必要单独实现一套,这个系统就是提供显示屏和音响,软件方面交给手机,手机放上去也可以充电,再制定一套基本的通信协议,比如方向盘上的按钮输入,显示屏亮度和声音大小的调节等,也没有必要像现在单独使用蓝牙接口来接听电话,手机放上去之后,车载的这些系统就是输入输出,而手机则统一控制,通过底座连接通信,某些应用甚至可以通过获取汽车的运行状态信息,生成报表统计。

这样汽车厂商就解放出来,只用专心做汽车机械部分就行了,这些上层的部分,还是交给IT公司来做好一些,IT公司对于新想法新事物接受和实现速度都快很多,而且独立出来以后,可以随时更新升级,产品体验也会好很多,不同的软件产品可以根据喜好换着使用。某些用户可能觉得林志玲语音版的导航软件听起来不错,另一些用户希望通过语音识别发送指令,还有的用户想要有旅程的各种距离,油耗,时间和和路径记录等数据统计,那直接装上相应app就可以了,app要是不好用,还有其他厂商的可以替代,这是一个完全开放竞争化的环境。

医疗

因为运动过多,左腿膝关节有了点小问题,几次来回往医院跑,最后确定,是半月板轻度损伤

第一次去,医生听我描述过轻微疼痛的情况后,用手摁了几下膝盖,说是关节软骨磨损,开了点药。回来仔细看了看关节的结构以及各种易发的疾病,后来有段时间不怎么痛了,就没太注意了

后来又去了一个专门的骨科医院,医生让验了下血,说尿酸偏高又可能是痛风,开了一堆药,其实我感觉他很不靠谱,也没吃药。

前几天仔细观察,左腿膝关节似乎有点积液,腿伸直的时候比右边大了一些。又去了趟南山医院,医生了解了以往的各种情况和症状以及就诊记录之后,说做个MRI检查下吧。于是就做了下磁共振检查,这次检测出来的结果,比较可靠一些,报告上说,“左侧膝关节外侧半月板前角内可见小片状稍高信号影”,医生说先好好休息休息,尽量避免爬山,上楼之类的运动,开了一二十盒硫酸氨基葡萄糖。

关节软骨由于没有血管,只通过关节液来输送营养物质,所以很难再生。不过好像说纯天然锯峰齿鲛软骨粉可以促进关节软骨再生,希望如此吧,如果后面的医学发展可以搞清楚具体的机制并合成相应的药物,很多疾病都能解决。

通过观察了解,我感觉门诊医生这个角色职位,完全可以用机器自动化起来,我们看病,无非是听病人描述一些那里疼痛,什么症状之类的,然后把问题范围缩小,最后使用一些检测仪器和手段,比如验血,X光,CT之类的,检测各种参数结构,最后得出来结论。

如果编写一个程序,通过问答式的交互输入,比如,问病人那里不舒服,病人输入头疼咳嗽之类的症状描述,这个程序就通过内部建好的数据来查询相应符合症状的各种问题,然后继续提示问其他症状,一一排除不必要的问题,直到最后剩下一两个,就可以给出结果,是感冒发烧之类的。如果有歧义,有好几个选择,则提示各种可能,并给出相应的检查选项,比如建议去做一下CT,然后提交报告,程序根据检测报告再进行相应的判断

这个系统可以通过全世界各地的医学专家不断的完善,不停的在数据库里加入新的描述,并修改完善已有的描述,这样专业度就越来越高,即使不能完全取代门诊医生,也可以作为医生诊断的重要辅助。

其实门诊医生是个很尴尬的职位,如果一个医生经验丰富,那么他就会处于很重要的位置,很难去坐诊挨个处理挂号病人,当然我们现在有挂专家号,就是为他们准备的,但这种资源毕竟比较少,大部分门诊医生良莠不齐,技术水平也参差有别,加上病人很多,各种问题就容易滋生,现在的医患关系紧张,也是多方面因素都有的。

人体也只是一个构造十分微妙的机器,如果能够精确检测这个机器的每一个状态,那么就能给出各种疾病和问题的可能。其实人体内部有各种各样的感应传感器的,这就是我们的疼痛系统,某个位置疼痛,就说明功能出现了异常,具体的情况,就要通过其他方式检测了,现在我们医学常规的手段,检查下外观,验血,然后就是各种成像技术,内窥镜,超声波,X光,磁共振等,这些手段检测出具体部位的内部构造结构发生的改变,确定问题所在。

现在可穿戴医疗设备炒的很热,通过传感器实时检测血压,心率等身体运行参数,统计报表分析,得出各种参考结果。

毕竟我们能够检测的参数有限,所以这些设备的功能也受到限制,但大体思路是可以看清楚的,如果能够出现更多的便携传感器,那么这些设备的功能也会变得更强大。

如果有一天我们的科技得到突破,可以用一个很小的便携设备检测身体的任意断面结构,那基本上医院就可以不用去了,简单疾病只需要通过程序测试,复杂一点的可以上传各种检测图片到某些网络医疗结构,这些机构的医生坐在电脑前跟程序员一样上班,检查那些电脑无法处理的结果,得出结论后直接通过医疗机构的网站系统,生成诊断报告,邮件通知,这样医院就成为一个做手术的地方了,只有对人体进行操作才会过去。

现实一点说,前面提到那个医疗程序,是完全可以用现在的技术实现的,在服务器端存贮各种数据信息,用web页面进行简单诊断,或者做成一个手机应用,病人只用简单的交互选择回答一些问题,甚至就是在给出的可能选项中进行选择,就像现在的某些性格测试的小游戏一样,这样就不用到医院人山人海的挂号排队了,甚至还可以保证诊断的可靠性。

早期的人工智能研究者实现过一些类似的专家系统,比如MYCIN,现在也有一些类似的诊断辅助系统,可惜都没有做大,一方面是技术原因吧,我觉得更多的是社会和经济方面的限制。如果在这个领域投入大量的金钱和人力,大量医生思想转变并进行尝试,这个领域应该大有可为。

Galileo

今天到make faire去看了看,还不错。

如果真正评价一下专业性质的话,只能说大部分东西看起来有些好玩,毕竟创客是个业余的行为,很多东西看一眼就能估算出来背后的工作量,基本比入门阶段走不了多远。但是也有一些专业的公司参加,比如DJI,应该是借机会宣传下他们的产品。

意外的收获是,发现了intel的Galileo。当时看到一个哥们做的监控植物生长温湿度并调节风扇和供水的东西,他的笔记本上在打出来收集的数据信息log,我惊讶的发现他是用putty直接ssh到开发板的,跟他聊了聊,了解到了Galileo的一些信息。

回来之后便仔细的找了找Galileo相关的资料,并且买了一块开发板,这里有一篇介绍Galileo的很好的文章。

仔细的看下可以发现,Galileo就是一个标准的PC系统,EFI BIOS,USB,网卡,PCI Express等PC接口和组件一应俱全。

Galileo的引脚和Arduino保持兼容,这意味着一大堆的shields可以直接拿过来使用。

Galileo提供的是一个完整的开放PC平台,包括一个x86指令集的400MHz CPU,提供256M内存,通过SD卡实现存储。

个人感觉,intel推出来Galileo是一个很正确的选择,市场定位很清晰,应该很快就会流行起来。

不同能力的硬件,可以用来做不同的事情,而不同的开发者团体,则意味着什么样的东西会被开发出来。

我上学的时候对硬件感兴趣过,可以通过几个例子对比说明下。

大一的时候有段时间玩8051,在面包板上接个LED闪几下,拼几个汉字显示,基本都是这些简单的东西。

大三的时候一个电气学院的同学,找我一起做个ARM平台的东西,用的芯片是LPC2148,当时还没有安卓,按照我们的需求,要往SD卡上记录一些数据,我就尝试移植文件系统上去,读了一大堆SD卡时序接口之类的文档,还有FAT文件系统的结构,好像成功的写入过几次数据,不过后来都以失败告终。最让人头疼的是,在调试器直接跟踪汇编代码都无法知道具体发生了什么,我记得好像有个本来是3.3v电压的地方我们电路版上直接接到了5v。反正最后没有做出来,不了了之。

毕业后有次跟他聊起这件事,觉得当时还是经验不足,甚至电路都没有完全调通。这时候他在工作中已经很娴熟的往SD卡上记录数据了。

当时主要的精力都花在了不相干的底层细节上了。

arduino就是当初8051的替代,用的avr的基本同档次cpu,简化了入门的复杂度,提供了一个很好的扩展性平台。

Galileo则解决了后来arm上的那个问题,不过现在arm也很成熟了,随便买个开发板就能烧个安卓上去,能搞的很专业。

arduino和galileo做的事情,就像前面说的用51和arm之间做的事情的区别一样,一个是简单应用,另一个则规模宏大。

至少galileo让我觉得很方便,不用担心底层和电路相关的东西,集中精力实现应用就行了,基本就是个开放的PC平台,极度程序员友好。

arduino面向的用户其实很尴尬,真正的电子工程师很少会直接使用arduino制作产品,可能偶尔会在上面实现几个原型设计,真正的产品,还是会自己绘制电路,使用最具有性价比的单片机来做出来产品。他们毕竟对各种芯片和电路很熟悉了,知道需要的是什么以及该用什么来实现。

武断的说,arduino是用来玩的,不是用来做真正应用,它的主要用户,不是电子工程师而是感兴趣的普通爱好者,有一个好玩的东西来实现他们一些想法。

而Galileo就不一样了,它提供的是一个可扩展的PC平台,程序员应该很容易喜欢上这个东西,到处都是很熟悉的工具和环境,不用考虑细节麻烦,连接上网线,就是一台linux服务器,可以直接写代码处理各种输入数据,提供了实现各种有趣系统的可能性。

当有大量的程序员开始投入的时候,各种精巧的东西就会被构造出来,毕竟电子工程师做的还是偏底层,单片机和嵌入式应用一般侧重点都在硬件,这样就导致他们不会实现太复杂的东西。

由于Galileo刚刚推出,所以近期看不到太多实现,估计它应该会火起来,两三年后可以回头验证下我的预测。

select

先指出一点,select的设计存在很多问题和缺陷

select的几个参数都很诡异。

许多人经常遇到的问题是每次调用过后没有重新FD_SET一下文件描述符列表,我刚接触这个函数的时候就被这个问题困扰过。

今天又遇到了个问题。

下面这段代码在我上大学的时候就写下了,一直工作的很好
[code]
int status()
{
FD_SET(fd,&fset);
return ::select(1,&fset,NULL,NULL,&s_timeout);
}

bool avail(){
int x= status();
if (x==-1){
perror("select()");
}
// char buf[4];
// int err = recv(fd, buf, sizeof(buf), MSG_PEEK);
// if(err == 0 || (err < 0 && errno != EAGAIN))
// throw 1;

return x>0;
}
[/code]
直到今天我把它移植到mac系统上

程序可以正常的发数据包,但一直没有收到任何数据,花了一段时间折腾才定位到select一直返回为0

但是这段代码在windows下工作的很好,为什么到了OS X上就失败呢?

仔细的看了看文档

int
select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict errorfds, struct timeval *restrict timeout);

DESCRIPTION
     Select() examines the I/O descriptor sets whose addresses are passed in readfds, writefds, and errorfds to see if some of their descriptors are ready for reading, are ready for writing, or have an exceptional condition pending, respectively.  The first nfds descriptors are checked in each set; i.e., the descriptors from 0 through nfds-1 in the descriptor sets are examined.  (Example: If you have set two file descriptors "4" and "17", nfds should  not be "2", but rather "17 + 1" or "18".)  On return, select() replaces the given descriptor sets with subsets consist-ing consisting of those descriptors that are ready for the requested operation.
Select() returns the total number of ready descriptors in all the sets.

第一个参数nfds代表的并不是所要观察的文件描述符的个数,而是最大的文件描述符加一,上面那个函数调用应该写成
[code]::select(fd+1,&fset,NULL,NULL,&s_timeout);[/code]
才行。

那么,为什么windows下能够工作正常呢?msdn上给出的说法简单粗暴

nfds [in]
Ignored. The nfds parameter is included only for compatibility with Berkeley sockets.

windows保留这个参数只是为了跟bsd socket兼容,直接忽略掉了。

也就是说,当初我把这个参数误认为了个数,windows也忽略了,所以一直能够正常使用。而mac系统是基于freebsd内核的,是标准的bsd实现,问题才浮出水面。

select的最后一个参数timeout是个struct,使用起来需要填充各个值,很不方便,其实直接用一个int表示毫秒数就行了。

总之,这个函数的每个参数都存在陷阱和缺陷,unix的设计哲学是简洁统一,而select是个十足的反面例子。

另一个经常被人诟病的问题是性能,在服务器端已经是很经典的问题了,由于select每次都要轮询整个列表,对于一个多人在线的服务器会造成很大的性能开销,另一个poll函数也类似,为了解决这个问题,linux在2.6版本内核引入了epoll机制,epoll_wait函数并不轮询,当数据到达的时候内核会把这些描述符放入一个完成队列,检查下它就行了。

对于客户端应用来说,select的性能问题并不存在,而且在windows下,异步socket一般需要个消息循环,所以对于一些命令行程序来讲,select就是唯一的选择了,这也是我一直在使用上面这段代码的原因。

能量

吃饭的时候有一个问题浮现在我的大脑中,我们通过摄取食物提供生存的能量,而所有食物的根源来自于植物,以当今的科技水平,是否可以脱离植物,人工合成所需要的食物呢?

人类需要从外界获取的物质可分为几类,糖类,无机盐类,氨基酸,以及一些维生素之类的小分子。其中所需的钠钾离子之类的无机盐类是广泛存在于地球之上的,剩下的几种,糖类是提供能量的,需要仔细讨论。

普通存在于人类体内并起着重要作用的是葡萄糖,葡萄糖是一种单糖,分子式为C6H10O6,植物通过光合作用来合成,但并不是直接以葡萄糖的形式存储下来。

能量来源于何处?归根结底,一个答案,太阳。上帝把这个核反应堆放置于距离地球1.5亿公里以外的地方,通过电磁波把能量传递到自由空间,而地球所接收的极小截面提供了所有生物赖以生存的能量。

直接利用阳光的能量是不明智的,因为单位面积内的光线的能量密度很低,必须用一个特别大的接受装置才能提供发动机的动力。而且晚上没有阳光。

其实在能量利用上,最大的问题不是如何产生能量,而是如何有效的储存能量。

我们通常意义上讲的储存,一般都是通过分子间的电势能来实现的,比如,氢气燃烧释放出能量,这个过程涉及到跟氧气结合形成势能更低的水分子。相对的说,能量可以储存成氢气的形式。葡萄糖也是一样,通过跟氧气反应结合成二氧化碳和水,降低了静电势。显然,葡萄糖由于本身就是多个分子结合的状态,碳原子质量也比氢高很多,所以储存的能量密度比氢气低许多,但是氢气是气态,也容易爆炸,不易安全的储存使用。

覆盖整个地面的植物,每天都通过光合作用,把阳光中的能量固定存储下来。储存形式多种多样,比如纤维素,淀粉。

有个有趣的事,看一下羊肉串,我们为什么要把它串起来吃呢?因为一个一个的不容易烤,拿起来也不方便,串到一起就方便多了。淀粉和纤维素也是如此,都是葡萄糖这些简单的小分子成千上万的串联起来。这样能量就储存在其中,运输和使用就很方便了。

可以看一下我们人类储存能量的一种形式,存在于肝脏内部糖原的结构。

让人不得不惊叹生物是伟大的编织者,把一个个简单的分子组装成复杂的图样

现在量产葡萄糖的方法是把淀粉之类的大分子链打断,也许单分子的葡萄糖我们可以也从无机界人工合成,但那肯定要耗费大量能量。要是再把这些单糖分子组装成淀粉纤维素之类的大分子链,几乎是不肯能。其实关于开头问题的结论很简单,谁都能凭直觉给出,人类目前还是无法脱离植物产生食物,换句话说,不能直接通过阳光生产面包。

从能量密度的角度来看,目前可行的储存能量方式,氢气是最高的,每千克可以达到123百万焦耳,汽油可以达到46,而前面讨论的糖类只有17左右,这就是为什么现在汽车广泛使用石油作为燃料的原因。

我们使用的手机里的锂电池,能量密度只有0.8左右,要提升它的续航时间,也许可以考虑使用汽油或者氢气,但是这些能源利用起来需要沉重的反应装置比如内燃机,所以就限制了他们在手机这些便携设备上的使用,如果能够找到轻便的释放方式,也许我们的电池技术就会有突飞猛进的发展。

现在鼓吹的很火的新能源汽车,比如tesla,其实就是一大堆锂电池带动电动机,显然锂电池的能量密度限制了它们的最大行驶里程,不过使用油电混合动力的方式可以很大程度上弥补这一点,比如大众XL1。

从产业的角度看,能量的储存方式是改变产业结构的基本力量。人类的文明程度几乎可以用使用能源多少来衡量,在生活的每个角落,电视机,冰箱,燃气灶,汽车,手机……随处一瞥便能看到能源需求。

更高的能量密度和更好的能量释放方式,可以带动电池和发动机的变革,所有智能手机用户都对电池使用时间表示过不满,石油危机也导致汽车产业对新能源的尝试,需求不可谓不强。

单从化学能方面我们很难获得比石油更高的能量密度,除非基础物理有进展可以和平释放原子能,但是上帝把太阳放到离我们如此远的地方仅仅让世界以分子间电势能的形式运行应该有一定道理的,就像大人把刀枪放在远离小孩子的地方一样。

对于能源枯竭,最好的解决方案也许是从阳光来合成石油,比如利用基因工程的方法,改造利用某些植物,合成相应生成石油的原料,虽然我们很难获得比石油更高的储存能量方式,但可以通过寻找再生石油的方法来解决耗尽的危机。

理论上说,太阳的能源利用率很低,大部分都被反射和吸收后热辐射出去了。有句话叫开源节流,对于能源节流意味着文明倒退,开源才是注意力的重点,要尽可能的有效利用阳光。

个人认为,解决能量问题,应该把更多的精力花在生物学和基因工程上。

NAT

先罗列下我的需求,有两台机器,一台windows 7,另一台mac,现在想在这两个机器间自动同步文件。两台机器位于不同的私有子网段,也就是说,躲在两个不同的路由器后面接入公网。

因为nat的存在,使得两台机器间直接建立tcp连接成为不可能,想了几个方案:

第一,通过一台有公网ip的机器中转,两台机器同时跟这台服务器建立tcp连接,定时交换文件时间戳并相应的更新。这种方案存在巨大的弊端,我的服务器在美国,两台挨在一起的机器,传送数据居然需要绕过层层路由跨过海底光缆传送到大洋彼端然后再传回来,速度慢,占带宽,太折腾。

第二种就是udp打洞了,假设两台机器分别为A和B,中转机器为S,这样A和B都可以向S发送UDP数据包了,S可以得到NAT转换后的ip和端口号,由于UDP不是面向连接的,任何主机都可以向这个ip和端口号发送数据。理论上讲,只要S把这些信息告诉A和B,这两台机器就可以直接通信了。但是因为安全性的考虑,如果这个数据包的ip和端口号不是先前发给S的那个地址,就会被路由器丢弃。如何取得路由器的信任,是打洞最需要解决的问题。一个简单的想法是,A向B的NAT转换后的地址发送一个udp包,这样A的NAT对于B的这个地址就是敞开的了,如果B能发出具有这样地址的包,二者之间的连接就能建立起来。

今天花了一个下午编写调试相应的代码,A和B之间的数据包始终不能成功接收,后来才意识到,A向B再次发送数据包的时候,NAT路由器会重新生成不同的端口号,换了个端口向S发包测试打印地址印证了这个想法,这个端口号似乎是随机生成的。

google了下,有些路由器确实是这样实现的,这种情况下UDP打洞基本不可行。

总之,问题没那么圆满的解决,最终还是使用方案一中转,先只同步小文件吧,至少比手动拷方便些。

客观评价下NAT对互联网的影响吧,好的方面:解决了ip地址快速耗尽的问题,估计延缓了一二十年。也提高了安全性,某种程度把病毒隔离在一个个子网范围内。

另一方面,把端口号扩展为ip地址的一部分,本身就是一种很踅脚的做法,增加了软件的复杂度。一个好的设计应该简单统一,不应该让人在实现的时候感到头疼。

在已有系统上缝缝补补,用最简单的改变来适应环境,是生物的进化的基本特征。互联网在遇到问题时的解决方案似乎也在重复着大自然的脚步。

还是希望ipv6早日普及吧。