Android Socket 深度剖析:从原理到实战

Android Socket 深度剖析:从原理到实战

目录

  1. 引言
  2. [Socket 基础架构](#Socket 基础架构)
  3. [Android 平台特殊性](#Android 平台特殊性)
  4. [TCP Socket 深入解析](#TCP Socket 深入解析)
  5. [UDP Socket 与组播](#UDP Socket 与组播)
  6. [NIO 与多路复用](#NIO 与多路复用)
  7. 心跳机制设计
  8. 断线重连策略
  9. 粘包与拆包
  10. [SSL/TLS 安全通信](#SSL/TLS 安全通信)
  11. 性能优化
  12. 实战架构设计
  13. 调试与问题排查

引言

在移动互联网时代,实时通信已成为应用的核心能力。无论是即时通讯、实时游戏、股票行情推送,还是IoT设备控制,Socket都是底层通信的基石。本文将从操作系统内核层面到应用层架构,全方位剖析Android平台上的Socket编程。

Socket 基础架构

1.1 Socket 本质

Socket(套接字)本质上是操作系统内核中的一个数据结构,它是应用层与传输层之间的抽象接口。在Linux内核(Android基于Linux)中,Socket以文件描述符(File Descriptor)的形式存在。

复制代码
┌─────────────────────────────────────────────────────────┐
│                    Application Layer                     │
│                   (Your Android App)                     │
├─────────────────────────────────────────────────────────┤
│                    Socket API (JNI)                      │
│              java.net.Socket / java.nio.*                │
├─────────────────────────────────────────────────────────┤
│                   System Call Layer                      │
│         socket(), bind(), connect(), send(), recv()      │
├─────────────────────────────────────────────────────────┤
│                    Kernel Space                          │
│  ┌─────────────────────────────────────────────────┐    │
│  │              Socket Buffer (SKB)                 │    │
│  │  ┌─────────────┐         ┌─────────────┐       │    │
│  │  │ Send Buffer │         │ Recv Buffer │       │    │
│  │  └─────────────┘         └─────────────┘       │    │
│  └─────────────────────────────────────────────────┘    │
├─────────────────────────────────────────────────────────┤
│                  TCP/IP Protocol Stack                   │
│        TCP/UDP → IP → ARP → Network Driver               │
└─────────────────────────────────────────────────────────┘

1.2 Linux 内核中的 Socket 结构

c 复制代码
// 简化的内核socket结构
struct socket {
    socket_state            state;      // 连接状态
    short                   type;       // SOCK_STREAM/SOCK_DGRAM
    unsigned long           flags;      // 标志位
    struct file            *file;       // 关联的文件对象
    struct sock            *sk;         // 网络层socket
    const struct proto_ops *ops;        // 协议操作函数指针
};

struct sock {
    struct sk_buff_head     sk_receive_queue;  // 接收队列
    struct sk_buff_head     sk_write_queue;    // 发送队列
    wait_queue_head_t       sk_wq;             // 等待队列
    // ... 更多字段
};

1.3 TCP 三次握手在 Socket 中的体现

复制代码
     Client                                   Server
        │                                        │
        │  socket() → SYN_SENT                   │ socket() → bind() → listen()
        │                                        │ (LISTEN state)
        │                                        │
        │  ──────── SYN (seq=x) ────────────►   │
        │                                        │ SYN_RCVD
        │                                        │
        │  ◄─────── SYN+ACK (seq=y,ack=x+1) ──  │
        │                                        │
        │  ESTABLISHED                           │
        │  ──────── ACK (ack=y+1) ──────────►   │
        │                                        │ ESTABLISHED
        │                                        │
     connect()                               accept()
     returns                                 returns

Android 平台特殊性

2.1 主线程网络限制

Android 3.0+ 引入了 StrictMode,在主线程执行网络操作会抛出 NetworkOnMainThreadException

java 复制代码
// 错误示例 - 主线程网络操作
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 这会崩溃!
        Socket socket = new Socket("192.168.1.1", 8080);
    }
}

底层原理 :Android在BlockGuard中设置了策略检查:

java 复制代码
// android.os.StrictMode 内部实现
public static void onNetwork() {
    BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
    if (policy.getPolicyMask() != 0) {
        policy.onNetwork();  // 触发检查
    }
}

2.2 Doze 模式与网络限制

Android 6.0+ 的 Doze 模式会影响网络连接:

复制代码
┌──────────────────────────────────────────────────────────────┐
│                      Doze Mode States                         │
├──────────────────────────────────────────────────────────────┤
│                                                               │
│   ACTIVE ──► INACTIVE ──► IDLE_PENDING ──► IDLE              │
│      ▲                                        │               │
│      │                                        │               │
│      └──────── Motion/Screen On ◄────────────┘               │
│                                                               │
│   IDLE状态下:                                                │
│   • 网络访问被暂停                                            │
│   • 仅在维护窗口期允许网络                                     │
│   • 高优先级FCM消息可唤醒                                      │
│                                                               │
└──────────────────────────────────────────────────────────────┘

解决方案

java 复制代码
// 请求忽略电池优化
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(getPackageName())) {
    Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
    intent.setData(Uri.parse("package:" + getPackageName()));
    startActivity(intent);
}

// 使用前台服务保活Socket连接
public class SocketService extends Service {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startForeground(NOTIFICATION_ID, buildNotification());
        return START_STICKY;
    }
}

2.3 网络切换处理

java 复制代码
public class NetworkMonitor {
    private ConnectivityManager.NetworkCallback networkCallback;

    public void register(Context context) {
        ConnectivityManager cm = (ConnectivityManager)
            context.getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkRequest request = new NetworkRequest.Builder()
            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
            .build();

        networkCallback = new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(Network network) {
                // 网络可用,尝试重连Socket
                reconnectSocket();
            }

            @Override
            public void onLost(Network network) {
                // 网络丢失,标记断开状态
                markDisconnected();
            }

            @Override
            public void onCapabilitiesChanged(Network network,
                    NetworkCapabilities capabilities) {
                // 网络能力变化(如从WiFi切换到4G)
                int downBandwidth = capabilities.getLinkDownstreamBandwidthKbps();
                adjustBufferSize(downBandwidth);
            }
        };

        cm.registerNetworkCallback(request, networkCallback);
    }
}

TCP Socket 深入解析

3.1 连接建立的完整流程

java 复制代码
public class TcpClient {
    private Socket socket;
    private DataInputStream inputStream;
    private DataOutputStream outputStream;

    public void connect(String host, int port, int timeout) throws IOException {
        // 创建未连接的Socket
        socket = new Socket();

        // 配置Socket选项(必须在connect之前)
        configureSocket();

        // 连接(带超时)
        InetSocketAddress address = new InetSocketAddress(host, port);
        socket.connect(address, timeout);

        // 获取流
        inputStream = new DataInputStream(
            new BufferedInputStream(socket.getInputStream(), 8192));
        outputStream = new DataOutputStream(
            new BufferedOutputStream(socket.getOutputStream(), 8192));
    }

    private void configureSocket() throws SocketException {
        // TCP_NODELAY: 禁用Nagle算法
        // Nagle算法会将小包合并,降低网络利用率但增加延迟
        // 实时性要求高的场景应禁用
        socket.setTcpNoDelay(true);

        // SO_KEEPALIVE: TCP层心跳
        // 默认2小时发送一次探测包,不适合移动网络
        socket.setKeepAlive(true);

        // SO_RCVBUF/SO_SNDBUF: 缓冲区大小
        // 影响TCP窗口大小和吞吐量
        socket.setReceiveBufferSize(64 * 1024);  // 64KB
        socket.setSendBufferSize(64 * 1024);

        // SO_TIMEOUT: 读取超时
        // 超时后抛出SocketTimeoutException,但连接不关闭
        socket.setSoTimeout(30000);  // 30秒

        // SO_LINGER: 关闭行为
        // linger=true, time=0: 立即关闭,发送RST,丢弃未发送数据
        // linger=true, time>0: 等待指定秒数,发送完成或超时后关闭
        // linger=false: 默认行为,close()立即返回,后台继续发送
        socket.setSoLinger(true, 5);

        // SO_REUSEADDR: 地址复用
        // 允许绑定TIME_WAIT状态的地址
        socket.setReuseAddress(true);
    }
}

3.2 Nagle 算法深度理解

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    Nagle Algorithm                           │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  发送条件(满足任一):                                        │
│  1. 数据量 >= MSS (Maximum Segment Size)                     │
│  2. 之前发送的所有小包都已被ACK                                │
│  3. 超时(通常200ms)                                         │
│                                                              │
│  ┌──────────────────────────────────────────────────────┐   │
│  │  App: send("H") send("e") send("l") send("l") send("o") │
│  │                                                       │   │
│  │  With Nagle (TCP_NODELAY=false):                     │   │
│  │  ──► [H] ──wait ACK──► [ello] ──►                    │   │
│  │  延迟高,包数少                                        │   │
│  │                                                       │   │
│  │  Without Nagle (TCP_NODELAY=true):                   │   │
│  │  ──► [H] ──► [e] ──► [l] ──► [l] ──► [o] ──►        │   │
│  │  延迟低,包数多                                        │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  最佳实践:                                                   │
│  • 实时游戏、交互应用:禁用 Nagle                             │
│  • 文件传输、批量数据:启用 Nagle                             │
│                                                              │
└─────────────────────────────────────────────────────────────┘

3.3 TCP 滑动窗口与流量控制

java 复制代码
/**
 * TCP窗口大小直接影响吞吐量
 *
 * 理论最大吞吐量 = Window Size / RTT
 *
 * 例如:
 * - Window Size = 64KB = 65536 bytes
 * - RTT = 100ms
 * - 吞吐量 = 65536 / 0.1 = 655360 bytes/s ≈ 640 KB/s
 *
 * 在高延迟网络中,需要更大的窗口才能充分利用带宽
 */
public class TcpWindowAnalyzer {

    public void optimizeForNetwork(Socket socket, NetworkInfo info)
            throws SocketException {
        int rtt = info.getEstimatedRtt();  // 毫秒
        int bandwidth = info.getBandwidthKbps();  // Kbps

        // 计算BDP (Bandwidth-Delay Product)
        // BDP = Bandwidth × RTT
        // 这是网络"管道"中能容纳的数据量
        long bdp = (long) bandwidth * 1000 / 8 * rtt / 1000;

        // 窗口大小应至少等于BDP
        int windowSize = (int) Math.min(bdp * 2, 1024 * 1024);  // 最大1MB
        windowSize = Math.max(windowSize, 64 * 1024);  // 最小64KB

        socket.setReceiveBufferSize(windowSize);
        socket.setSendBufferSize(windowSize);

        Log.d("TCP", String.format(
            "RTT=%dms, BW=%dKbps, BDP=%d, Window=%d",
            rtt, bandwidth, bdp, windowSize));
    }
}

3.4 TCP 状态机详解

复制代码
                              ┌───────────────────┐
                              │      CLOSED       │
                              └─────────┬─────────┘
                                        │
              ┌─────────────────────────┼─────────────────────────┐
              │                         │                         │
              ▼                         │                         ▼
     ┌────────────────┐                 │               ┌────────────────┐
     │     LISTEN     │                 │               │    SYN_SENT    │
     │   (服务端)      │                 │               │    (客户端)     │
     └───────┬────────┘                 │               └───────┬────────┘
             │                          │                       │
             │ recv SYN                 │                       │ recv SYN+ACK
             │ send SYN+ACK             │                       │ send ACK
             ▼                          │                       │
     ┌────────────────┐                 │                       │
     │   SYN_RCVD     │                 │                       │
     └───────┬────────┘                 │                       │
             │                          │                       │
             │ recv ACK                 │                       │
             └──────────────────────────┼───────────────────────┘
                                        │
                                        ▼
                              ┌───────────────────┐
                              │   ESTABLISHED     │
                              │    (数据传输)      │
                              └─────────┬─────────┘
                                        │
              ┌─────────────────────────┼─────────────────────────┐
              │ (主动关闭)               │                (被动关闭) │
              ▼                         │                         ▼
     ┌────────────────┐                 │               ┌────────────────┐
     │    FIN_WAIT_1  │                 │               │   CLOSE_WAIT   │
     └───────┬────────┘                 │               └───────┬────────┘
             │ recv ACK                 │                       │ send FIN
             ▼                         │                       ▼
     ┌────────────────┐                 │               ┌────────────────┐
     │    FIN_WAIT_2  │                 │               │    LAST_ACK    │
     └───────┬────────┘                 │               └───────┬────────┘
             │ recv FIN                 │                       │ recv ACK
             │ send ACK                 │                       │
             ▼                         │                       │
     ┌────────────────┐                 │                       │
     │   TIME_WAIT    │ ◄───────────────┘───────────────────────┘
     │  (2MSL等待)     │
     └───────┬────────┘
             │ timeout (2MSL ≈ 4min)
             ▼
     ┌────────────────┐
     │     CLOSED     │
     └────────────────┘

UDP Socket 与组播

4.1 UDP 基础

java 复制代码
public class UdpClient {
    private DatagramSocket socket;
    private InetAddress serverAddress;
    private int serverPort;

    public void init(String host, int port) throws Exception {
        // UDP Socket不需要连接
        socket = new DatagramSocket();

        // 可选:设置超时
        socket.setSoTimeout(5000);

        // 可选:绑定本地端口(用于接收响应)
        // socket = new DatagramSocket(localPort);

        serverAddress = InetAddress.getByName(host);
        serverPort = port;

        // 可选:"连接"UDP Socket
        // 这不是真正的连接,而是记录目标地址
        // 好处:1.可以使用send()而不是sendTo() 2.只接收来自该地址的数据
        socket.connect(serverAddress, serverPort);
    }

    public void send(byte[] data) throws IOException {
        DatagramPacket packet = new DatagramPacket(
            data, data.length, serverAddress, serverPort);
        socket.send(packet);
    }

    public byte[] receive() throws IOException {
        byte[] buffer = new byte[65535];  // UDP最大包65535字节
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
        socket.receive(packet);  // 阻塞直到收到数据

        // 返回实际接收的数据
        return Arrays.copyOf(packet.getData(), packet.getLength());
    }
}

4.2 UDP 组播(Multicast)

组播适用于一对多通信场景,如局域网设备发现、实时视频分发等。

java 复制代码
public class MulticastClient {
    private MulticastSocket socket;
    private InetAddress groupAddress;
    private int port;
    private NetworkInterface networkInterface;

    /**
     * 组播地址范围:224.0.0.0 - 239.255.255.255
     *
     * 224.0.0.0 - 224.0.0.255: 本地链路(TTL=1,不跨路由器)
     * 224.0.1.0 - 224.0.1.255: 互联网控制
     * 239.0.0.0 - 239.255.255.255: 管理作用域(私有组播)
     */
    public void joinGroup(String group, int port) throws Exception {
        this.port = port;
        this.groupAddress = InetAddress.getByName(group);

        socket = new MulticastSocket(port);

        // 设置TTL(Time To Live)
        // TTL=1: 仅本地网络
        // TTL=32: 同一站点
        // TTL=64: 同一区域
        // TTL=128: 同一大陆
        // TTL=255: 全球
        socket.setTimeToLive(1);

        // 获取WiFi网络接口
        networkInterface = getWifiInterface();

        if (networkInterface != null) {
            // 加入组播组
            socket.joinGroup(
                new InetSocketAddress(groupAddress, port),
                networkInterface);
        }

        // 是否接收自己发送的消息
        socket.setLoopbackMode(true);  // true = 不接收
    }

    private NetworkInterface getWifiInterface() throws SocketException {
        Enumeration<NetworkInterface> interfaces =
            NetworkInterface.getNetworkInterfaces();
        while (interfaces.hasMoreElements()) {
            NetworkInterface ni = interfaces.nextElement();
            if (ni.getName().startsWith("wlan") && ni.isUp()) {
                return ni;
            }
        }
        return null;
    }

    public void sendToGroup(byte[] data) throws IOException {
        DatagramPacket packet = new DatagramPacket(
            data, data.length, groupAddress, port);
        socket.send(packet);
    }

    public void leaveGroup() throws IOException {
        if (socket != null && networkInterface != null) {
            socket.leaveGroup(
                new InetSocketAddress(groupAddress, port),
                networkInterface);
            socket.close();
        }
    }
}

4.3 UDP 可靠性增强

UDP本身不保证可靠性,需要应用层实现:

java 复制代码
public class ReliableUdp {
    private static final int MAX_RETRIES = 3;
    private static final int TIMEOUT_MS = 1000;
    private final Map<Integer, PendingPacket> pendingAcks = new ConcurrentHashMap<>();
    private final AtomicInteger sequenceNumber = new AtomicInteger(0);

    /**
     * 自定义可靠UDP协议头
     *
     * ┌─────────────────────────────────────────────────────┐
     * │  0-1  │  2-5   │   6    │  7-N  │
     * │ Type  │ SeqNum │ Flags  │ Data  │
     * └─────────────────────────────────────────────────────┘
     *
     * Type: 0x01=DATA, 0x02=ACK, 0x03=NACK
     * Flags: 0x01=需要ACK, 0x02=重传
     */

    public void sendReliable(byte[] data, DatagramSocket socket,
            InetAddress address, int port) throws IOException {
        int seq = sequenceNumber.incrementAndGet();
        byte[] packet = buildPacket(0x01, seq, (byte) 0x01, data);

        DatagramPacket dgram = new DatagramPacket(
            packet, packet.length, address, port);

        PendingPacket pending = new PendingPacket(dgram, System.currentTimeMillis());
        pendingAcks.put(seq, pending);

        // 发送并等待ACK
        for (int i = 0; i < MAX_RETRIES; i++) {
            socket.send(dgram);

            if (waitForAck(socket, seq, TIMEOUT_MS)) {
                pendingAcks.remove(seq);
                return;  // 成功
            }
        }

        pendingAcks.remove(seq);
        throw new IOException("发送失败:未收到ACK");
    }

    private boolean waitForAck(DatagramSocket socket, int expectedSeq,
            int timeoutMs) throws IOException {
        socket.setSoTimeout(timeoutMs);
        byte[] buffer = new byte[7];  // ACK包很小
        DatagramPacket ackPacket = new DatagramPacket(buffer, buffer.length);

        try {
            socket.receive(ackPacket);
            int type = buffer[0];
            int ackSeq = ByteBuffer.wrap(buffer, 1, 4).getInt();
            return type == 0x02 && ackSeq == expectedSeq;
        } catch (SocketTimeoutException e) {
            return false;
        }
    }

    private byte[] buildPacket(int type, int seq, byte flags, byte[] data) {
        ByteBuffer buffer = ByteBuffer.allocate(7 + data.length);
        buffer.put((byte) type);
        buffer.putInt(seq);
        buffer.put(flags);
        buffer.put((byte) data.length);
        buffer.put(data);
        return buffer.array();
    }

    static class PendingPacket {
        DatagramPacket packet;
        long timestamp;
        int retries;

        PendingPacket(DatagramPacket packet, long timestamp) {
            this.packet = packet;
            this.timestamp = timestamp;
            this.retries = 0;
        }
    }
}

NIO 与多路复用

5.1 BIO vs NIO 对比

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                    BIO (Blocking I/O)                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  Thread-1 ────► Socket-1.read() [阻塞] ────► 处理 ────► 继续        │
│  Thread-2 ────► Socket-2.read() [阻塞] ────► 处理 ────► 继续        │
│  Thread-3 ────► Socket-3.read() [阻塞] ────► 处理 ────► 继续        │
│  ...                                                                 │
│  Thread-N ────► Socket-N.read() [阻塞] ────► 处理 ────► 继续        │
│                                                                      │
│  问题:                                                              │
│  • 每个连接需要一个线程,资源消耗大                                   │
│  • 线程切换开销大                                                    │
│  • 连接数受限于线程数                                                 │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│                    NIO (Non-blocking I/O)                            │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│                    ┌─────────────┐                                   │
│                    │  Selector   │                                   │
│                    │  (多路复用)  │                                   │
│                    └──────┬──────┘                                   │
│           ┌───────────────┼───────────────┐                         │
│           ▼               ▼               ▼                         │
│      ┌─────────┐    ┌─────────┐    ┌─────────┐                     │
│      │Channel-1│    │Channel-2│    │Channel-N│                     │
│      └─────────┘    └─────────┘    └─────────┘                     │
│                                                                      │
│  单线程处理多个连接:                                                 │
│  while (true) {                                                      │
│      selector.select();  // 阻塞直到有事件                           │
│      for (SelectionKey key : selectedKeys) {                        │
│          if (key.isReadable()) handleRead(key);                     │
│          if (key.isWritable()) handleWrite(key);                    │
│      }                                                               │
│  }                                                                   │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

5.2 NIO 核心组件

java 复制代码
public class NioSocketClient {
    private Selector selector;
    private SocketChannel channel;
    private ByteBuffer readBuffer = ByteBuffer.allocate(8192);
    private ByteBuffer writeBuffer = ByteBuffer.allocate(8192);
    private final Queue<ByteBuffer> writeQueue = new ConcurrentLinkedQueue<>();

    public void connect(String host, int port) throws IOException {
        // 创建Selector
        selector = Selector.open();

        // 创建SocketChannel
        channel = SocketChannel.open();
        channel.configureBlocking(false);  // 非阻塞模式

        // 配置Socket选项
        channel.setOption(StandardSocketOptions.TCP_NODELAY, true);
        channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
        channel.setOption(StandardSocketOptions.SO_RCVBUF, 64 * 1024);
        channel.setOption(StandardSocketOptions.SO_SNDBUF, 64 * 1024);

        // 注册连接事件
        channel.register(selector, SelectionKey.OP_CONNECT);

        // 发起连接(非阻塞,立即返回)
        channel.connect(new InetSocketAddress(host, port));

        // 启动事件循环
        startEventLoop();
    }

    private void startEventLoop() {
        new Thread(() -> {
            try {
                while (!Thread.currentThread().isInterrupted()) {
                    // 阻塞等待事件,最多1秒
                    int readyCount = selector.select(1000);

                    if (readyCount == 0) {
                        continue;
                    }

                    Iterator<SelectionKey> iterator =
                        selector.selectedKeys().iterator();

                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();
                        iterator.remove();  // 必须手动移除

                        if (!key.isValid()) continue;

                        if (key.isConnectable()) {
                            handleConnect(key);
                        }
                        if (key.isReadable()) {
                            handleRead(key);
                        }
                        if (key.isWritable()) {
                            handleWrite(key);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }, "NIO-EventLoop").start();
    }

    private void handleConnect(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();

        if (channel.finishConnect()) {
            // 连接成功,注册读事件
            key.interestOps(SelectionKey.OP_READ);
            onConnected();
        }
    }

    private void handleRead(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        readBuffer.clear();

        int bytesRead = channel.read(readBuffer);

        if (bytesRead == -1) {
            // 连接关闭
            key.cancel();
            channel.close();
            onDisconnected();
            return;
        }

        if (bytesRead > 0) {
            readBuffer.flip();
            byte[] data = new byte[readBuffer.remaining()];
            readBuffer.get(data);
            onDataReceived(data);
        }
    }

    private void handleWrite(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();

        while (!writeQueue.isEmpty()) {
            ByteBuffer buffer = writeQueue.peek();
            channel.write(buffer);

            if (buffer.hasRemaining()) {
                // 写缓冲区满,等待下次可写事件
                return;
            }

            writeQueue.poll();
        }

        // 队列空了,取消写事件监听
        key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
    }

    public void send(byte[] data) {
        ByteBuffer buffer = ByteBuffer.wrap(data);
        writeQueue.offer(buffer);

        // 注册写事件
        SelectionKey key = channel.keyFor(selector);
        if (key != null && key.isValid()) {
            key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
            selector.wakeup();  // 唤醒阻塞的select()
        }
    }

    // 回调方法
    protected void onConnected() {}
    protected void onDisconnected() {}
    protected void onDataReceived(byte[] data) {}
}

5.3 Direct Buffer vs Heap Buffer

java 复制代码
/**
 * ByteBuffer 类型对比
 *
 * ┌─────────────────────────────────────────────────────────────────┐
 * │                     Heap Buffer                                  │
 * │  ByteBuffer.allocate(size)                                       │
 * ├─────────────────────────────────────────────────────────────────┤
 * │  • 分配在JVM堆内存                                               │
 * │  • GC可以回收                                                    │
 * │  • I/O操作需要额外拷贝到native内存                               │
 * │  • 适合:小数据、频繁创建销毁                                     │
 * └─────────────────────────────────────────────────────────────────┘
 *
 * ┌─────────────────────────────────────────────────────────────────┐
 * │                    Direct Buffer                                 │
 * │  ByteBuffer.allocateDirect(size)                                 │
 * ├─────────────────────────────────────────────────────────────────┤
 * │  • 分配在native内存(堆外内存)                                   │
 * │  • 不受GC直接管理(有Cleaner回收)                               │
 * │  • I/O操作零拷贝                                                 │
 * │  • 分配和释放开销大                                              │
 * │  • 适合:大数据、长期使用、频繁I/O                                │
 * └─────────────────────────────────────────────────────────────────┘
 */
public class BufferPerformanceTest {

    public void compare() {
        int size = 1024 * 1024;  // 1MB
        int iterations = 1000;

        // Heap Buffer
        long start = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            ByteBuffer buffer = ByteBuffer.allocate(size);
            buffer.put(new byte[size]);
            buffer.flip();
        }
        long heapTime = System.nanoTime() - start;

        // Direct Buffer (预分配,重用)
        ByteBuffer directBuffer = ByteBuffer.allocateDirect(size);
        start = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            directBuffer.clear();
            directBuffer.put(new byte[size]);
            directBuffer.flip();
        }
        long directTime = System.nanoTime() - start;

        Log.d("Buffer", String.format(
            "Heap: %dms, Direct: %dms",
            heapTime / 1_000_000,
            directTime / 1_000_000));
    }
}

心跳机制设计

6.1 心跳的必要性

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                    为什么需要应用层心跳?                             │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  1. TCP KeepAlive 不够用                                            │
│     • 默认2小时才探测(tcp_keepalive_time=7200s)                    │
│     • 移动网络NAT超时通常5-10分钟                                    │
│     • 无法检测应用层的"假死"状态                                     │
│                                                                      │
│  2. 移动网络的特殊性                                                 │
│     • 基站切换可能导致连接中断                                       │
│     • NAT/防火墙会清理空闲连接                                       │
│     • 运营商可能对长连接做特殊处理                                   │
│                                                                      │
│  3. 业务需求                                                         │
│     • 实时检测服务端状态                                             │
│     • 快速发现连接断开                                               │
│     • 触发自动重连                                                   │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

6.2 智能心跳算法

java 复制代码
public class SmartHeartbeat {
    // 心跳间隔范围
    private static final long MIN_INTERVAL = 30 * 1000;   // 30秒
    private static final long MAX_INTERVAL = 300 * 1000;  // 5分钟
    private static final long STEP = 30 * 1000;           // 步长30秒

    private long currentInterval = MIN_INTERVAL;
    private int successCount = 0;
    private int failCount = 0;
    private long lastSuccessTime = 0;
    private NetworkType lastNetworkType;

    private final Handler handler = new Handler(Looper.getMainLooper());
    private final SocketClient client;

    public SmartHeartbeat(SocketClient client) {
        this.client = client;
    }

    /**
     * 智能心跳算法:
     *
     * 1. 初始使用最小间隔
     * 2. 连续3次成功后,增加间隔
     * 3. 失败1次后,立即回退到最小间隔
     * 4. 网络切换时,重置到最小间隔
     *
     * ┌─────────────────────────────────────────────────────────┐
     * │  成功  成功  成功  →  增加间隔                           │
     * │   30s   30s   30s →    60s                             │
     * │                                                         │
     * │  成功  成功  失败  →  重置间隔                           │
     * │   60s   60s  FAIL →    30s                             │
     * └─────────────────────────────────────────────────────────┘
     */
    public void start() {
        scheduleNextHeartbeat();
    }

    private void scheduleNextHeartbeat() {
        handler.postDelayed(this::sendHeartbeat, currentInterval);
    }

    private void sendHeartbeat() {
        long startTime = System.currentTimeMillis();

        client.sendHeartbeat(new HeartbeatCallback() {
            @Override
            public void onSuccess() {
                long rtt = System.currentTimeMillis() - startTime;
                onHeartbeatSuccess(rtt);
            }

            @Override
            public void onFailure() {
                onHeartbeatFailure();
            }
        });
    }

    private void onHeartbeatSuccess(long rtt) {
        failCount = 0;
        successCount++;
        lastSuccessTime = System.currentTimeMillis();

        // 连续成功3次,尝试增加间隔
        if (successCount >= 3) {
            successCount = 0;
            currentInterval = Math.min(currentInterval + STEP, MAX_INTERVAL);
            Log.d("Heartbeat", "增加心跳间隔至: " + currentInterval + "ms");
        }

        scheduleNextHeartbeat();
    }

    private void onHeartbeatFailure() {
        successCount = 0;
        failCount++;

        // 立即回退到最小间隔
        currentInterval = MIN_INTERVAL;
        Log.w("Heartbeat", "心跳失败,重置间隔至: " + currentInterval + "ms");

        // 连续失败3次,触发重连
        if (failCount >= 3) {
            failCount = 0;
            client.reconnect();
        } else {
            scheduleNextHeartbeat();
        }
    }

    public void onNetworkChanged(NetworkType newType) {
        if (lastNetworkType != newType) {
            lastNetworkType = newType;
            currentInterval = MIN_INTERVAL;
            successCount = 0;

            // 立即发送一次心跳
            handler.removeCallbacksAndMessages(null);
            sendHeartbeat();
        }
    }

    public void stop() {
        handler.removeCallbacksAndMessages(null);
    }
}

6.3 心跳包设计

java 复制代码
/**
 * 心跳包格式设计原则:
 * 1. 尽可能小(节省流量)
 * 2. 包含必要信息(时间戳用于RTT计算)
 * 3. 区分请求和响应
 */
public class HeartbeatPacket {

    /**
     * 简单心跳包(4字节)
     *
     * ┌─────────┬─────────┐
     * │  Type   │ Payload │
     * │ 1 byte  │ 3 bytes │
     * └─────────┴─────────┘
     *
     * Type: 0x01=Ping, 0x02=Pong
     * Payload: 可选,用于携带序号或时间戳
     */
    public static byte[] createPing(int sequence) {
        ByteBuffer buffer = ByteBuffer.allocate(4);
        buffer.put((byte) 0x01);
        buffer.put((byte) ((sequence >> 16) & 0xFF));
        buffer.put((byte) ((sequence >> 8) & 0xFF));
        buffer.put((byte) (sequence & 0xFF));
        return buffer.array();
    }

    public static byte[] createPong(int sequence) {
        ByteBuffer buffer = ByteBuffer.allocate(4);
        buffer.put((byte) 0x02);
        buffer.put((byte) ((sequence >> 16) & 0xFF));
        buffer.put((byte) ((sequence >> 8) & 0xFF));
        buffer.put((byte) (sequence & 0xFF));
        return buffer.array();
    }

    /**
     * 带时间戳的心跳包(12字节)
     *
     * ┌─────────┬───────────┬───────────┐
     * │  Type   │ Sequence  │ Timestamp │
     * │ 1 byte  │  3 bytes  │  8 bytes  │
     * └─────────┴───────────┴───────────┘
     */
    public static byte[] createTimestampPing(int sequence) {
        ByteBuffer buffer = ByteBuffer.allocate(12);
        buffer.put((byte) 0x01);
        buffer.put((byte) ((sequence >> 16) & 0xFF));
        buffer.put((byte) ((sequence >> 8) & 0xFF));
        buffer.put((byte) (sequence & 0xFF));
        buffer.putLong(System.currentTimeMillis());
        return buffer.array();
    }
}

断线重连策略

7.1 指数退避算法

java 复制代码
public class ReconnectManager {
    private static final long BASE_DELAY = 1000;      // 基础延迟 1秒
    private static final long MAX_DELAY = 60000;      // 最大延迟 60秒
    private static final double MULTIPLIER = 2.0;     // 倍数
    private static final double JITTER = 0.2;         // 抖动 ±20%

    private int attemptCount = 0;
    private boolean isReconnecting = false;
    private final Handler handler = new Handler(Looper.getMainLooper());
    private final SocketClient client;
    private final Random random = new Random();

    public ReconnectManager(SocketClient client) {
        this.client = client;
    }

    /**
     * 指数退避算法:
     *
     * delay = min(BASE_DELAY * MULTIPLIER^attempt, MAX_DELAY) * (1 ± JITTER)
     *
     * 示例序列:
     * 尝试1: 1s × (1±0.2) = 0.8-1.2s
     * 尝试2: 2s × (1±0.2) = 1.6-2.4s
     * 尝试3: 4s × (1±0.2) = 3.2-4.8s
     * 尝试4: 8s × (1±0.2) = 6.4-9.6s
     * 尝试5: 16s × (1±0.2) = 12.8-19.2s
     * 尝试6: 32s × (1±0.2) = 25.6-38.4s
     * 尝试7+: 60s × (1±0.2) = 48-72s (capped)
     */
    public void scheduleReconnect() {
        if (isReconnecting) return;
        isReconnecting = true;

        long delay = calculateDelay();
        Log.d("Reconnect", String.format(
            "第%d次重连,延迟%dms", attemptCount + 1, delay));

        handler.postDelayed(this::doReconnect, delay);
    }

    private long calculateDelay() {
        // 计算基础延迟
        double exponentialDelay = BASE_DELAY * Math.pow(MULTIPLIER, attemptCount);
        long delay = (long) Math.min(exponentialDelay, MAX_DELAY);

        // 添加抖动(避免雷群效应)
        double jitter = 1.0 + (random.nextDouble() * 2 - 1) * JITTER;
        delay = (long) (delay * jitter);

        return delay;
    }

    private void doReconnect() {
        attemptCount++;

        client.connect(new ConnectionCallback() {
            @Override
            public void onSuccess() {
                isReconnecting = false;
                attemptCount = 0;
                Log.d("Reconnect", "重连成功");
            }

            @Override
            public void onFailure(Exception e) {
                isReconnecting = false;
                Log.w("Reconnect", "重连失败: " + e.getMessage());
                scheduleReconnect();  // 继续尝试
            }
        });
    }

    public void reset() {
        attemptCount = 0;
        isReconnecting = false;
        handler.removeCallbacksAndMessages(null);
    }

    public void stop() {
        isReconnecting = false;
        handler.removeCallbacksAndMessages(null);
    }
}

7.2 完整的连接状态机

java 复制代码
public class ConnectionStateMachine {

    public enum State {
        DISCONNECTED,    // 断开
        CONNECTING,      // 连接中
        CONNECTED,       // 已连接
        RECONNECTING,    // 重连中
        SUSPENDED        // 挂起(如:网络不可用)
    }

    public enum Event {
        CONNECT,         // 用户发起连接
        CONNECT_SUCCESS, // 连接成功
        CONNECT_FAILURE, // 连接失败
        DISCONNECT,      // 用户断开
        NETWORK_LOST,    // 网络丢失
        NETWORK_AVAILABLE, // 网络恢复
        HEARTBEAT_TIMEOUT, // 心跳超时
        SERVER_CLOSE     // 服务端关闭
    }

    /**
     * 状态转换表:
     *
     * ┌─────────────────┬──────────────────┬─────────────────┐
     * │   Current State │      Event       │   Next State    │
     * ├─────────────────┼──────────────────┼─────────────────┤
     * │  DISCONNECTED   │     CONNECT      │   CONNECTING    │
     * │  CONNECTING     │  CONNECT_SUCCESS │   CONNECTED     │
     * │  CONNECTING     │  CONNECT_FAILURE │   RECONNECTING  │
     * │  CONNECTED      │   DISCONNECT     │   DISCONNECTED  │
     * │  CONNECTED      │   NETWORK_LOST   │   SUSPENDED     │
     * │  CONNECTED      │ HEARTBEAT_TIMEOUT│   RECONNECTING  │
     * │  CONNECTED      │   SERVER_CLOSE   │   RECONNECTING  │
     * │  RECONNECTING   │  CONNECT_SUCCESS │   CONNECTED     │
     * │  RECONNECTING   │  CONNECT_FAILURE │   RECONNECTING  │
     * │  RECONNECTING   │   DISCONNECT     │   DISCONNECTED  │
     * │  SUSPENDED      │ NETWORK_AVAILABLE│   RECONNECTING  │
     * │  SUSPENDED      │   DISCONNECT     │   DISCONNECTED  │
     * └─────────────────┴──────────────────┴─────────────────┘
     */

    private State currentState = State.DISCONNECTED;
    private final List<StateChangeListener> listeners = new ArrayList<>();

    public synchronized void handleEvent(Event event) {
        State previousState = currentState;
        State nextState = getNextState(currentState, event);

        if (nextState != null && nextState != currentState) {
            currentState = nextState;
            notifyStateChange(previousState, currentState, event);
        }
    }

    private State getNextState(State current, Event event) {
        switch (current) {
            case DISCONNECTED:
                if (event == Event.CONNECT) return State.CONNECTING;
                break;

            case CONNECTING:
                if (event == Event.CONNECT_SUCCESS) return State.CONNECTED;
                if (event == Event.CONNECT_FAILURE) return State.RECONNECTING;
                if (event == Event.DISCONNECT) return State.DISCONNECTED;
                break;

            case CONNECTED:
                if (event == Event.DISCONNECT) return State.DISCONNECTED;
                if (event == Event.NETWORK_LOST) return State.SUSPENDED;
                if (event == Event.HEARTBEAT_TIMEOUT) return State.RECONNECTING;
                if (event == Event.SERVER_CLOSE) return State.RECONNECTING;
                break;

            case RECONNECTING:
                if (event == Event.CONNECT_SUCCESS) return State.CONNECTED;
                if (event == Event.CONNECT_FAILURE) return State.RECONNECTING;
                if (event == Event.DISCONNECT) return State.DISCONNECTED;
                if (event == Event.NETWORK_LOST) return State.SUSPENDED;
                break;

            case SUSPENDED:
                if (event == Event.NETWORK_AVAILABLE) return State.RECONNECTING;
                if (event == Event.DISCONNECT) return State.DISCONNECTED;
                break;
        }
        return null;
    }

    public State getCurrentState() {
        return currentState;
    }

    public void addStateChangeListener(StateChangeListener listener) {
        listeners.add(listener);
    }

    private void notifyStateChange(State from, State to, Event event) {
        for (StateChangeListener listener : listeners) {
            listener.onStateChanged(from, to, event);
        }
    }

    public interface StateChangeListener {
        void onStateChanged(State from, State to, Event event);
    }
}

粘包与拆包

8.1 问题本质

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                    TCP 是流式协议                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  应用层发送:  [消息A] [消息B] [消息C]                                │
│                                                                      │
│  TCP发送缓冲区可能的情况:                                           │
│                                                                      │
│  情况1(正常): [消息A] | [消息B] | [消息C]                          │
│                                                                      │
│  情况2(粘包): [消息A][消息B] | [消息C]                             │
│                                                                      │
│  情况3(拆包): [消息A的前半] | [消息A的后半][消息B] | [消息C]        │
│                                                                      │
│  情况4(混合): [消息A][消息B的前半] | [消息B的后半][消息C]           │
│                                                                      │
│  原因:                                                              │
│  1. TCP是面向流的,不保留消息边界                                    │
│  2. Nagle算法合并小包                                                │
│  3. 网络MTU限制导致分片                                              │
│  4. 接收缓冲区大小限制                                               │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

8.2 解决方案

java 复制代码
/**
 * 协议设计方案对比:
 *
 * 方案1:固定长度
 * ┌────────────────────────────────────┐
 * │ Data (固定100字节,不足补0)         │
 * └────────────────────────────────────┘
 * 优点:简单
 * 缺点:浪费带宽
 *
 * 方案2:分隔符
 * ┌─────────────┬────┐
 * │    Data     │ \n │
 * └─────────────┴────┘
 * 优点:简单,适合文本协议
 * 缺点:数据中不能包含分隔符
 *
 * 方案3:长度字段(推荐)
 * ┌─────────┬─────────────┐
 * │ Length  │    Data     │
 * │ 4 bytes │ N bytes     │
 * └─────────┴─────────────┘
 * 优点:灵活,高效
 * 缺点:实现稍复杂
 *
 * 方案4:TLV格式
 * ┌──────┬─────────┬─────────────┐
 * │ Type │ Length  │    Value    │
 * │ 2B   │ 4B      │ N bytes     │
 * └──────┴─────────┴─────────────┘
 * 优点:可扩展,自描述
 * 缺点:开销稍大
 */
public class PacketCodec {

    private static final int HEADER_SIZE = 4;  // 长度字段4字节
    private static final int MAX_PACKET_SIZE = 1024 * 1024;  // 最大1MB

    private ByteBuffer readBuffer = ByteBuffer.allocate(64 * 1024);
    private int expectedLength = -1;

    /**
     * 编码:添加长度头
     */
    public byte[] encode(byte[] data) {
        ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE + data.length);
        buffer.putInt(data.length);
        buffer.put(data);
        return buffer.array();
    }

    /**
     * 解码:处理粘包/拆包
     *
     * 返回完整的消息列表,不完整的数据保留在缓冲区
     */
    public List<byte[]> decode(byte[] receivedData) {
        List<byte[]> messages = new ArrayList<>();

        // 追加新数据到缓冲区
        ensureCapacity(receivedData.length);
        readBuffer.put(receivedData);
        readBuffer.flip();  // 切换到读模式

        while (true) {
            // 状态1:需要读取长度
            if (expectedLength == -1) {
                if (readBuffer.remaining() < HEADER_SIZE) {
                    break;  // 数据不足,等待更多数据
                }
                expectedLength = readBuffer.getInt();

                // 安全检查
                if (expectedLength < 0 || expectedLength > MAX_PACKET_SIZE) {
                    throw new IllegalStateException(
                        "Invalid packet length: " + expectedLength);
                }
            }

            // 状态2:需要读取数据
            if (readBuffer.remaining() < expectedLength) {
                break;  // 数据不足,等待更多数据
            }

            // 读取完整消息
            byte[] message = new byte[expectedLength];
            readBuffer.get(message);
            messages.add(message);

            // 重置状态,准备读取下一个消息
            expectedLength = -1;
        }

        // 压缩缓冲区,保留未处理的数据
        readBuffer.compact();

        return messages;
    }

    private void ensureCapacity(int additionalSize) {
        if (readBuffer.remaining() < additionalSize) {
            // 扩容
            ByteBuffer newBuffer = ByteBuffer.allocate(
                readBuffer.capacity() * 2 + additionalSize);
            readBuffer.flip();
            newBuffer.put(readBuffer);
            readBuffer = newBuffer;
        }
    }

    public void reset() {
        readBuffer.clear();
        expectedLength = -1;
    }
}

8.3 完整的协议实现

java 复制代码
/**
 * 完整的二进制协议设计:
 *
 * ┌──────────────────────────────────────────────────────────────┐
 * │                       Packet Structure                        │
 * ├──────┬──────┬──────┬──────┬──────┬───────────┬──────────────┤
 * │Magic │ Ver  │ Type │ Seq  │ Len  │  Payload  │   Checksum   │
 * │ 2B   │ 1B   │ 1B   │ 4B   │ 4B   │  N bytes  │     4B       │
 * └──────┴──────┴──────┴──────┴──────┴───────────┴──────────────┘
 *
 * Magic: 魔数,用于识别协议 (0xCAFE)
 * Ver: 协议版本
 * Type: 消息类型
 * Seq: 序列号
 * Len: Payload长度
 * Payload: 消息体
 * Checksum: CRC32校验
 */
public class Protocol {

    public static final short MAGIC = (short) 0xCAFE;
    public static final byte VERSION = 1;
    public static final int HEADER_SIZE = 12;  // Magic(2) + Ver(1) + Type(1) + Seq(4) + Len(4)
    public static final int CHECKSUM_SIZE = 4;

    // 消息类型
    public static final byte TYPE_HEARTBEAT_REQ = 0x01;
    public static final byte TYPE_HEARTBEAT_RSP = 0x02;
    public static final byte TYPE_AUTH_REQ = 0x10;
    public static final byte TYPE_AUTH_RSP = 0x11;
    public static final byte TYPE_MESSAGE = 0x20;
    public static final byte TYPE_ACK = 0x21;

    private final AtomicInteger sequenceGenerator = new AtomicInteger(0);
    private final CRC32 crc32 = new CRC32();

    public static class Packet {
        public byte version;
        public byte type;
        public int sequence;
        public byte[] payload;

        public Packet(byte type, byte[] payload) {
            this.version = VERSION;
            this.type = type;
            this.payload = payload;
        }
    }

    public byte[] encode(Packet packet) {
        int totalLength = HEADER_SIZE + packet.payload.length + CHECKSUM_SIZE;
        ByteBuffer buffer = ByteBuffer.allocate(totalLength);

        // 写入头部
        buffer.putShort(MAGIC);
        buffer.put(packet.version);
        buffer.put(packet.type);
        buffer.putInt(sequenceGenerator.incrementAndGet());
        buffer.putInt(packet.payload.length);

        // 写入载荷
        buffer.put(packet.payload);

        // 计算并写入校验和
        crc32.reset();
        crc32.update(buffer.array(), 0, totalLength - CHECKSUM_SIZE);
        buffer.putInt((int) crc32.getValue());

        return buffer.array();
    }

    public Packet decode(byte[] data) throws ProtocolException {
        if (data.length < HEADER_SIZE + CHECKSUM_SIZE) {
            throw new ProtocolException("数据包太短");
        }

        ByteBuffer buffer = ByteBuffer.wrap(data);

        // 验证魔数
        short magic = buffer.getShort();
        if (magic != MAGIC) {
            throw new ProtocolException("无效的魔数: " + Integer.toHexString(magic));
        }

        // 读取头部
        byte version = buffer.get();
        byte type = buffer.get();
        int sequence = buffer.getInt();
        int payloadLength = buffer.getInt();

        // 验证长度
        if (payloadLength < 0 || payloadLength > data.length - HEADER_SIZE - CHECKSUM_SIZE) {
            throw new ProtocolException("无效的载荷长度");
        }

        // 读取载荷
        byte[] payload = new byte[payloadLength];
        buffer.get(payload);

        // 验证校验和
        int receivedChecksum = buffer.getInt();
        crc32.reset();
        crc32.update(data, 0, data.length - CHECKSUM_SIZE);
        int calculatedChecksum = (int) crc32.getValue();

        if (receivedChecksum != calculatedChecksum) {
            throw new ProtocolException("校验和不匹配");
        }

        Packet packet = new Packet(type, payload);
        packet.version = version;
        packet.sequence = sequence;
        return packet;
    }

    public static class ProtocolException extends Exception {
        public ProtocolException(String message) {
            super(message);
        }
    }
}

SSL/TLS 安全通信

9.1 SSL/TLS 握手过程

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                    TLS 1.2 握手过程                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│     Client                                        Server             │
│        │                                             │               │
│        │ ────── ClientHello ──────────────────────► │               │
│        │        (版本、随机数、支持的加密套件)        │               │
│        │                                             │               │
│        │ ◄────── ServerHello ────────────────────── │               │
│        │         (选定的版本和加密套件)              │               │
│        │                                             │               │
│        │ ◄────── Certificate ────────────────────── │               │
│        │         (服务器证书链)                      │               │
│        │                                             │               │
│        │ ◄────── ServerKeyExchange ─────────────── │               │
│        │         (DH参数,可选)                      │               │
│        │                                             │               │
│        │ ◄────── ServerHelloDone ───────────────── │               │
│        │                                             │               │
│        │ ────── ClientKeyExchange ─────────────────►│               │
│        │        (预主密钥)                           │               │
│        │                                             │               │
│        │ ────── ChangeCipherSpec ──────────────────►│               │
│        │                                             │               │
│        │ ────── Finished ──────────────────────────►│               │
│        │                                             │               │
│        │ ◄────── ChangeCipherSpec ─────────────────│               │
│        │                                             │               │
│        │ ◄────── Finished ─────────────────────────│               │
│        │                                             │               │
│        │◄═══════════ 加密通信 ═══════════════════►│               │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

9.2 SSLSocket 实现

java 复制代码
public class SecureSocketClient {
    private SSLSocket sslSocket;
    private SSLContext sslContext;

    /**
     * 初始化SSL上下文
     *
     * 三种验证模式:
     * 1. 系统CA验证(默认)
     * 2. 自签名证书(需要添加到信任库)
     * 3. 证书固定(Certificate Pinning)
     */
    public void initSSL(Context context) throws Exception {
        sslContext = SSLContext.getInstance("TLS");

        // 方式1:使用系统默认信任库
        // sslContext.init(null, null, null);

        // 方式2:自定义信任库(包含自签名证书)
        TrustManagerFactory tmf = createTrustManagerFactory(context);
        sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
    }

    private TrustManagerFactory createTrustManagerFactory(Context context)
            throws Exception {
        // 加载自定义证书
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream certInput = context.getAssets().open("server.crt");
        Certificate ca = cf.generateCertificate(certInput);
        certInput.close();

        // 创建包含该证书的KeyStore
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null, null);
        keyStore.setCertificateEntry("server", ca);

        // 创建TrustManager
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(
            TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(keyStore);

        return tmf;
    }

    public void connect(String host, int port) throws IOException {
        SSLSocketFactory factory = sslContext.getSocketFactory();
        sslSocket = (SSLSocket) factory.createSocket(host, port);

        // 配置SSL参数
        configureSSL(host);

        // 执行握手
        sslSocket.startHandshake();

        // 验证主机名
        verifyHostname(host);
    }

    private void configureSSL(String host) {
        // 设置支持的协议版本
        sslSocket.setEnabledProtocols(new String[]{"TLSv1.2", "TLSv1.3"});

        // 设置加密套件(排除弱加密)
        String[] strongCipherSuites = filterWeakCipherSuites(
            sslSocket.getSupportedCipherSuites());
        sslSocket.setEnabledCipherSuites(strongCipherSuites);

        // 启用SNI(Server Name Indication)
        SSLParameters params = sslSocket.getSSLParameters();
        params.setServerNames(Collections.singletonList(
            new SNIHostName(host)));
        sslSocket.setSSLParameters(params);
    }

    private String[] filterWeakCipherSuites(String[] suites) {
        return Arrays.stream(suites)
            .filter(s -> !s.contains("NULL"))
            .filter(s -> !s.contains("EXPORT"))
            .filter(s -> !s.contains("DES"))
            .filter(s -> !s.contains("RC4"))
            .filter(s -> !s.contains("MD5"))
            .toArray(String[]::new);
    }

    private void verifyHostname(String expectedHost) throws SSLPeerUnverifiedException {
        HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
        if (!verifier.verify(expectedHost, sslSocket.getSession())) {
            throw new SSLPeerUnverifiedException(
                "主机名验证失败: " + expectedHost);
        }
    }
}

9.3 证书固定(Certificate Pinning)

java 复制代码
/**
 * 证书固定:防止中间人攻击
 *
 * 原理:在客户端预置服务器证书的公钥或指纹,
 *       连接时验证服务器证书是否匹配。
 *
 * 固定方式:
 * 1. 固定证书:整个证书的SHA256
 * 2. 固定公钥:证书公钥的SHA256(推荐,证书续期不受影响)
 */
public class CertificatePinner {

    // 预置的公钥SHA256(Base64编码)
    private static final Set<String> PINNED_KEYS = new HashSet<>(Arrays.asList(
        "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",  // 主证书
        "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="   // 备用证书
    ));

    public TrustManager[] createPinningTrustManagers() {
        return new TrustManager[]{
            new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) {
                    // 客户端证书验证(如果需要)
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {
                    if (chain == null || chain.length == 0) {
                        throw new CertificateException("空证书链");
                    }

                    // 先进行标准验证
                    try {
                        TrustManagerFactory tmf = TrustManagerFactory.getInstance(
                            TrustManagerFactory.getDefaultAlgorithm());
                        tmf.init((KeyStore) null);

                        for (TrustManager tm : tmf.getTrustManagers()) {
                            if (tm instanceof X509TrustManager) {
                                ((X509TrustManager) tm).checkServerTrusted(chain, authType);
                            }
                        }
                    } catch (Exception e) {
                        throw new CertificateException("标准验证失败", e);
                    }

                    // 证书固定验证
                    boolean pinMatched = false;
                    for (X509Certificate cert : chain) {
                        String publicKeyHash = getPublicKeyHash(cert);
                        if (PINNED_KEYS.contains(publicKeyHash)) {
                            pinMatched = true;
                            break;
                        }
                    }

                    if (!pinMatched) {
                        throw new CertificateException("证书固定验证失败");
                    }
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            }
        };
    }

    private String getPublicKeyHash(X509Certificate cert) {
        try {
            byte[] publicKeyBytes = cert.getPublicKey().getEncoded();
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(publicKeyBytes);
            return "sha256/" + Base64.encodeToString(hash, Base64.NO_WRAP);
        } catch (Exception e) {
            return "";
        }
    }
}

性能优化

10.1 Buffer 池化

java 复制代码
/**
 * ByteBuffer对象池
 *
 * 避免频繁创建和销毁Buffer对象,减少GC压力
 */
public class ByteBufferPool {

    private static final int[] BUCKET_SIZES = {
        512, 1024, 2048, 4096, 8192, 16384, 32768, 65536
    };

    private final Map<Integer, Queue<ByteBuffer>> heapPools = new ConcurrentHashMap<>();
    private final Map<Integer, Queue<ByteBuffer>> directPools = new ConcurrentHashMap<>();
    private final AtomicInteger allocatedHeap = new AtomicInteger(0);
    private final AtomicInteger allocatedDirect = new AtomicInteger(0);

    private static final int MAX_POOL_SIZE = 64;  // 每个桶最多缓存64个

    private static final ByteBufferPool INSTANCE = new ByteBufferPool();

    public static ByteBufferPool getInstance() {
        return INSTANCE;
    }

    private ByteBufferPool() {
        for (int size : BUCKET_SIZES) {
            heapPools.put(size, new ConcurrentLinkedQueue<>());
            directPools.put(size, new ConcurrentLinkedQueue<>());
        }
    }

    /**
     * 获取合适大小的Buffer
     */
    public ByteBuffer acquire(int minCapacity, boolean direct) {
        int bucketSize = findBucketSize(minCapacity);
        Map<Integer, Queue<ByteBuffer>> pools = direct ? directPools : heapPools;
        Queue<ByteBuffer> pool = pools.get(bucketSize);

        ByteBuffer buffer = null;
        if (pool != null) {
            buffer = pool.poll();
        }

        if (buffer == null) {
            // 池中没有可用的,创建新的
            buffer = direct
                ? ByteBuffer.allocateDirect(bucketSize)
                : ByteBuffer.allocate(bucketSize);

            if (direct) {
                allocatedDirect.incrementAndGet();
            } else {
                allocatedHeap.incrementAndGet();
            }
        }

        buffer.clear();
        buffer.limit(minCapacity);  // 限制到请求的大小
        return buffer;
    }

    /**
     * 归还Buffer到池中
     */
    public void release(ByteBuffer buffer) {
        if (buffer == null) return;

        int bucketSize = findBucketSize(buffer.capacity());
        if (bucketSize != buffer.capacity()) {
            // 不是标准大小,不回收
            return;
        }

        Map<Integer, Queue<ByteBuffer>> pools =
            buffer.isDirect() ? directPools : heapPools;
        Queue<ByteBuffer> pool = pools.get(bucketSize);

        if (pool != null && pool.size() < MAX_POOL_SIZE) {
            buffer.clear();
            pool.offer(buffer);
        }
        // 否则让GC回收
    }

    private int findBucketSize(int size) {
        for (int bucketSize : BUCKET_SIZES) {
            if (bucketSize >= size) {
                return bucketSize;
            }
        }
        // 超过最大桶,返回请求的大小(向上取整到4KB对齐)
        return ((size + 4095) / 4096) * 4096;
    }

    public String getStats() {
        return String.format(
            "Pool Stats: Heap=%d, Direct=%d",
            allocatedHeap.get(), allocatedDirect.get());
    }
}

10.2 零拷贝技术

java 复制代码
/**
 * 零拷贝原理:
 *
 * 传统拷贝路径(4次拷贝):
 * ┌────────┐    ┌────────┐    ┌────────┐    ┌────────┐    ┌────────┐
 * │  磁盘  │ ─► │ 内核   │ ─► │  用户  │ ─► │ Socket │ ─► │  网卡  │
 * │        │    │ Buffer │    │ Buffer │    │ Buffer │    │        │
 * └────────┘    └────────┘    └────────┘    └────────┘    └────────┘
 *            DMA拷贝      CPU拷贝      CPU拷贝      DMA拷贝
 *
 * 零拷贝路径(2次DMA拷贝):
 * ┌────────┐    ┌────────┐                              ┌────────┐
 * │  磁盘  │ ─► │ 内核   │ ─────────────────────────► │  网卡  │
 * │        │    │ Buffer │    sendfile() / mmap()       │        │
 * └────────┘    └────────┘                              └────────┘
 *            DMA拷贝                                  DMA拷贝
 */
public class ZeroCopyDemo {

    /**
     * 使用FileChannel.transferTo实现零拷贝
     */
    public void sendFile(String filePath, SocketChannel socketChannel)
            throws IOException {
        try (FileChannel fileChannel = FileChannel.open(
                Paths.get(filePath), StandardOpenOption.READ)) {

            long fileSize = fileChannel.size();
            long transferred = 0;

            while (transferred < fileSize) {
                // transferTo内部使用sendfile系统调用
                long bytes = fileChannel.transferTo(
                    transferred,
                    fileSize - transferred,
                    socketChannel);

                if (bytes <= 0) {
                    break;
                }
                transferred += bytes;
            }
        }
    }

    /**
     * 使用内存映射文件
     */
    public void sendFileWithMmap(String filePath, SocketChannel socketChannel)
            throws IOException {
        try (FileChannel fileChannel = FileChannel.open(
                Paths.get(filePath), StandardOpenOption.READ)) {

            long fileSize = fileChannel.size();

            // 映射文件到内存
            MappedByteBuffer mappedBuffer = fileChannel.map(
                FileChannel.MapMode.READ_ONLY, 0, fileSize);

            // 直接发送映射的缓冲区
            while (mappedBuffer.hasRemaining()) {
                socketChannel.write(mappedBuffer);
            }
        }
    }
}

10.3 连接池设计

java 复制代码
/**
 * Socket连接池
 *
 * 适用于短连接场景,复用TCP连接减少握手开销
 */
public class SocketPool {

    private final String host;
    private final int port;
    private final int maxConnections;
    private final long maxIdleTime;
    private final long connectionTimeout;

    private final Queue<PooledSocket> idleConnections = new ConcurrentLinkedQueue<>();
    private final AtomicInteger activeCount = new AtomicInteger(0);
    private final AtomicInteger totalCreated = new AtomicInteger(0);

    private final ScheduledExecutorService cleaner =
        Executors.newSingleThreadScheduledExecutor();

    public SocketPool(String host, int port, int maxConnections,
            long maxIdleTime, long connectionTimeout) {
        this.host = host;
        this.port = port;
        this.maxConnections = maxConnections;
        this.maxIdleTime = maxIdleTime;
        this.connectionTimeout = connectionTimeout;

        // 定期清理空闲连接
        cleaner.scheduleWithFixedDelay(
            this::cleanIdleConnections, 30, 30, TimeUnit.SECONDS);
    }

    /**
     * 获取连接
     */
    public PooledSocket acquire() throws IOException {
        // 尝试从空闲池获取
        PooledSocket pooled = idleConnections.poll();
        while (pooled != null) {
            if (pooled.isValid()) {
                activeCount.incrementAndGet();
                return pooled;
            }
            // 连接已失效,关闭它
            pooled.closeQuietly();
            pooled = idleConnections.poll();
        }

        // 空闲池没有可用连接,检查是否可以创建新的
        if (activeCount.get() + idleConnections.size() >= maxConnections) {
            throw new IOException("连接池已满");
        }

        // 创建新连接
        Socket socket = new Socket();
        socket.connect(new InetSocketAddress(host, port), (int) connectionTimeout);
        socket.setSoTimeout(30000);
        socket.setTcpNoDelay(true);

        totalCreated.incrementAndGet();
        activeCount.incrementAndGet();

        return new PooledSocket(socket, this);
    }

    /**
     * 归还连接
     */
    void release(PooledSocket pooled) {
        activeCount.decrementAndGet();

        if (pooled.isValid() && idleConnections.size() < maxConnections) {
            pooled.lastUsedTime = System.currentTimeMillis();
            idleConnections.offer(pooled);
        } else {
            pooled.closeQuietly();
        }
    }

    private void cleanIdleConnections() {
        long now = System.currentTimeMillis();
        Iterator<PooledSocket> iterator = idleConnections.iterator();

        while (iterator.hasNext()) {
            PooledSocket pooled = iterator.next();
            if (now - pooled.lastUsedTime > maxIdleTime || !pooled.isValid()) {
                iterator.remove();
                pooled.closeQuietly();
            }
        }
    }

    public void shutdown() {
        cleaner.shutdown();
        PooledSocket pooled;
        while ((pooled = idleConnections.poll()) != null) {
            pooled.closeQuietly();
        }
    }

    public static class PooledSocket implements Closeable {
        private final Socket socket;
        private final SocketPool pool;
        private long lastUsedTime;
        private boolean closed = false;

        PooledSocket(Socket socket, SocketPool pool) {
            this.socket = socket;
            this.pool = pool;
            this.lastUsedTime = System.currentTimeMillis();
        }

        public Socket getSocket() {
            return socket;
        }

        public InputStream getInputStream() throws IOException {
            return socket.getInputStream();
        }

        public OutputStream getOutputStream() throws IOException {
            return socket.getOutputStream();
        }

        boolean isValid() {
            return !closed && socket.isConnected() && !socket.isClosed();
        }

        @Override
        public void close() {
            if (!closed) {
                closed = true;
                pool.release(this);
            }
        }

        void closeQuietly() {
            try {
                socket.close();
            } catch (IOException ignored) {
            }
        }
    }
}

实战架构设计

11.1 完整的Socket客户端架构

java 复制代码
/**
 * 生产级Socket客户端架构
 *
 * ┌──────────────────────────────────────────────────────────────────┐
 * │                        Application Layer                         │
 * │                    (Business Logic / UI)                         │
 * └───────────────────────────┬──────────────────────────────────────┘
 *                             │
 * ┌───────────────────────────▼──────────────────────────────────────┐
 * │                      SocketClient (API)                          │
 * │  • connect() / disconnect()                                      │
 * │  • send(message) / addListener()                                 │
 * └───────────────────────────┬──────────────────────────────────────┘
 *                             │
 * ┌───────────────────────────▼──────────────────────────────────────┐
 * │                     Connection Manager                           │
 * │  • 状态管理 (State Machine)                                      │
 * │  • 重连策略 (Exponential Backoff)                                │
 * │  • 心跳管理 (Smart Heartbeat)                                    │
 * └───────────────────────────┬──────────────────────────────────────┘
 *                             │
 * ┌───────────────────────────▼──────────────────────────────────────┐
 * │                     Protocol Layer                               │
 * │  • 消息编解码 (Codec)                                            │
 * │  • 粘包处理 (Frame Decoder)                                      │
 * │  • 序列化 (JSON/Protobuf/Custom)                                 │
 * └───────────────────────────┬──────────────────────────────────────┘
 *                             │
 * ┌───────────────────────────▼──────────────────────────────────────┐
 * │                     Transport Layer                              │
 * │  • TCP/SSL Socket                                                │
 * │  • NIO Channel                                                   │
 * │  • I/O Thread                                                    │
 * └──────────────────────────────────────────────────────────────────┘
 */
public class SocketClient {

    private final SocketConfig config;
    private final ConnectionManager connectionManager;
    private final ProtocolCodec codec;
    private final MessageDispatcher dispatcher;
    private final ExecutorService callbackExecutor;

    private volatile Socket socket;
    private volatile DataInputStream inputStream;
    private volatile DataOutputStream outputStream;
    private volatile boolean running = false;

    private final List<SocketListener> listeners = new CopyOnWriteArrayList<>();

    public SocketClient(SocketConfig config) {
        this.config = config;
        this.codec = new ProtocolCodec();
        this.dispatcher = new MessageDispatcher();
        this.callbackExecutor = Executors.newSingleThreadExecutor(r -> {
            Thread t = new Thread(r, "Socket-Callback");
            t.setDaemon(true);
            return t;
        });
        this.connectionManager = new ConnectionManager(this, config);
    }

    public void connect() {
        connectionManager.connect();
    }

    public void disconnect() {
        connectionManager.disconnect();
    }

    public void send(Message message, SendCallback callback) {
        if (!isConnected()) {
            if (callback != null) {
                callback.onFailure(new IOException("未连接"));
            }
            return;
        }

        try {
            byte[] data = codec.encode(message);
            synchronized (outputStream) {
                outputStream.write(data);
                outputStream.flush();
            }
            if (callback != null) {
                callback.onSuccess();
            }
        } catch (IOException e) {
            if (callback != null) {
                callback.onFailure(e);
            }
            connectionManager.onConnectionLost(e);
        }
    }

    public <T> void sendWithAck(Message message, Class<T> responseType,
            ResponseCallback<T> callback, long timeoutMs) {
        dispatcher.registerCallback(message.getSequence(), responseType,
            callback, timeoutMs);
        send(message, new SendCallback() {
            @Override
            public void onSuccess() {
                // 等待响应
            }

            @Override
            public void onFailure(Exception e) {
                dispatcher.cancelCallback(message.getSequence());
                callback.onFailure(e);
            }
        });
    }

    // 内部连接实现
    void doConnect() throws IOException {
        socket = new Socket();
        socket.setTcpNoDelay(config.isTcpNoDelay());
        socket.setKeepAlive(config.isKeepAlive());
        socket.setSoTimeout(config.getReadTimeout());
        socket.setReceiveBufferSize(config.getReceiveBufferSize());
        socket.setSendBufferSize(config.getSendBufferSize());

        InetSocketAddress address = new InetSocketAddress(
            config.getHost(), config.getPort());
        socket.connect(address, config.getConnectTimeout());

        inputStream = new DataInputStream(
            new BufferedInputStream(socket.getInputStream(), 8192));
        outputStream = new DataOutputStream(
            new BufferedOutputStream(socket.getOutputStream(), 8192));

        running = true;
        startReadLoop();
    }

    void doDisconnect() {
        running = false;
        closeQuietly(inputStream);
        closeQuietly(outputStream);
        closeQuietly(socket);
    }

    private void startReadLoop() {
        new Thread(() -> {
            while (running) {
                try {
                    byte[] data = codec.readFrame(inputStream);
                    if (data != null) {
                        Message message = codec.decode(data);
                        handleMessage(message);
                    }
                } catch (SocketTimeoutException e) {
                    // 读取超时,继续
                } catch (IOException e) {
                    if (running) {
                        connectionManager.onConnectionLost(e);
                    }
                    break;
                }
            }
        }, "Socket-Reader").start();
    }

    private void handleMessage(Message message) {
        // 先检查是否是等待响应的消息
        if (dispatcher.dispatch(message)) {
            return;
        }

        // 通知监听器
        callbackExecutor.execute(() -> {
            for (SocketListener listener : listeners) {
                try {
                    listener.onMessage(message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public boolean isConnected() {
        return socket != null && socket.isConnected() && !socket.isClosed();
    }

    public void addListener(SocketListener listener) {
        listeners.add(listener);
    }

    public void removeListener(SocketListener listener) {
        listeners.remove(listener);
    }

    private void closeQuietly(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException ignored) {
            }
        }
    }

    // 接口定义
    public interface SocketListener {
        void onConnected();
        void onDisconnected(Throwable cause);
        void onMessage(Message message);
    }

    public interface SendCallback {
        void onSuccess();
        void onFailure(Exception e);
    }

    public interface ResponseCallback<T> {
        void onSuccess(T response);
        void onFailure(Exception e);
    }
}

11.2 消息分发器

java 复制代码
/**
 * 消息分发器:处理请求-响应模式
 */
public class MessageDispatcher {

    private final Map<Integer, PendingRequest<?>> pendingRequests =
        new ConcurrentHashMap<>();
    private final ScheduledExecutorService timeoutScheduler =
        Executors.newSingleThreadScheduledExecutor();

    public <T> void registerCallback(int sequence, Class<T> responseType,
            SocketClient.ResponseCallback<T> callback, long timeoutMs) {
        PendingRequest<T> request = new PendingRequest<>(responseType, callback);
        pendingRequests.put(sequence, request);

        // 设置超时
        request.timeoutFuture = timeoutScheduler.schedule(() -> {
            PendingRequest<?> removed = pendingRequests.remove(sequence);
            if (removed != null) {
                removed.callback.onFailure(new TimeoutException("响应超时"));
            }
        }, timeoutMs, TimeUnit.MILLISECONDS);
    }

    public void cancelCallback(int sequence) {
        PendingRequest<?> request = pendingRequests.remove(sequence);
        if (request != null && request.timeoutFuture != null) {
            request.timeoutFuture.cancel(false);
        }
    }

    @SuppressWarnings("unchecked")
    public boolean dispatch(Message message) {
        PendingRequest<?> request = pendingRequests.remove(message.getSequence());
        if (request == null) {
            return false;
        }

        if (request.timeoutFuture != null) {
            request.timeoutFuture.cancel(false);
        }

        try {
            Object response = parseResponse(message, request.responseType);
            ((SocketClient.ResponseCallback<Object>) request.callback).onSuccess(response);
        } catch (Exception e) {
            request.callback.onFailure(e);
        }

        return true;
    }

    private <T> T parseResponse(Message message, Class<T> type) {
        // 根据类型解析响应
        // 这里可以使用JSON、Protobuf等序列化方式
        return null;  // 简化示例
    }

    private static class PendingRequest<T> {
        final Class<T> responseType;
        final SocketClient.ResponseCallback<T> callback;
        ScheduledFuture<?> timeoutFuture;

        PendingRequest(Class<T> responseType, SocketClient.ResponseCallback<T> callback) {
            this.responseType = responseType;
            this.callback = callback;
        }
    }
}

调试与问题排查

12.1 常见问题诊断

java 复制代码
/**
 * Socket调试工具类
 */
public class SocketDiagnostics {

    /**
     * 诊断连接问题
     */
    public static DiagnosticResult diagnoseConnection(String host, int port) {
        DiagnosticResult result = new DiagnosticResult();

        // 1. DNS解析
        long dnsStart = System.currentTimeMillis();
        try {
            InetAddress[] addresses = InetAddress.getAllByName(host);
            result.dnsTime = System.currentTimeMillis() - dnsStart;
            result.resolvedAddresses = addresses;
            result.dnsSuccess = true;
        } catch (UnknownHostException e) {
            result.dnsTime = System.currentTimeMillis() - dnsStart;
            result.dnsError = e.getMessage();
            return result;
        }

        // 2. TCP连接
        for (InetAddress address : result.resolvedAddresses) {
            Socket socket = new Socket();
            long connectStart = System.currentTimeMillis();
            try {
                socket.connect(new InetSocketAddress(address, port), 5000);
                result.connectTime = System.currentTimeMillis() - connectStart;
                result.connectedAddress = address;
                result.connectSuccess = true;

                // 3. 获取本地端口和地址
                result.localAddress = socket.getLocalAddress();
                result.localPort = socket.getLocalPort();

                socket.close();
                break;
            } catch (IOException e) {
                result.connectTime = System.currentTimeMillis() - connectStart;
                result.connectError = e.getMessage();
            } finally {
                try { socket.close(); } catch (IOException ignored) {}
            }
        }

        return result;
    }

    /**
     * 测量RTT
     */
    public static long measureRTT(Socket socket, int samples) throws IOException {
        DataOutputStream out = new DataOutputStream(socket.getOutputStream());
        DataInputStream in = new DataInputStream(socket.getInputStream());

        long totalRtt = 0;
        for (int i = 0; i < samples; i++) {
            long start = System.nanoTime();

            // 发送Ping
            out.writeByte(0x01);  // Ping type
            out.writeLong(start);
            out.flush();

            // 等待Pong
            byte type = in.readByte();
            long sentTime = in.readLong();

            long rtt = System.nanoTime() - start;
            totalRtt += rtt;

            try { Thread.sleep(100); } catch (InterruptedException ignored) {}
        }

        return totalRtt / samples / 1_000_000;  // 返回平均RTT(毫秒)
    }

    /**
     * 获取Socket状态信息
     */
    public static String getSocketInfo(Socket socket) {
        if (socket == null) return "Socket is null";

        StringBuilder sb = new StringBuilder();
        sb.append("=== Socket Info ===\n");
        sb.append("Connected: ").append(socket.isConnected()).append("\n");
        sb.append("Closed: ").append(socket.isClosed()).append("\n");
        sb.append("Bound: ").append(socket.isBound()).append("\n");
        sb.append("Input Shutdown: ").append(socket.isInputShutdown()).append("\n");
        sb.append("Output Shutdown: ").append(socket.isOutputShutdown()).append("\n");
        sb.append("Local: ").append(socket.getLocalSocketAddress()).append("\n");
        sb.append("Remote: ").append(socket.getRemoteSocketAddress()).append("\n");

        try {
            sb.append("TCP_NODELAY: ").append(socket.getTcpNoDelay()).append("\n");
            sb.append("SO_KEEPALIVE: ").append(socket.getKeepAlive()).append("\n");
            sb.append("SO_TIMEOUT: ").append(socket.getSoTimeout()).append("ms\n");
            sb.append("SO_RCVBUF: ").append(socket.getReceiveBufferSize()).append("\n");
            sb.append("SO_SNDBUF: ").append(socket.getSendBufferSize()).append("\n");
            sb.append("SO_LINGER: ").append(socket.getSoLinger()).append("\n");
        } catch (SocketException e) {
            sb.append("Error getting socket options: ").append(e.getMessage());
        }

        return sb.toString();
    }

    public static class DiagnosticResult {
        public boolean dnsSuccess;
        public long dnsTime;
        public String dnsError;
        public InetAddress[] resolvedAddresses;

        public boolean connectSuccess;
        public long connectTime;
        public String connectError;
        public InetAddress connectedAddress;
        public InetAddress localAddress;
        public int localPort;

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("=== Connection Diagnostic ===\n");
            sb.append("DNS: ").append(dnsSuccess ? "OK" : "FAILED");
            sb.append(" (").append(dnsTime).append("ms)");
            if (!dnsSuccess) sb.append(" Error: ").append(dnsError);
            sb.append("\n");

            if (resolvedAddresses != null) {
                sb.append("Resolved: ");
                for (InetAddress addr : resolvedAddresses) {
                    sb.append(addr.getHostAddress()).append(" ");
                }
                sb.append("\n");
            }

            sb.append("Connect: ").append(connectSuccess ? "OK" : "FAILED");
            sb.append(" (").append(connectTime).append("ms)");
            if (!connectSuccess) sb.append(" Error: ").append(connectError);
            sb.append("\n");

            if (connectSuccess) {
                sb.append("Connected to: ").append(connectedAddress).append("\n");
                sb.append("Local: ").append(localAddress).append(":").append(localPort);
            }

            return sb.toString();
        }
    }
}

12.2 网络日志记录

java 复制代码
/**
 * Socket数据包日志记录器
 */
public class PacketLogger {

    private static final String TAG = "PacketLogger";
    private static final SimpleDateFormat DATE_FORMAT =
        new SimpleDateFormat("HH:mm:ss.SSS", Locale.US);

    private final boolean enabled;
    private final int maxPayloadLog;
    private final File logFile;
    private PrintWriter writer;

    public PacketLogger(boolean enabled, int maxPayloadLog, File logFile) {
        this.enabled = enabled;
        this.maxPayloadLog = maxPayloadLog;
        this.logFile = logFile;

        if (enabled && logFile != null) {
            try {
                writer = new PrintWriter(new FileWriter(logFile, true));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void logSend(byte[] data) {
        if (!enabled) return;
        log(">>>", data);
    }

    public void logReceive(byte[] data) {
        if (!enabled) return;
        log("<<<", data);
    }

    private void log(String direction, byte[] data) {
        String timestamp = DATE_FORMAT.format(new Date());
        String hex = bytesToHex(data, maxPayloadLog);
        String ascii = bytesToAscii(data, maxPayloadLog);

        String logLine = String.format("%s %s [%d bytes] %s | %s",
            timestamp, direction, data.length, hex, ascii);

        Log.d(TAG, logLine);

        if (writer != null) {
            writer.println(logLine);
            writer.flush();
        }
    }

    private String bytesToHex(byte[] data, int maxLen) {
        StringBuilder sb = new StringBuilder();
        int len = Math.min(data.length, maxLen);
        for (int i = 0; i < len; i++) {
            sb.append(String.format("%02X ", data[i]));
        }
        if (data.length > maxLen) {
            sb.append("...");
        }
        return sb.toString();
    }

    private String bytesToAscii(byte[] data, int maxLen) {
        StringBuilder sb = new StringBuilder();
        int len = Math.min(data.length, maxLen);
        for (int i = 0; i < len; i++) {
            char c = (char) data[i];
            sb.append(c >= 32 && c < 127 ? c : '.');
        }
        if (data.length > maxLen) {
            sb.append("...");
        }
        return sb.toString();
    }

    public void close() {
        if (writer != null) {
            writer.close();
        }
    }
}

12.3 常见问题与解决方案

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                    常见问题排查指南                                   │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  问题1:Connection refused                                          │
│  ─────────────────────────────────────                              │
│  原因:                                                              │
│  • 服务端未启动                                                      │
│  • 端口错误                                                          │
│  • 防火墙阻止                                                        │
│  排查:                                                              │
│  • telnet host port                                                 │
│  • netstat -an | grep port                                          │
│                                                                      │
│  问题2:Connection timed out                                        │
│  ─────────────────────────────────────                              │
│  原因:                                                              │
│  • 网络不通                                                          │
│  • 服务端不可达                                                      │
│  • 中间网络设备丢包                                                  │
│  排查:                                                              │
│  • ping host                                                        │
│  • traceroute host                                                  │
│                                                                      │
│  问题3:Connection reset                                            │
│  ─────────────────────────────────────                              │
│  原因:                                                              │
│  • 服务端异常关闭                                                    │
│  • 服务端发送RST                                                     │
│  • 网络设备重置连接                                                  │
│  排查:                                                              │
│  • 检查服务端日志                                                    │
│  • 抓包分析                                                          │
│                                                                      │
│  问题4:SocketTimeoutException (read)                               │
│  ─────────────────────────────────────                              │
│  原因:                                                              │
│  • 服务端处理慢                                                      │
│  • 网络延迟高                                                        │
│  • 超时设置过短                                                      │
│  解决:                                                              │
│  • 增加SO_TIMEOUT                                                   │
│  • 优化服务端性能                                                    │
│                                                                      │
│  问题5:Broken pipe                                                 │
│  ─────────────────────────────────────                              │
│  原因:                                                              │
│  • 向已关闭的连接写数据                                              │
│  • 对端已关闭读端                                                    │
│  解决:                                                              │
│  • 发送前检查连接状态                                                │
│  • 捕获异常并重连                                                    │
│                                                                      │
│  问题6:Too many open files                                         │
│  ─────────────────────────────────────                              │
│  原因:                                                              │
│  • Socket未正确关闭                                                  │
│  • 文件描述符泄漏                                                    │
│  解决:                                                              │
│  • 确保finally中关闭Socket                                          │
│  • 使用try-with-resources                                           │
│  • ulimit -n 增加限制                                               │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

总结

本文深入探讨了Android平台Socket编程的方方面面:

  1. 基础原理:从内核Socket结构到TCP状态机,理解底层机制
  2. 平台特性:Android特有的限制和解决方案
  3. 协议设计:心跳、重连、粘包处理等工程实践
  4. 安全通信:SSL/TLS和证书固定
  5. 性能优化:Buffer池化、零拷贝、连接池
  6. 架构设计:生产级Socket客户端的完整实现
  7. 问题排查:常见问题的诊断和解决

Socket编程看似简单,实则涉及网络协议、操作系统、并发编程等多个领域。希望本文能帮助读者建立完整的知识体系,在实际项目中游刃有余。


作者注:本文代码示例仅用于说明原理,生产环境请根据实际需求进行完善和测试。下期将发布socket通信框架,敬请期待

相关推荐
毕设源码-朱学姐8 小时前
【开题答辩全过程】以 基于Android的留守儿童贫困资助管理系统的设计与实现为例,包含答辩的问题和答案
android
愤怒的代码8 小时前
深入理解 IdleHandler:从启动优化到内存管理
android·架构·kotlin
恋猫de小郭8 小时前
OpenAI :你不需要跨平台框架,只需要在 Android 和 iOS 上使用 Codex
android·前端·openai
路在脚下,梦在心里9 小时前
net学习总结
android·学习
走在路上的菜鸟9 小时前
Android学Dart学习笔记第二十节 类-枚举
android·笔记·学习·flutter
星光一影9 小时前
合成植物大战僵尸 安卓原生APP Cocos游戏 支持Sigmob
android·游戏·php·html5·web app
2501_915918419 小时前
iOS 项目中证书管理常见的协作问题
android·ios·小程序·https·uni-app·iphone·webview
allk559 小时前
Android ANR 深度起底:从系统埋雷机制到全链路治理体系
android
满天星83035779 小时前
【Linux】信号(下)
android·linux·运维·服务器·开发语言·性能优化