public class EchoProtocol implements Runnable {
private static final int BUFSIZE = 32 * 1024; // 32KB 緩衝
private final Socket clientSocket;
private final Logger logger;
public EchoProtocol(Socket socket, Logger logger) {
this.clientSocket = socket;
this.logger = logger;
}
public static void handleEchoClient(Socket socket, Logger logger) {
try (InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream()) {
byte[] buffer = new byte[BUFSIZE];
int bytesRead;
long total = 0;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
total += bytesRead;
}
logger.info(() -> "Client " + socket.getRemoteSocketAddress() + " echoed " + total + " bytes");
} catch (IOException e) {
logger.log(Level.WARNING, "Echo error", e);
} finally {
try { socket.close(); } catch (IOException ignored) {}
}
}
@Override public void run() { handleEchoClient(clientSocket, logger); }
}
public class TCPEchoServerThread {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(5500)) {
Logger logger = Logger.getLogger("practical");
while (true) {
Socket client = server.accept();
new Thread(new EchoProtocol(client, logger)).start();
logger.info("Started thread for " + client.getRemoteSocketAddress());
}
}
}
}
public class TCPEchoServerPool {
public static void main(String[] args) throws IOException {
int poolSize = 3;
try (ServerSocket server = new ServerSocket(5500)) {
Logger logger = Logger.getLogger("practical");
for (int i = 0; i < poolSize; i++) {
new Thread(() -> {
while (true) {
try {
Socket client = server.accept(); // 多線程競爭 accept
EchoProtocol.handleEchoClient(client, logger);
} catch (IOException e) {
logger.log(Level.WARNING, "Accept failed", e);
}
}
}, "Worker-" + i).start();
}
}
}
}
缺點:無隊列緩衝、無動態調整、異常處理簡陋。
public class ThreadPoolTaskExecutor {
private static final ExecutorService executor =
new ThreadPoolExecutor(
4, // corePoolSize
16, // maximumPoolSize
60L, TimeUnit.SECONDS, // keepAliveTime
new LinkedBlockingQueue<>(1024), // workQueue
new ThreadFactoryBuilder().setNameFormat("tcp-worker-%d").build(),
new ThreadPoolExecutor.AbortPolicy() // 拒絕策略
);
// 同步提交 + 超時控制
public static <T> T submit(Callable<T> task, long timeout, TimeUnit unit)
throws TimeoutException, ExecutionException, InterruptedException {
Future<T> future = executor.submit(task);
return future.get(timeout, unit);
}
public static void submit(Runnable task) { executor.submit(task); }
public static void shutdown() { executor.shutdown(); }
}
public class TCPEchoServerExecutor {
public static void main(String[] args) throws IOException {
try (ServerSocket server = new ServerSocket(5500)) {
Logger logger = Logger.getLogger("practical");
while (true) {
Socket client = server.accept();
// 異步提交,主線程立即返回 accept 下一個
ThreadPoolTaskExecutor.submit(new EchoProtocol(client, logger));
}
} finally {
ThreadPoolTaskExecutor.shutdown();
}
}
}
// 客戶端發送:文件元數據 (JSON) + 文件內容
// 元數據:{fileName, fileSize, fileMD5, chunkIndex, totalChunks, offset}
// 服務端:RandomAccessFile.seek(offset) 寫入對應分片
// 完成後校驗 MD5,回覆成功/失敗
RandomAccessFile 支持任意位置讀寫,實現分片追加chunkIndex 確保亂序到達也能正確組裝filename.part,完成後重命名手寫 NIO/線程池易出 Bug,生產環境強烈建議直接使用 Netty:
FileServer / FileClientTCP 服務端線程模型演進:單線程 → 每請求一線程 → 固定池 → 成熟線程池 → Netty。核心竅門:
ThreadPoolExecutor 替代手寫池,獲得隊列、擴縮容、監控、拒絕策略RandomAccessFile 實現斷點續傳