如果我事先知道两台不同局域网的主机的映射IP地址和映射端口号,并且双方都通过某种网络通讯工具(socket)向对方发送数据,使双方的路由器建立映射表,那么可以不通过公网服务器进行通讯吗?
// 向服务器(域名是host。端口号为port)发起TCP连接,若成功,则创建Socket对象,否则抛出异常。
Scocket socket = new Socket(InetAddress.getByName("LAPTOP-RSJSE505"),9999);
// 根据InetAddress对象所表示的IP地址以及端口号port发起连接。
Socket socket = new Socket(InetAddress.getByName("172.17.31.57"),9999);
对于两台不同局域网的主机,在没有公网服务器的情况下进行通讯并不是一件容易的事情。因为局域网和公网是隔离的,也就是说,两台主机之间是无法直接通讯的。但是,在一些特殊情况下,可以通过使用NAT(网络地址转换)来实现两台主机之间的通讯。
具体实现方法如下:
在两台主机上都安装一个支持UPnP协议的软件,如端口映射工具。
在局域网中,通过UPnP协议向路由器申请外网IP和端口的映射。
通过UPnP协议向路由器注册一个回调监听函数,以便在路由器映射规则发生变化时能够及时得到通知。
在程序中设置一个监听端口并绑定到本地IP地址。
创建一个套接字并连接远程主机的映射IP地址和端口号。
发送数据。
接收数据。
参考代码如下:
import java.net.*;
import java.io.*;
public class Client {
public static void main(String [] args) {
String serverName = "192.168.1.100";
int port = 9999;
try {
// 创建一个套接字并连接远程主机的映射IP地址和端口号
Socket client = new Socket(serverName, port);
System.out.println("已连接到主机: " + client.getRemoteSocketAddress());
// 发送数据
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.writeUTF("Hello from " + client.getLocalSocketAddress());
// 接收数据
InputStream inFromServer = client.getInputStream();
DataInputStream in = new DataInputStream(inFromServer);
System.out.println("服务器响应: " + in.readUTF());
// 关闭套接字
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.net.*;
import java.io.*;
public class Server extends Thread {
private ServerSocket serverSocket;
public Server(int port) throws IOException {
// 在程序中设置一个监听端口并绑定到本地IP地址
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(100000);
}
public void run() {
while(true) {
try {
System.out.println("等待远程连接,端口号为:" + serverSocket.getLocalPort() + "...");
// 等待客户端连接
Socket server = serverSocket.accept();
System.out.println("远程主机地址:" + server.getRemoteSocketAddress());
// 接收数据
DataInputStream in = new DataInputStream(server.getInputStream());
System.out.println(in.readUTF());
// 向客户端发送响应数据
DataOutputStream out = new DataOutputStream(server.getOutputStream());
out.writeUTF("谢谢连接我:" + server.getLocalSocketAddress() + "\nGoodbye!");
// 关闭套接字
server.close();
} catch (SocketTimeoutException s) {
System.out.println("Socket timed out!");
break;
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
public static void main(String [] args) {
int port = 9999;
try {
// 开启服务器监听
Thread t = new Server(port);
t.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
需要特别注意,因为NAT的运作机制是根据TCP/UDP数据包的头字段进行操作,所以使用非标准的TCP/UDP数据包(例如自定义TCP/UDP数据包)可能会导致NAT无法工作。此外,由于UPnP协议实现较为复杂,在使用过程中也要注意网络安全问题。
用什么工具通讯不重要,之所以外网不能连接内网就是因为路由器没有映射关系,现在知道访问成功路由器会建立映射关系,如果访问失败路由器会不会建立映射关系?