伙伴最近遇到一个小问题,多个Android模拟器如何进行网络通信。虚拟机不习惯用,平时一直用真机测试,第一次尝试解决这种问题。
记得用Android模拟器访问本机http服务用的网址是10.0.2.2,所以Android模拟器有自己的虚拟网络。两个模拟器网络独立,相互通信,一定要通过本机的网络。另外Android模拟器默认情况下不转发10.0.2.2的udp、tcp等协议,需要进行手动配置。
我们先来看看两个Android模拟器的网络结构:
通过上图可以看到,要实现多个模拟器上应用网络通信,需要设置一下虚拟路由器,让虚拟机1的网络请求,可以通过虚拟路由访问到虚拟机2。
在这里,我们暴露虚拟机1
的6666端口
到本机网络,让虚拟机2
可以发送消息到6666端口。
在这里参考一下解决两个 Android 模拟器之间无法网络通信的问题和官方文档Set Up Android Emulator Networking。
window自带telnet工具,但是需要启用,启用方法自行百度。 mac telnet工具已经移除,需要用brew安装一下。brew安装方法参考Mac OS X系统 HomeBrew的安装和简单使用。 安装telnet一句命令搞定:
brew install telnet
使用telnet命令连接到模拟器
:
telnet localhost 5554
小问题:
你说5554是啥?
5554是模拟器与PC通信的端口号,在窗口栏上可以看到。 例如:Android Emulator - Nexus_5X_API_24_1:5554。
什么?你说连接不上,显示下面这些?
Trying 127.0.0.1... telnet: connect to address 127.0.0.1: Connection refused Trying ::1... telnet: connect to address ::1: Connection refused telnet: Unable to connect to remote host
请先打开
Android模拟器
好不,我们要配置的就是模拟器
。 连接后,很有可能显示如下:Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Android Console: Authentication required Android Console: type 'auth <auth_token>' to authenticate Android Console: you can find your <auth_token> in '/Users/doc881/.emulator_console_auth_token' OK
这是缺少权限,
vi ~/.emulator_console_auth_token
查看一下自己的,然后在这里输入auth ,例如: auth NJ6QjlVf3/fWFEin
返回如下内容:
Android Console: type 'help' for a list of commands OK
这时候我们已经进入到模拟器的console,可以配置路由转发规则了。
3、配置虚拟路由
命令为
redir add <协议名>:<PC端口号>:<Android模拟器内网端口号>
,例如:redir add udp:6666:6666
这样就可以通过本机网络的6666端口,访问Android模拟器的6666端口了。对本地网络6666端口的tcp、udp等连接请求,也会被转发到模拟器的6666端口。 redir的使用还是比较灵活的,某种情况下,要将pc端的5555端口映射到Android模拟器的6666端口上,可以使用如下命令:
redir add udp:5000:6666
这里不要把端口号的顺序整错了。
映射虚拟机1
的6666端口
到本机网络的6666端口
:
telnet localhost 5554
auth NJ6QjlVf3/fWFEin
redir add udp:6666:6666
到这里,端口的配置就完成了。下面写个测试程序来试验一下
下面是一个服务端和一个客户端的简单实现。先来看看效果:
伙伴的例子里用的是DatagramSocket,我这里也使用同样的类。
Tip:DatagramSocket在通信的时候,需要给定一个数据对象,而不是数据流或byte数组。
服务器端监听6666端口
try {
serverSocket = new DatagramSocket(6666);
} catch (SocketException e) {
e.printStackTrace();
}
check_thread = new Thread(new Runnable() {
@Override
public void run() {
while (true){
byte[] receiveData = new byte[1024];
Log.i("MainActivity","start recieving");
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
try {
serverSocket.receive(receivePacket);
InetAddress sourceIPAddress = receivePacket.getAddress();
receiveData = receivePacket.getData();
String messageNow = new String(receiveData);
Log.i("MainActivity","messageNow="+messageNow);
message.add(messageNow);
handler.sendEmptyMessage(SHOW_MESSAGE);
} catch (Exception e) {
}
}
}
});
check_thread.start();
客户端向10.0.2.2的6666端口发送消息
String host = client_host_input.getText().toString();
int port = Integer.parseInt(client_port_input.getText().toString());
byte[] content = client_content_input.getText().toString().getBytes(StandardCharsets.UTF_8);
try {
InetAddress address = InetAddress.getByName(host);
DatagramPacket sendPacket = new DatagramPacket(content,
content.length, address, port);
clientSocket.send(sendPacket);
client_content_input.setText("");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
具体的代码看AndroidNetworkTest