| 特性 | TCP | UDP |
|---|---|---|
| 連接模式 | 面向連接(三次握手/四次揮手) | 無連接 |
| 可靠性 | 可靠、有序、無錯誤 | 不可靠、無序、可能丟包/重複 |
| 數據邊界 | 流模式(無邊界,需應用層協議分幀) | 數據報模式(保留邊界,<64KB) |
| 開銷 | 較大(連接建立/維護、確認重傳) | 極小 |
| 適用場景 | 文件傳輸、遠程登錄、HTTP、數據庫連接 | 視頻會議、直播、DNS、即時遊戲、廣播 |
Socket = IP 地址 + 端口號,雙向通信端點。Java java.net 包提供 Socket(客戶端)和 ServerSocket(服務端)。
ServerSocket.bind() → listen() → accept() 阻塞等待new Socket(host, port) 發起連接InputStream/OutputStream 進行讀寫public class TcpClient {
public static void main(String[] args) throws IOException {
try (Socket socket = new Socket("127.0.0.1", 8888);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true);
BufferedReader console = new BufferedReader(new InputStreamReader(System.in))) {
String input;
while ((input = console.readLine()) != null) {
if ("bye".equalsIgnoreCase(input)) break;
out.println(input); // 發送
String response = in.readLine(); // 接收
System.out.println("Server: " + response);
}
}
}
}
public class TcpServer {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(8888)) {
System.out.println("Server started on port 8888");
while (true) {
try (Socket client = server.accept();
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream(), StandardCharsets.UTF_8));
PrintWriter out = new PrintWriter(new OutputStreamWriter(client.getOutputStream(), StandardCharsets.UTF_8), true)) {
String msg;
while ((msg = in.readLine()) != null) {
if ("bye".equalsIgnoreCase(msg)) break;
System.out.println("Client: " + msg);
out.println("Echo: " + msg); // 回聲響應
}
} catch (IOException e) {
System.err.println("Client error: " + e.getMessage());
}
}
}
}
}
主線程 accept() 循環,每個客戶端連接啟動獨立線程處理:
public class ConcurrentTcpServer {
private static final ExecutorService POOL = Executors.newCachedThreadPool();
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(8888)) {
System.out.println("Concurrent server started on 8888");
while (true) {
Socket client = server.accept();
POOL.execute(new ClientHandler(client));
}
} finally {
POOL.shutdown();
}
}
static class ClientHandler implements Runnable {
private final Socket socket;
ClientHandler(Socket socket) { this.socket = socket; }
@Override
public void run() {
String clientInfo = socket.getRemoteSocketAddress().toString();
System.out.println("New client: " + clientInfo);
try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true)) {
String msg;
while ((msg = in.readLine()) != null) {
if ("bye".equalsIgnoreCase(msg)) break;
System.out.println(clientInfo + ": " + msg);
out.println("Server received: " + msg);
}
} catch (IOException e) {
System.err.println(clientInfo + " error: " + e.getMessage());
} finally {
System.out.println("Client disconnected: " + clientInfo);
}
}
}
}
顯式指定 StandardCharsets.UTF_8,避免跨平台亂碼。
\n、\r\n 或自定義分隔符try-with-resources 自動關閉流與 SocketThreadPoolExecutor 自定義核心/最大線程、隊列、拒絕策略避開 0-1023 系統保留端口(HTTP 80、SSH 22、FTP 21/20 等),選擇 1024-65535 範圍。
// UDP 發送端
try (DatagramSocket socket = new DatagramSocket()) {
byte[] data = "Hello UDP".getBytes(StandardCharsets.UTF_8);
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("127.0.0.1"), 9999);
socket.send(packet);
}
// UDP 接收端
try (DatagramSocket socket = new DatagramSocket(9999)) {
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
System.out.println(new String(packet.getData(), 0, packet.getLength(), StandardCharsets.UTF_8));
}
Selector + ServerSocketChannel + SocketChannel,單線程多路複用Socket 編程是網絡通信基石。掌握 TCP/UDP 差異、BIO 多線程模型、消息幀解碼、資源管理、線程池優化 可構建穩健網絡應用。高併發場景請轉向 NIO/Netty,享受事件驅動非阻塞架構紅利。