TCP/IP 是互聯網基石,分層模型:
| 類 | 協議 | 角色 | 關鍵方法 |
|---|---|---|---|
| ServerSocket | TCP | 服務端監聽 | accept()、bind()、setSoTimeout() |
| Socket | TCP | 客戶端/服務端連接 | getInputStream()、getOutputStream()、close() |
| DatagramSocket | UDP | 發送/接收數據報 | send()、receive()、setSoTimeout() |
| DatagramPacket | UDP | 數據報容器 | getData()、getAddress()、getPort() |
| InetAddress | 通用 | IP 地址封裝 | getByName()、getLocalHost()、getHostAddress() |
public static void printNetworkInterfaces() throws SocketException {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
System.out.println("Interface: " + ni.getName() + " (" + ni.getDisplayName() + ")");
Enumeration<InetAddress> addrs = ni.getInetAddresses();
while (addrs.hasMoreElements()) {
InetAddress addr = addrs.nextElement();
System.out.println(" IP: " + addr.getHostAddress() + " [" + (addr.isLoopbackAddress() ? "Loopback" : "") + "]");
}
}
}
public class TcpEchoServer {
private static final int PORT = 7;
private static final int BUF_SIZE = 1024;
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(PORT)) {
server.setSoTimeout(0); // 永不超時
System.out.println("TCP Echo Server 啟動,端口: " + PORT);
while (true) {
try (Socket client = server.accept()) {
System.out.println("客戶端連接: " + client.getRemoteSocketAddress());
try (InputStream in = client.getInputStream();
OutputStream out = client.getOutputStream()) {
byte[] buf = new byte[BUF_SIZE];
int len;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len); // 原樣返回
}
}
} catch (IOException e) {
System.err.println("客戶端處理異常: " + e.getMessage());
}
}
}
}
}
public class TcpEchoClient {
public static void main(String[] args) throws IOException {
if (args.length < 2) {
System.err.println("用法: java TcpEchoClient ");
return;
}
String host = args[0];
byte[] data = args[1].getBytes(StandardCharsets.UTF_8);
int port = 7;
try (Socket socket = new Socket(host, port);
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream()) {
System.out.println("已連接到 " + host + ":" + port);
out.write(data);
out.flush();
byte[] buf = new byte[data.length];
int total = 0, read;
while (total < data.length && (read = in.read(buf, total, data.length - total)) != -1) {
total += read;
}
System.out.println("服務端回傳: " + new String(buf, 0, total, StandardCharsets.UTF_8));
}
}
}
public class UdpEchoServer {
private static final int PORT = 7;
private static final int MAX_PACKET = 65507; // UDP 最大負載
public static void main(String[] args) throws IOException {
try (DatagramSocket socket = new DatagramSocket(PORT)) {
byte[] buf = new byte[MAX_PACKET];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
System.out.println("UDP Echo Server 啟動,端口: " + PORT);
while (true) {
socket.receive(packet); // 阻塞等待
System.out.println("收到來自 " + packet.getAddress() + ":" + packet.getPort() + " 的數據");
socket.send(packet); // 原樣發回
packet.setLength(buf.length); // 重置長度供下次接收
}
}
}
}
public class UdpEchoClient {
public static void main(String[] args) throws IOException {
if (args.length < 2) {
System.err.println("用法: java UdpEchoClient ");
return;
}
InetAddress serverAddr = InetAddress.getByName(args[0]);
byte[] data = args[1].getBytes(StandardCharsets.UTF_8);
int port = 7;
try (DatagramSocket socket = new DatagramSocket()) {
socket.setSoTimeout(3000); // 3秒超時
DatagramPacket sendPkt = new DatagramPacket(data, data.length, serverAddr, port);
DatagramPacket recvPkt = new DatagramPacket(new byte[data.length], data.length);
int retries = 0;
boolean received = false;
while (!received && retries < 5) {
socket.send(sendPkt);
try {
socket.receive(recvPkt);
if (!recvPkt.getAddress().equals(serverAddr)) {
throw new IOException("未知來源回復");
}
received = true;
System.out.println("服務端回傳: " + new String(recvPkt.getData(), 0, recvPkt.getLength()));
} catch (SocketTimeoutException e) {
retries++;
System.out.println("超時,重試 " + retries + "/5");
}
}
if (!received) System.out.println("最終無響應");
}
}
}
| 維度 | TCP | UDP |
|---|---|---|
| 連接建立 | 三次握手 | 無連接 |
| 可靠性 | 序列號+ACK+重傳,保證到達 | 不可靠,可能丟包、亂序、重複 |
| 有序性 | 保證順序 | 無序 |
| 流控/擁塞控制 | 滑動窗口、慢啟動、快速恢復 | 無 |
| 邊界 | 字節流,無消息邊界 | 數據報,保留邊界 |
| 最大傳輸單元 | 無上限(流控窗口) | 65507 字節(IPv4) |
| 適用場景 | HTTP、數據庫、文件傳輸、郵件 | DNS、視頻會議、即時遊戲、廣播、IoT |
Socket.setReceiveBufferSize()/setSendBufferSize() 調優吞吐。setSoTimeout() 防止無限阻塞,setSoKeepAlive() 檢測死連接。Selector + ServerSocketChannel/SocketChannel/DatagramChannel 實現非阻塞多路複用。Socket 是網絡編程基石。TCP 提供可靠字節流,適合強一致性場景;UDP 提供輕量數據報,適合高實時、容忍丟包場景。掌握阻塞 I/O 基礎後,進階學習 NIO、Netty、gRPC 等高性能框架,構建生產級網絡應用。