网络编程基础 #

一、网络通信概述 #

1.1 网络协议栈 #

text
┌─────────────────────────────────────────────────────────┐
│                    TCP/IP协议栈                          │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  应用层:HTTP, MQTT, CoAP, WebSocket                    │
│  ─────────────────────────────────────────────          │
│  传输层:TCP (可靠), UDP (快速)                          │
│  ─────────────────────────────────────────────          │
│  网络层:IP (寻址, 路由)                                 │
│  ─────────────────────────────────────────────          │
│  链路层:Ethernet, WiFi                                 │
│                                                         │
└─────────────────────────────────────────────────────────┘

1.2 TCP vs UDP #

特性 TCP UDP
连接 面向连接 无连接
可靠性 可靠传输 尽力传输
顺序 有序 无序
速度 较慢 较快
应用 文件传输、控制 视频流、传感器数据

1.3 端口号 #

端口范围 用途
0-1023 知名端口(系统保留)
1024-49151 注册端口
49152-65535 动态/私有端口

二、TCP Socket编程 #

2.1 TCP服务器 #

java
package com.example.network;

import java.io.*;
import java.net.*;
import java.util.concurrent.*;
import java.util.function.Consumer;

public class TCPServer {

    private final int port;
    private ServerSocket serverSocket;
    private volatile boolean running = false;
    private final ExecutorService executor = Executors.newCachedThreadPool();
    private final Map<String, ClientHandler> clients = new ConcurrentHashMap<>();
    private Consumer<String> messageHandler;
    private Consumer<String> connectHandler;
    private Consumer<String> disconnectHandler;
    
    public TCPServer(int port) {
        this.port = port;
    }
    
    public void start() throws IOException {
        serverSocket = new ServerSocket(port);
        running = true;
        
        System.out.println("TCP服务器启动,端口: " + port);
        
        executor.submit(() -> {
            while (running) {
                try {
                    Socket clientSocket = serverSocket.accept();
                    ClientHandler handler = new ClientHandler(clientSocket);
                    clients.put(handler.getId(), handler);
                    executor.submit(handler);
                    
                    if (connectHandler != null) {
                        connectHandler.accept(handler.getId());
                    }
                } catch (IOException e) {
                    if (running) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }
    
    public void stop() {
        running = false;
        
        for (ClientHandler handler : clients.values()) {
            handler.close();
        }
        clients.clear();
        
        try {
            if (serverSocket != null) {
                serverSocket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        executor.shutdown();
    }
    
    public void send(String clientId, String message) {
        ClientHandler handler = clients.get(clientId);
        if (handler != null) {
            handler.send(message);
        }
    }
    
    public void broadcast(String message) {
        for (ClientHandler handler : clients.values()) {
            handler.send(message);
        }
    }
    
    public void onMessage(Consumer<String> handler) {
        this.messageHandler = handler;
    }
    
    public void onConnect(Consumer<String> handler) {
        this.connectHandler = handler;
    }
    
    public void onDisconnect(Consumer<String> handler) {
        this.disconnectHandler = handler;
    }
    
    private class ClientHandler implements Runnable {
        private final Socket socket;
        private final String id;
        private BufferedReader reader;
        private PrintWriter writer;
        
        public ClientHandler(Socket socket) {
            this.socket = socket;
            this.id = socket.getRemoteSocketAddress().toString();
            
            try {
                reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                writer = new PrintWriter(socket.getOutputStream(), true);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        @Override
        public void run() {
            try {
                String line;
                while ((line = reader.readLine()) != null) {
                    if (messageHandler != null) {
                        messageHandler.accept(id + ":" + line);
                    }
                }
            } catch (IOException e) {
                // 客户端断开连接
            } finally {
                close();
                clients.remove(id);
                
                if (disconnectHandler != null) {
                    disconnectHandler.accept(id);
                }
            }
        }
        
        public void send(String message) {
            if (writer != null) {
                writer.println(message);
            }
        }
        
        public void close() {
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        public String getId() {
            return id;
        }
    }
    
    public static void main(String[] args) throws IOException {
        TCPServer server = new TCPServer(8080);
        
        server.onConnect(id -> System.out.println("客户端连接: " + id));
        server.onDisconnect(id -> System.out.println("客户端断开: " + id));
        server.onMessage(msg -> {
            System.out.println("收到消息: " + msg);
            server.broadcast("Echo: " + msg);
        });
        
        server.start();
        
        Runtime.getRuntime().addShutdownHook(new Thread(server::stop));
    }
}

2.2 TCP客户端 #

java
package com.example.network;

import java.io.*;
import java.net.*;
import java.util.function.Consumer;

public class TCPClient {

    private final String host;
    private final int port;
    private Socket socket;
    private BufferedReader reader;
    private PrintWriter writer;
    private volatile boolean connected = false;
    private Consumer<String> messageHandler;
    
    public TCPClient(String host, int port) {
        this.host = host;
        this.port = port;
    }
    
    public void connect() throws IOException {
        socket = new Socket(host, port);
        reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        writer = new PrintWriter(socket.getOutputStream(), true);
        connected = true;
        
        new Thread(() -> {
            try {
                String line;
                while (connected && (line = reader.readLine()) != null) {
                    if (messageHandler != null) {
                        messageHandler.accept(line);
                    }
                }
            } catch (IOException e) {
                if (connected) {
                    e.printStackTrace();
                }
            } finally {
                connected = false;
            }
        }).start();
        
        System.out.println("已连接到 " + host + ":" + port);
    }
    
    public void disconnect() {
        connected = false;
        try {
            if (socket != null) {
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public void send(String message) {
        if (writer != null && connected) {
            writer.println(message);
        }
    }
    
    public void onMessage(Consumer<String> handler) {
        this.messageHandler = handler;
    }
    
    public boolean isConnected() {
        return connected;
    }
    
    public static void main(String[] args) throws IOException, InterruptedException {
        TCPClient client = new TCPClient("localhost", 8080);
        
        client.onMessage(msg -> System.out.println("收到: " + msg));
        
        client.connect();
        
        for (int i = 0; i < 10; i++) {
            client.send("Hello " + i);
            Thread.sleep(1000);
        }
        
        client.disconnect();
    }
}

三、UDP编程 #

3.1 UDP服务器 #

java
package com.example.network;

import java.net.*;
import java.util.concurrent.*;
import java.util.function.BiConsumer;

public class UDPServer {

    private final int port;
    private DatagramSocket socket;
    private volatile boolean running = false;
    private final ExecutorService executor = Executors.newSingleThreadExecutor();
    private BiConsumer<SocketAddress, byte[]> messageHandler;
    
    public UDPServer(int port) {
        this.port = port;
    }
    
    public void start() throws SocketException {
        socket = new DatagramSocket(port);
        running = true;
        
        System.out.println("UDP服务器启动,端口: " + port);
        
        executor.submit(() -> {
            byte[] buffer = new byte[1024];
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
            
            while (running) {
                try {
                    socket.receive(packet);
                    
                    byte[] data = new byte[packet.getLength()];
                    System.arraycopy(packet.getData(), 0, data, 0, packet.getLength());
                    
                    if (messageHandler != null) {
                        messageHandler.accept(packet.getSocketAddress(), data);
                    }
                } catch (IOException e) {
                    if (running) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }
    
    public void stop() {
        running = false;
        if (socket != null) {
            socket.close();
        }
        executor.shutdown();
    }
    
    public void send(SocketAddress address, byte[] data) throws IOException {
        DatagramPacket packet = new DatagramPacket(data, data.length, address);
        socket.send(packet);
    }
    
    public void send(SocketAddress address, String message) throws IOException {
        send(address, message.getBytes());
    }
    
    public void onMessage(BiConsumer<SocketAddress, byte[]> handler) {
        this.messageHandler = handler;
    }
    
    public static void main(String[] args) throws SocketException, InterruptedException {
        UDPServer server = new UDPServer(9090);
        
        server.onMessage((address, data) -> {
            String message = new String(data);
            System.out.println("收到来自 " + address + ": " + message);
            
            try {
                server.send(address, "Echo: " + message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        
        server.start();
        
        Thread.sleep(60000);
        server.stop();
    }
}

3.2 UDP客户端 #

java
package com.example.network;

import java.net.*;
import java.util.function.Consumer;

public class UDPClient {

    private final String host;
    private final int port;
    private DatagramSocket socket;
    private InetSocketAddress serverAddress;
    private volatile boolean running = false;
    private Consumer<byte[]> responseHandler;
    
    public UDPClient(String host, int port) {
        this.host = host;
        this.port = port;
    }
    
    public void connect() throws SocketException {
        socket = new DatagramSocket();
        serverAddress = new InetSocketAddress(host, port);
        running = true;
        
        new Thread(() -> {
            byte[] buffer = new byte[1024];
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
            
            while (running) {
                try {
                    socket.receive(packet);
                    
                    byte[] data = new byte[packet.getLength()];
                    System.arraycopy(packet.getData(), 0, data, 0, packet.getLength());
                    
                    if (responseHandler != null) {
                        responseHandler.accept(data);
                    }
                } catch (IOException e) {
                    if (running) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
    
    public void disconnect() {
        running = false;
        if (socket != null) {
            socket.close();
        }
    }
    
    public void send(byte[] data) throws IOException {
        DatagramPacket packet = new DatagramPacket(data, data.length, serverAddress);
        socket.send(packet);
    }
    
    public void send(String message) throws IOException {
        send(message.getBytes());
    }
    
    public void onResponse(Consumer<byte[]> handler) {
        this.responseHandler = handler;
    }
    
    public static void main(String[] args) throws IOException, InterruptedException {
        UDPClient client = new UDPClient("localhost", 9090);
        
        client.onResponse(data -> System.out.println("收到响应: " + new String(data)));
        
        client.connect();
        
        for (int i = 0; i < 10; i++) {
            client.send("Hello UDP " + i);
            Thread.sleep(1000);
        }
        
        client.disconnect();
    }
}

四、网络工具类 #

4.1 网络信息获取 #

java
package com.example.network;

import java.net.*;
import java.util.*;

public class NetworkUtils {

    public static String getLocalIPAddress() throws SocketException {
        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
        
        while (interfaces.hasMoreElements()) {
            NetworkInterface iface = interfaces.nextElement();
            
            if (iface.isLoopback() || !iface.isUp()) {
                continue;
            }
            
            Enumeration<InetAddress> addresses = iface.getInetAddresses();
            while (addresses.hasMoreElements()) {
                InetAddress addr = addresses.nextElement();
                
                if (addr instanceof Inet4Address && !addr.isLoopbackAddress()) {
                    return addr.getHostAddress();
                }
            }
        }
        
        return null;
    }
    
    public static List<String> getAllIPAddresses() throws SocketException {
        List<String> ips = new ArrayList<>();
        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
        
        while (interfaces.hasMoreElements()) {
            NetworkInterface iface = interfaces.nextElement();
            
            if (iface.isLoopback() || !iface.isUp()) {
                continue;
            }
            
            Enumeration<InetAddress> addresses = iface.getInetAddresses();
            while (addresses.hasMoreElements()) {
                InetAddress addr = addresses.nextElement();
                if (!addr.isLoopbackAddress()) {
                    ips.add(addr.getHostAddress());
                }
            }
        }
        
        return ips;
    }
    
    public static String getMACAddress() throws SocketException {
        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
        
        while (interfaces.hasMoreElements()) {
            NetworkInterface iface = interfaces.nextElement();
            
            if (iface.isLoopback() || !iface.isUp()) {
                continue;
            }
            
            byte[] mac = iface.getHardwareAddress();
            if (mac != null) {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < mac.length; i++) {
                    sb.append(String.format("%02X", mac[i]));
                    if (i < mac.length - 1) {
                        sb.append(":");
                    }
                }
                return sb.toString();
            }
        }
        
        return null;
    }
    
    public static boolean isPortAvailable(int port) {
        try (ServerSocket socket = new ServerSocket(port)) {
            socket.setReuseAddress(true);
            return true;
        } catch (IOException e) {
            return false;
        }
    }
    
    public static boolean isHostReachable(String host, int timeout) {
        try {
            InetAddress address = InetAddress.getByName(host);
            return address.isReachable(timeout);
        } catch (IOException e) {
            return false;
        }
    }
    
    public static String getHostname() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            return "unknown";
        }
    }
    
    public static void main(String[] args) throws SocketException {
        System.out.println("主机名: " + getHostname());
        System.out.println("本机IP: " + getLocalIPAddress());
        System.out.println("所有IP: " + getAllIPAddresses());
        System.out.println("MAC地址: " + getMACAddress());
        System.out.println("端口8080可用: " + isPortAvailable(8080));
        System.out.println("localhost可达: " + isHostReachable("localhost", 1000));
    }
}

五、总结 #

网络编程基础要点:

  1. 协议选择:根据应用需求选择TCP或UDP
  2. 资源管理:正确关闭Socket和网络资源
  3. 异常处理:处理网络中断和超时
  4. 线程安全:多线程环境下的数据同步
  5. 性能优化:使用NIO提高并发性能

下一章我们将学习MQTT协议,实现物联网消息通信。

最后更新:2026-03-27