突然有人说使用android的模拟器做socket服务器,PC做客户端,使用UDP通信的时候,android端无法收到数据包。反过来没问题,我觉得这怎么可能,首先如果你代码没问题的话,那就只有真实IP和端口有问题了。于是我搜索了一下,网上还真不少人问这个问题,我不得不说现在java程序员都被SSH毁了,很多已经到了不去思考的地步了,遇到问题想都不想一问,原来你也这样?既然大家都这样,那就说明无法解决了……….

特别是对java来说开源库各种各样,很多人已经养成了不去思考和尝试的习惯,直接找现有的实现了。这样就完全没有编程的乐趣了,哎,好了回到本文要点吧。首先对于TCP/UDP通信来说至少要有一定的知识。

首先在不考虑TCP/UDP的具体协议和实现方式、网络设备和OSI各种协议的前提下至少要明白java的SOCKET框架,当然SOCKET对于各种语言来说道理是完全一样的,只是底层实现不同而已,到了java连socket的实现也不需要管了,只要知道类库怎么用就可以。

socket通信至少需要两个前提,对方的IP和端口。这也是基要求,所以出问题了在排除代码问题之后只能是出现在这里了,当然还有就是你网络的物理结构没问题。然后是本文的使用范围,本文的范围是PC和android模拟器或者使用USB方式与真实android进行socket通信。如果android连接上路由设备,与PC处于同等地位,对外有独立IP的情况下,比如WIFI。自然不在本文讨论范围,这时候你查看两个设备的IP然后指定端口连接就是了。

在此需要另外说明android设备的IP策略问题。当android设备与PC相连的的情况下,会默认PC的IP为10.0.2.2,自身的IP为10.0.2.15/127.0.1。也就是说android设备连接IP10.0.2.2就可以达到连接PC的效果,但是反过来,PC无法知道android的IP地址所以你无法使用某个默认值来主动向android设备发出连接请求。在想到这点时基本就明白接下来要怎么做了。

首先是TCP情况下,TCP是有连接状态的,所以任何一段连接建立成功就可以通信。使用TCP/SOCKET连接android设备的场景很多,比如,在android设备中有一个自己的apk做socket服务器,然后再PC端的程序需要与其建立连接然后获取android设备的详细情况。这时候一般是用adb forward 将本机 TCP端口转发到android设备的TCP端口,这样在PC端就可以无需知道android设备的IP直接向本机,如127.0.0.1的某个端口发送数据包,之后端口转发机制会将其转发到android设备。这个不多说,没什么意思。

接下来是UDP,之所以着重说UDP,是因为UDP非常特殊。好在这里是与PC直连,但是adb forward不能转发UDP端口信息,只能是TCP…. 好吧,也正因为如此我发现了转发端口的基本命令redir。

redir add < udp/tcp >:< pc端口 >:< 模拟器端口 >,如redir add udp:1096:1097 redir tcp:1096:1097,作用就是将PC的1096端口转发到android设备的1097端口,当然两个端口号可以相同,因为他们是在两个不同的设备上。但是有个缺点,就是不如adb forward灵活。操作过程如下:

如上图,PC端使用telnet命令连接到android设备,telnet需要的IP就是本机,端口可以使用adb devices命令查看,连接成功之后可以使用redir命令,有list、add、del几个参数,list如图就是列出存在的转发关系,add添加,del就是删除了,详细说明参考文档这里不必多说。设置转发成功之后就有一个从PC看是1098的通信端口,从android’设备看是1097通信端口的直连通道。接下来上代码。

上图左侧为android代码,实现一个UDP/SOCKET的服务端,监听本地1097端口。右侧为PC端java代码直接向本机1098端口发送UDP数据包。两侧分别启动,在转发关系建立之前,android是不会受到UDP数据包。一旦转发关系1098:1097建立之后UDP服务端就会收到PC端的消息。

如上图所示,右侧控制台为PC端socket发送内容,左侧logcat为android设备中的socket服务器收到的内容。