這是一個簡單的包含發送端和接收端的例子。發送端向接收端發送文件名和文件內容 ,接收端將收到的文件保存在磁盤上。接收端可以同時接收多個發送端傳來的文件,但沒 有處理文件同名的情況。
這個例子中設計了一個簡單的協議。發送的內容是這樣的:
文件名長度(4字節)—文件名—文件內容長度(4字節)—文件內容 。
接收端也按照這個結構進行解析。建議先看 Client 類,再看 Server 類。
01.import java.io.*; 02.import java.net.ServerSocket; 03.import java.net.Socket; 04. 05./** 06. * 簡單的文件發送與接收示例 07. */ 08.public class FileTrasmission { 09. 10. //程序入口 11. public static void main(String[] args) throws Exception { 12. int port = 7788; 13. new Server(port, "c:\\save\\").start(); 14. new Client().sendFile("127.0.0.1", port, "c:\\迷失在康熙末年.txt"); 15. } 16.} 17. 18./** 19. * 接收端。可同時接收多個發送端發來的文件。但如果發來的文件是同名的話那就亂了。 20. */ 21.class Server { 22. 23. private int listenPort; 24. 25. private String savePath; 26. 27. /** 28. * 構造方法 29. * 30. * @param listenPort 偵聽端口 31. * @param savePath 接收的文件要保存的路徑 32. * 33. * @throws IOException 如果創建保存路徑失敗 34. */ 35. Server(int listenPort, String savePath) throws IOException { 36. this.listenPort = listenPort; 37. this.savePath = savePath; 38. 39. File file = new File(savePath); 40. if (!file.exists() && !file.mkdirs()) { 41. throw new IOException("無法創建文件夾 " + savePath); 42. } 43. } 44. 45. // 開始偵聽 46. public void start() { 47. new ListenThread().start(); 48. } 49. 50. // 網上抄來的,將字節轉成 int。b 長度不得小於 4,且只會取前 4 位。 51. public static int b2i(byte[] b) { 52. int value = 0; 53. for (int i = 0; i < 4; i++) { 54. int shift = (4 - 1 - i) * 8; 55. value += (b[i] & 0x000000FF) << shift; 56. } 57. return value; 58. } 59. 60. 61. /** 62. * 偵聽線程 63. */ 64. private class ListenThread extends Thread { 65. 66. @Override 67. public void run() { 68. try { 69. ServerSocket server = new ServerSocket(listenPort); 70. 71. // 開始循環 72. while (true) { 73. Socket socket = server.accept(); 74. new HandleThread(socket).start(); 75. } 76. } catch (IOException e) { 77. e.printStackTrace(); 78. } 79. } 80. } 81. 82. /** 83. * 讀取流並保存文件的線程 84. */ 85. private class HandleThread extends Thread { 86. 87. private Socket socket; 88. 89. private HandleThread(Socket socket) { 90. this.socket = socket; 91. } 92. 93. @Override 94. public void run() { 95. try { 96. InputStream is = socket.getInputStream(); 97. readAndSave(is); 98. } catch (IOException e) { 99. e.printStackTrace(); 100. } finally { 101. try { 102. socket.close(); 103. } catch (IOException e) { 104. // nothing to do 105. } 106. } 107. } 108. 109. // 從流中讀取內容並保存 110. private void readAndSave(InputStream is) throws IOException { 111. String filename = getFileName(is); 112. int file_len = readInteger(is); 113. System.out.println("接收文件:" + filename + ",長度:" + file_len); 114. 115. readAndSave0(is, savePath + filename, file_len); 116. 117. System.out.println("文件保存成功(" + file_len + "字節)。"); 118. } 119. 120. private void readAndSave0(InputStream is, String path, int file_len) throws IOException { 121. FileOutputStream os = getFileOS(path); 122. readAndWrite(is, os, file_len); 123. os.close(); 124. } 125. 126. // 邊讀邊寫,直到讀取 size 個字節 127. private void readAndWrite(InputStream is, FileOutputStream os, int size) throws IOException { 128. byte[] buffer = new byte[4096]; 129. int count = 0; 130. while (count < size) { 131. int n = is.read(buffer); 132. // 這裡沒有考慮 n = -1 的情況 133. os.write(buffer, 0, n); 134. count += n; 135. } 136. } 137. 138. // 讀取文件名 139. private String getFileName(InputStream is) throws IOException { 140. int name_len = readInteger(is); 141. byte[] result = new byte[name_len]; 142. is.read(result); 143. return new String(result); 144. } 145. 146. // 讀取一個數字 147. private int readInteger(InputStream is) throws IOException { 148. byte[] bytes = new byte[4]; 149. is.read(bytes); 150. return b2i(bytes); 151. } 152. 153. // 創建文件並返回輸出流 154. private FileOutputStream getFileOS(String path) throws IOException { 155. File file = new File(path); 156. if (!file.exists()) { 157. file.createNewFile(); 158. } 159. 160. return new FileOutputStream(file); 161. } 162. } 163.} 164. 165./** 166. * 發送端 167. */ 168.class Client { 169. 170. // 網上抄來的,將 int 轉成字節 171. public static byte[] i2b(int i) { 172. return new byte[]{ 173. (byte) ((i >> 24) & 0xFF), 174. (byte) ((i >> 16) & 0xFF), 175. (byte) ((i >> 8) & 0xFF), 176. (byte) (i & 0xFF) 177. }; 178. } 179. 180. /** 181. * 發送文件。文件大小不能大於 {@link Integer#MAX_VALUE} 182. * 183. * @param hostname 接收端主機名或 IP 地址 184. * @param port 接收端端口號 185. * @param filepath 文件路徑 186. * 187. * @throws IOException 如果讀取文件或發送失敗 188. */ 189. public void sendFile(String hostname, int port, String filepath) throws IOException { 190. File file = new File(filepath); 191. FileInputStream is = new FileInputStream(filepath); 192. 193. Socket socket = new Socket(hostname, port); 194. OutputStream os = socket.getOutputStream(); 195. 196. try { 197. int length = (int) file.length(); 198. System.out.println("發送文件:" + file.getName() + ",長度:" + length); 199. 200. // 發送文件名和文件內容 201. writeFileName(file, os); 202. writeFileContent(is, os, length); 203. } finally { 204. os.close(); 205. is.close(); 206. } 207. } 208. 209. // 輸出文件內容 210. private void writeFileContent(InputStream is, OutputStream os, int length) throws IOException { 211. // 輸出文件長度 212. os.write(i2b(length)); 213. 214. // 輸出文件內容 215. byte[] buffer = new byte[4096]; 216. int size; 217. while ((size = is.read(buffer)) != -1) { 218. os.write(buffer, 0, size); 219. } 220. } 221. 222. // 輸出文件名 223. private void writeFileName(File file, OutputStream os) throws IOException { 224. byte[] fn_bytes = file.getName().getBytes(); 225. 226. os.write(i2b(fn_bytes.length)); // 輸出文件名長度 227. os.write(fn_bytes); // 輸出文件名 228. } 229.}