Java Socket實(shí)現(xiàn)文件傳輸示例代碼
最近學(xué)Socket學(xué)上癮了,就寫了一個(gè)簡單的文件傳輸程序。
客戶端設(shè)計(jì)思路:客戶端與服務(wù)端建立連接,選擇客戶端本地文件,先將文件名及大小等屬性發(fā)送給服務(wù)端,再將文件通過流的方式傳輸給服務(wù)端。傳輸?shù)倪M(jìn)度打印到控制臺中,直到傳輸完成。
服務(wù)端設(shè)計(jì)思路:服務(wù)端接收客戶端的請求(阻塞式),每接收到一個(gè)客戶端請求連接后,就新開一個(gè)處理文件的線程,開始寫入流,將文件到服務(wù)器的指定目錄下,并與傳輸過來的文件同名。
下面是客戶端和服務(wù)端的代碼實(shí)現(xiàn):
客戶端代碼:
import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.net.Socket; /** * 文件傳輸Client端<br> * 功能說明: * * @author 大智若愚的小懂 * @Date 2016年09月01日 * @version 1.0 */ public class FileTransferClient extends Socket { private static final String SERVER_IP = "127.0.0.1"; // 服務(wù)端IP private static final int SERVER_PORT = 8899; // 服務(wù)端端口 private Socket client; private FileInputStream fis; private DataOutputStream dos; /** * 構(gòu)造函數(shù)<br/> * 與服務(wù)器建立連接 * @throws Exception */ public FileTransferClient() throws Exception { super(SERVER_IP, SERVER_PORT); this.client = this; System.out.println("Cliect[port:" + client.getLocalPort() + "] 成功連接服務(wù)端"); } /** * 向服務(wù)端傳輸文件 * @throws Exception */ public void sendFile() throws Exception { try { File file = new File("E:\\JDK1.6中文參考手冊(JDK_API_1_6_zh_CN).CHM"); if(file.exists()) { fis = new FileInputStream(file); dos = new DataOutputStream(client.getOutputStream()); // 文件名和長度 dos.writeUTF(file.getName()); dos.flush(); dos.writeLong(file.length()); dos.flush(); // 開始傳輸文件 System.out.println("======== 開始傳輸文件 ========"); byte[] bytes = new byte[1024]; int length = 0; long progress = 0; while((length = fis.read(bytes, 0, bytes.length)) != -1) { dos.write(bytes, 0, length); dos.flush(); progress += length; System.out.print("| " + (100*progress/file.length()) + "% |"); } System.out.println(); System.out.println("======== 文件傳輸成功 ========"); } } catch (Exception e) { e.printStackTrace(); } finally { if(fis != null) fis.close(); if(dos != null) dos.close(); client.close(); } } /** * 入口 * @param args */ public static void main(String[] args) { try { FileTransferClient client = new FileTransferClient(); // 啟動(dòng)客戶端連接 client.sendFile(); // 傳輸文件 } catch (Exception e) { e.printStackTrace(); } } }
服務(wù)端代碼:
import java.io.DataInputStream; import java.io.File; import java.io.FileOutputStream; import java.math.RoundingMode; import java.net.ServerSocket; import java.net.Socket; import java.text.DecimalFormat; /** * 文件傳輸Server端<br> * 功能說明: * * @author 大智若愚的小懂 * @Date 2016年09月01日 * @version 1.0 */ public class FileTransferServer extends ServerSocket { private static final int SERVER_PORT = 8899; // 服務(wù)端端口 private static DecimalFormat df = null; static { // 設(shè)置數(shù)字格式,保留一位有效小數(shù) df = new DecimalFormat("#0.0"); df.setRoundingMode(RoundingMode.HALF_UP); df.setMinimumFractionDigits(1); df.setMaximumFractionDigits(1); } public FileTransferServer() throws Exception { super(SERVER_PORT); } /** * 使用線程處理每個(gè)客戶端傳輸?shù)奈募? * @throws Exception */ public void load() throws Exception { while (true) { // server嘗試接收其他Socket的連接請求,server的accept方法是阻塞式的 Socket socket = this.accept(); /** * 我們的服務(wù)端處理客戶端的連接請求是同步進(jìn)行的, 每次接收到來自客戶端的連接請求后, * 都要先跟當(dāng)前的客戶端通信完之后才能再處理下一個(gè)連接請求。 這在并發(fā)比較多的情況下會嚴(yán)重影響程序的性能, * 為此,我們可以把它改為如下這種異步處理與客戶端通信的方式 */ // 每接收到一個(gè)Socket就建立一個(gè)新的線程來處理它 new Thread(new Task(socket)).start(); } } /** * 處理客戶端傳輸過來的文件線程類 */ class Task implements Runnable { private Socket socket; private DataInputStream dis; private FileOutputStream fos; public Task(Socket socket) { this.socket = socket; } @Override public void run() { try { dis = new DataInputStream(socket.getInputStream()); // 文件名和長度 String fileName = dis.readUTF(); long fileLength = dis.readLong(); File directory = new File("D:\\FTCache"); if(!directory.exists()) { directory.mkdir(); } File file = new File(directory.getAbsolutePath() + File.separatorChar + fileName); fos = new FileOutputStream(file); // 開始接收文件 byte[] bytes = new byte[1024]; int length = 0; while((length = dis.read(bytes, 0, bytes.length)) != -1) { fos.write(bytes, 0, length); fos.flush(); } System.out.println("======== 文件接收成功 [File Name:" + fileName + "] [Size:" + getFormatFileSize(fileLength) + "] ========"); } catch (Exception e) { e.printStackTrace(); } finally { try { if(fos != null) fos.close(); if(dis != null) dis.close(); socket.close(); } catch (Exception e) {} } } } /** * 格式化文件大小 * @param length * @return */ private String getFormatFileSize(long length) { double size = ((double) length) / (1 << 30); if(size >= 1) { return df.format(size) + "GB"; } size = ((double) length) / (1 << 20); if(size >= 1) { return df.format(size) + "MB"; } size = ((double) length) / (1 << 10); if(size >= 1) { return df.format(size) + "KB"; } return length + "B"; } /** * 入口 * @param args */ public static void main(String[] args) { try { FileTransferServer server = new FileTransferServer(); // 啟動(dòng)服務(wù)端 server.load(); } catch (Exception e) { e.printStackTrace(); } } }
測試的結(jié)果(客戶端):
測試的結(jié)果(服務(wù)端):
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Cloud Zuul集成Swagger實(shí)現(xiàn)過程解析
這篇文章主要介紹了Spring Cloud Zuul集成Swagger實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11spring boot(三)之Spring Boot中Redis的使用
這篇文章主要介紹了spring boot(三)之Spring Boot中Redis的使用,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-05-05