package com.lanremote.watch; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; /** * RemoteClient 需要的传输层。 * * 为什么要抽这一层: * 原来 RemoteClient 直接用 Socket,只能在同一局域网里连。 * 跨网络时手表在 4G 上、电脑在 NAT 后面,直连不可能, * 只能两边都主动连出去、由中转服务器对接。 * 把"怎么传字节"抽出来之后,RemoteClient 的协议逻辑一行都不用改: * SocketTransport —— 原来的局域网直连 * RelayTransport —— 走中转服务器(HTTPS 长轮询) */ public interface Transport { /** 来自对端的字节流。read() 应该阻塞等待。 */ InputStream input() throws IOException; /** 发给对端的字节流。 */ OutputStream output() throws IOException; /** 对端已经断开 / 自己关掉了。 */ boolean isClosed(); /** 关掉,释放资源。可以重复调用。 */ void close(); /** 显示用的对端描述("192.168.2.64:8765" 或 "中转服务器")。 */ String describe(); // ================================================================== /** 局域网直连:就是原来的 Socket。 */ final class SocketTransport implements Transport { private final Socket socket; private final InputStream in; private final OutputStream out; private volatile boolean closed; public SocketTransport(Socket socket) throws IOException { this.socket = socket; this.in = socket.getInputStream(); this.out = socket.getOutputStream(); } public Socket socket() { return socket; } @Override public InputStream input() { return in; } @Override public OutputStream output() { return out; } @Override public boolean isClosed() { return closed || socket.isClosed(); } @Override public void close() { closed = true; try { socket.close(); } catch (IOException ignored) { // 关的时候出错不用管 } } @Override public String describe() { return socket.getInetAddress() == null ? "电脑" : socket.getInetAddress().getHostAddress() + ":" + socket.getPort(); } } }