客戶端並發上傳圖片文件,服務端以多線程接收並以「客戶端IP_時間戳.jpg」命名保存。圖片屬二進位資料,**必須使用位元組流**(InputStream/OutputStream),不可用字元流。
import java.io.*;
import java.net.*;
class UploadTask implements Runnable {
private final Socket socket;
public UploadTask(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream()) {
String ip = socket.getInetAddress().getHostAddress();
long timestamp = System.currentTimeMillis();
File outputFile = new File("E:/" + ip + "_" + timestamp + ".jpg");
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.flush();
}
// 回應客戶端
out.write("上傳完成...".getBytes());
out.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try { socket.close(); } catch (IOException ignored) {}
}
}
}
public class TCPServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8090);
System.out.println("服務端啟動,監聽 8090...");
while (true) {
Socket client = serverSocket.accept();
System.out.println("新連接:" + client.getInetAddress());
new Thread(new UploadTask(client)).start();
}
}
}
import java.io.*;
import java.net.*;
public class TCPClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8090);
System.out.println("已連接服務端");
try (OutputStream out = socket.getOutputStream();
FileInputStream fis = new FileInputStream("1.jpg")) {
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
out.flush();
// 關鍵:告知服務端發送結束(發送 FIN 包)
socket.shutdownOutput();
// 接收回應
try (InputStream in = socket.getInputStream()) {
byte[] resp = new byte[1024];
int rlen = in.read(resp);
System.out.println("服務端回應:" + new String(resp, 0, rlen));
}
} finally {
socket.close();
}
}
}
| 點 | 說明 |
|---|---|
| 位元組流 | FileInputStream/FileOutputStream、Socket.getInputStream()/getOutputStream(),不做編碼轉換 |
shutdownOutput() | 發送 TCP FIN,讓服務端 read() 返回 -1,否則服務端會永遠阻塞等待數據 |
| 多線程隔離 | 每連接一線程,互不干擾,利用多核 CPU |
| 檔案命名 | IP + 時間戳確保唯一,避免覆蓋 |
| 緩衝區大小 | 1024/4096/8192 皆可,過大佔記憶體,過小增加系統調用 |
new Thread() 為 ExecutorService,控制併發數、復用線程。TCP 文件上傳核心在於:位元組流 + shutdownOutput() 邊界標記 + 多線程並發。上述代碼為教學級最小實現,生產環境請加入線程池、協議幀、異常重試、安全驗證等企業級特性。