這是一個簡單的包含發送端和接收端的例子。發送端向接收端發送文件名和文件內容 ,接收端將收到的文件保存在磁盤上。接收端可以同時接收多個發送端傳來的文件,但沒 有處理文件同名的情況。
這個例子中設計了一個簡單的協議。發送的內容是這樣的:
文件名長度(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.}