最近學Socket學上癮了,就寫了一個簡單的文件傳輸程序。
客戶端設計思路:客戶端與服務端建立連接,選擇客戶端本地文件,先將文件名及大小等屬性發送給服務端,再將文件通過流的方式傳輸給服務端。傳輸的進度打印到控制臺中,直到傳輸完成。
服務端設計思路:服務端接收客戶端的請求(阻塞式),每接收到一個客戶端請求連接后,就新開一個處理文件的線程,開始寫入流,將文件到服務器的指定目錄下,并與傳輸過來的文件同名。下面是客戶端和服務端的代碼實現:
客戶端代碼:
?12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 | 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" ; // 服務端IP private static final int SERVER_PORT = 8899 ; // 服務端端口 private Socket client; private FileInputStream fis; private DataOutputStream dos; /** * 構造函數<br/> * 與服務器建立連接 * @throws Exception */ public FileTransferClient() throws Exception { super (SERVER_IP, SERVER_PORT); this .client = this ; System.out.println( "Cliect[port:" + client.getLocalPort() + "] 成功連接服務端" ); } /** * 向服務端傳輸文件 * @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(); // 啟動客戶端連接 client.sendFile(); // 傳輸文件 } catch (Exception e) { e.printStackTrace(); } } } |
服務端代碼:
?123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 | 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 ; // 服務端端口 private static DecimalFormat df = null ; static { // 設置數字格式,保留一位有效小數 df = new DecimalFormat( "#0.0" ); df.setRoundingMode(RoundingMode.HALF_UP); df.setMinimumFractionDigits( 1 ); df.setMaximumFractionDigits( 1 ); } public FileTransferServer() throws Exception { super (SERVER_PORT); } /** * 使用線程處理每個客戶端傳輸的文件 * @throws Exception */ public void load() throws Exception { while ( true ) { // server嘗試接收其他Socket的連接請求,server的accept方法是阻塞式的 Socket socket = this .accept(); /** * 我們的服務端處理客戶端的連接請求是同步進行的, 每次接收到來自客戶端的連接請求后, * 都要先跟當前的客戶端通信完之后才能再處理下一個連接請求。 這在并發比較多的情況下會嚴重影響程序的性能, * 為此,我們可以把它改為如下這種異步處理與客戶端通信的方式 */ // 每接收到一個Socket就建立一個新的線程來處理它 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(); // 啟動服務端 server.load(); } catch (Exception e) { e.printStackTrace(); } } } |
測試的結果(客戶端):
測試的結果(服務端):
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
新聞熱點
疑難解答