1. 程式人生 > >java socket 實現c/s檔案上傳下載功能+註冊登入

java socket 實現c/s檔案上傳下載功能+註冊登入

使用JAVA SOCKET實現c/s結構的通訊程式,以實現客戶端向伺服器端的檔案的上傳以及伺服器端向客戶端的檔案的下載。

實現思路:

1、在伺服器端,建立SocketServer物件,監聽某一埠,有連線請求則開一個執行緒處理這一請求

2、在客戶端,根據伺服器的ip地址以及埠號,建立socket連線,並通過此連線傳送資料以及訊號



實現效果:

①首先開啟伺服器程式:


②開啟客戶端程式,輸入伺服器ip,這裡由於都執行在本地,輸入127.0.0.1:


③客戶端啟動,顯示選擇選單:


④選擇1註冊開始註冊,註冊結束後在伺服器端會新增使用者,客戶端回到選單:


⑤選擇2登入開始使用剛才註冊的帳號登入,登入成功後進入子選單:


⑥以上過程在伺服器端都有記錄:


⑦客戶端中在子選單選擇1上傳檔案,輸入要上傳的檔案的路徑(這裡使用絕對路徑,也可以使用相對路徑):


⑧客戶端上傳檔案,需要伺服器端的同意:


⑨若不同意客戶端的檔案上傳,則取消:


⑩客戶端也會收到檔案無法上傳的通知,並且再次進入選單:


十一、再次上傳檔案,這次伺服器端同意檔案上傳,則檔案會被儲存到upload目錄下:


十二、檔案上傳成功:


十三、在選單中選擇下載檔案,會把伺服器端upload資料夾下的檔案羅列出來


十四、選擇需要下載的檔案,下載完成,檔案儲存在客戶端的download資料夾中:


十五、上傳下載的檔案:


原始碼如下:

(伺服器端程式)

package socket;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

//使用者類
class User {
	public String username;
	public String password;
	public List<String> uploadFiles; // 儲存上傳過的檔名稱
}

/**
 * 伺服器端程式
 * 
 * @author Charles
 *
 */
public class Server {
	public static final int PORT = 12345;// 監聽的埠號
	// 賬戶列表
	public static List<User> userList = new ArrayList<User>();

	public static void main(String[] args) {
		System.out.println("-------------------伺服器啟動------------------\n");
		Server server = new Server();
		server.init();
	}

	public void init() {
		try {
			ServerSocket serverSocket = new ServerSocket(PORT);
			while (true) {
				// 一旦有堵塞, 則表示伺服器與客戶端獲得了連線
				Socket client = serverSocket.accept();
				// 處理這次連線
				new HandlerThread(client);
			}
		} catch (Exception e) {
			System.out.println("伺服器異常: " + e.getMessage());
		}
	}

	// 處理資料傳輸的執行緒
	private class HandlerThread implements Runnable {
		private Socket socket;

		public HandlerThread(Socket client) {
			socket = client;
			new Thread(this).start();
			System.out.println("呼叫一次處理執行緒");
		}

		public void run() {
			try {
				// 讀取客戶端資料的流
				DataInputStream input = new DataInputStream(socket.getInputStream());
				// 向客戶端回覆資訊的流
				DataOutputStream out = new DataOutputStream(socket.getOutputStream());

				// 接收客戶端的選項
				String clientInputStr = input.readUTF();

				switch (clientInputStr) {
				case "申請註冊": {
					String usernamepassword = input.readUTF(); // 獲取註冊的資訊
					String[] strs = usernamepassword.split("#");
					// 根據上傳的註冊資訊新增使用者
					User user = new User();
					user.username = strs[0];
					user.password = strs[1];
					user.uploadFiles = new ArrayList<String>();
					userList.add(user);
					System.out.println("成功新增使用者:" + user.username);
					// System.out.println(userList);
					out.writeUTF("註冊成功!");
					break;
				} // #case "申請註冊"

				case "申請登入": {
					String usernamepassword = input.readUTF(); // 獲取註冊的資訊
					String[] strs = usernamepassword.split("#");

					System.out.println("客戶端試圖登入的帳號:" + strs[0]);

					boolean canlogin = false;
					User curuser = null;
					for (User u : userList) {
						if (u.username.equals(strs[0]) && u.password.equals(strs[1])) { // 可以登入
							curuser = u;
							canlogin = true;
							break;
						}
					}

					if (canlogin) { // 使用者名稱和密碼正確
						out.writeBoolean(true); // 向客戶端返回可以登入的訊號
						System.out.println("驗證成功,已登入!\n");

						boolean rei = true; // 迴圈接收傳送檔案
						while (rei) {

							// 接收到模組編號
							clientInputStr = input.readUTF();
							switch (clientInputStr) {
							case "退出檔案上傳下載模組": { // 退出檔案上傳下載模組
								rei = false;
								break;
							}
							case "申請上傳檔案": { // 申請上傳檔案
								clientInputStr = input.readUTF(); // 接收檔名
								System.out.println("客戶端上傳的檔案的檔名為:" + clientInputStr.substring(1) + "  是否接收此檔案?(y/n)");
								char receive = new Scanner(System.in).nextLine().toCharArray()[0];
								receive = Character.toLowerCase(receive);

								if (receive == 'y') {
									out.writeBoolean(true);
									File f = new File("upload/" + clientInputStr
											.substring(clientInputStr.lastIndexOf("/") + 1, clientInputStr.length()));
									if (!f.getParentFile().exists()) {
										f.getParentFile().mkdir();
									}
									if (!f.exists()) {
										f.createNewFile();
									}
									FileOutputStream fos = new FileOutputStream(f);
									byte[] buffer = new byte[1024];
									int flag = -1;
									System.out.print("接收檔案中...");

									// 構造結束標誌
									byte[] endstr = "檔案上傳結束".getBytes("utf-8");
									byte[] sendend = new byte[1024];
									for (int i = 0; i < endstr.length; i++) {
										sendend[i] = endstr[i];
									}

									// 當未讀取到結束標誌的時候就一直讀取
									while ((flag = input.read(buffer)) != -1
											&& !new String(buffer, "utf-8").equals(new String(sendend, "utf-8"))) {
										fos.write(buffer);
										buffer = new byte[1024];
									}
									fos.close();
									System.out.println("\n檔案上傳結束!\n");
								} else {
									System.out.println("已經拒絕客戶端的伺服器上傳!");
									out.writeBoolean(false);
								}

								continue;

							}
							case "申請下載檔案": { // 申請下載檔案
								// 首先獲取所有upload目錄下的檔案
								File uploadDir = new File("upload");
								if (uploadDir.exists()) { // 存在該目錄
									out.writeBoolean(true);
									// 獲取目錄下所有檔案
									File[] files = uploadDir.listFiles();
									String hint = "伺服器上一共有" + files.length + "個檔案\n0、退出下載\n";
									for (int i = 0; i < files.length; i++) {
										hint += (i+1)+"、"+files[i].getName()+"\n";
									}
									hint += "請輸入需要下載的檔案序號:";
									out.writeUTF(hint);
									int fileId=input.readInt();
									if(fileId==0) {
										continue;
									}
									out.writeUTF(files[fileId-1].getName());
									// 構造結束標誌
									byte[] endstr = "檔案下載結束".getBytes("utf-8");
									byte[] sendend = new byte[1024];
									for (int i = 0; i < endstr.length; i++) {
										sendend[i] = endstr[i];
									}
									
									//獲取檔案輸入流
									FileInputStream fin = new FileInputStream(files[fileId-1]);
									byte[] buffer = new byte[1024];
									int readflag=-1;
									System.out.println("正在向客戶端傳送檔案...");
									while((readflag=fin.read(buffer))!=-1) {
										out.write(buffer);
									}
									fin.close();
									out.write(sendend);
									System.out.println("檔案傳送成功!\n");
									continue;

								} else { // 目錄不存在,說明無法下載
									out.writeBoolean(false);
								}
								continue;
							}
							default: {
								break;
							}

							}

						}

					} else {
						out.writeBoolean(false); // 向客戶端返回不能登入的訊號
						System.out.println("驗證失敗不允許登入!\n");
					}
				} // #case "申請登入"

				}// #switch
					// 關閉流
				out.close();
				input.close();
			} catch (Exception e) {
				//e.printStackTrace();
				System.out.println("伺服器 run 異常: " + e.getMessage());
			} finally {

				if (socket != null) {
					try {
						socket.close();
					} catch (Exception e) {
						//e.printStackTrace();
						socket = null;
						System.out.println("服務端 finally 異常:" + e.getMessage());
					}
				}

			}
		}
	}
}

(2)客戶端程式

package socket;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.Scanner;

public class Client {
	public static String IP_ADDR = "localhost";// 伺服器地址
	public static final int PORT = 12345;// 伺服器埠號

	public static void main(String[] args) {
		System.out.print("請輸入要連線的伺服器IP地址:");
		Scanner in = new Scanner(System.in);
		IP_ADDR = in.next(); // 獲取伺服器地址

		System.out.println("\n------------------客戶端啟動--------------------\n");

		boolean f1 = true; // 是否退出的標誌變數
		while (f1) {
			System.out.print("選單:\n1、註冊\n2、登入\n3、退出\n請輸入你的選擇序號:");
			int select = in.nextInt();
			switch (select) {
			case 1: { // 註冊
				Socket socket = null;
				try {
					System.out.println("------------------開始註冊--------------------");
					System.out.println("請輸入使用者名稱(不得包含\"#\"):");
					String username = in.next();
					if (username.contains("#")) { // 檢查使用者名稱是否合法
						System.out.println("使用者名稱不合法");
						continue; // 跳出本次迴圈繼續
					}
					System.out.println("請輸入密碼(不得包含\"#\"):");
					String password = in.next();
					if (password.contains("#")) { // 檢查密碼是否合法
						System.out.println("密碼不合法");
						continue; // 跳出本次迴圈繼續
					}

					// 建立一個流套接字並將其連線到指定主機上的指定埠號
					socket = new Socket(IP_ADDR, PORT);
					// 向伺服器傳遞資料的流
					DataOutputStream out = new DataOutputStream(socket.getOutputStream());
					// 讀取伺服器端資料的流
					DataInputStream input = new DataInputStream(socket.getInputStream());

					out.writeUTF("申請註冊");

					String usernamepassword = username + "#" + password;
					out.writeUTF(usernamepassword); // 傳送註冊資訊

					String msg = input.readUTF();

					System.out.println("伺服器端:" + msg + "\n");

				} catch (IOException e) {
					System.out.println("客戶端註冊異常:" + e.getMessage());
				} finally {
					// 關閉socket連線,釋放資源
					if (socket != null) {
						try {
							socket.close();
						} catch (IOException e) {
							socket = null;
							e.printStackTrace();
							System.out.println("客戶端 finally 異常:" + e.getMessage());
						}
					}
				}
				break;
			}

			case 2: { // 登入
				Socket socket = null;
				try {
					System.out.println("\n------------------開始登入--------------------");
					System.out.println("請輸入使用者名稱:");
					String username = in.next();
					System.out.println("請輸入密碼:");
					String password = in.next();

					String usernamepassword = username + "#" + password;

					// 建立一個流套接字並將其連線到指定主機上的指定埠號
					socket = new Socket(IP_ADDR, PORT);
					// 向伺服器傳遞資料的流
					DataOutputStream out = new DataOutputStream(socket.getOutputStream());
					// 讀取伺服器端資料的流
					DataInputStream input = new DataInputStream(socket.getInputStream());

					out.writeUTF("申請登入"); // 向伺服器提交登入的請求,等待伺服器響應
					out.writeUTF(usernamepassword); // 向伺服器上傳登入資訊以驗證

					boolean msg = input.readBoolean(); // 讀取伺服器響應的訊號,判斷是否可以登入

					if (msg) { // 登陸成功
						System.out.println("登入驗證成功!\n");
						// 開始進入上傳下載檔案模組
						boolean f = true;
						while (f) {
							System.out.print("選單:\n1、上傳檔案\n2、下載檔案\n3、退出\n請輸入你的選擇序號:");
							int choice = in.nextInt();
							switch (choice) {
							case 1: { // 上傳檔案
								try {

									out.writeUTF("申請上傳檔案"); // 向伺服器提出上傳檔案的申請
									// 向伺服器端上傳檔案
									System.out.println("\n請輸入要上傳的檔案的路徑(絕對路徑或者相對於應用程式的相對路徑):");
									String uploadFileName = in.next();
									// 先把檔名傳給伺服器
									out.writeUTF("/" + uploadFileName);
									System.out.println("等待伺服器確認接收檔案...");
									boolean ret = input.readBoolean(); // 以utf編碼讀取
									if (!ret) {
										System.out.println("伺服器端拒絕接收此檔案!");
										continue;
									} else {
										BufferedInputStream fin = new BufferedInputStream(
												new FileInputStream(new File(uploadFileName)));
										byte[] buffer = new byte[1024];
										int flag = -1;
										System.out.print("上傳檔案中...");
										while ((flag = fin.read(buffer)) != -1) {
											// 當未讀取結束的時候就一直讀取並且傳送
											out.write(buffer);
										}
										fin.close();
										System.out.println("\n上傳結束!\n");
									}
									// out.close(); //不能這樣寫因為會把socket也關閉
									// socket.shutdownOutput(); //關閉輸出流而不關閉socket ,最後發現也不能這樣,因為output無法再次開啟
									// 構造結束標誌
									byte[] endstr = "檔案上傳結束".getBytes("utf-8");
									byte[] sendend = new byte[1024];
									for (int i = 0; i < endstr.length; i++) {
										sendend[i] = endstr[i];
									}
									out.write(sendend);

								} catch (Exception e) {
									e.printStackTrace();
									System.out.println("客戶端異常:" + e.getMessage());
								} finally {
									continue;
								}
							}

							case 2: { // 下載檔案
								out.writeUTF("申請下載檔案"); // 向伺服器提出下載檔案的申請
								boolean candown = input.readBoolean();
								if (candown) {
									System.out.println(input.readUTF());
									int fileId = in.nextInt();
									if (fileId == 0) {
										out.writeInt(0);
										continue;
									} else {
										out.writeInt(fileId);
										// 獲取檔名
										String fname = input.readUTF();
										// 接收檔案
										File df = new File("download/" + fname);
										if (!df.getParentFile().exists()) {
											df.getParentFile().mkdir();
										}
										if (!df.exists()) {
											df.createNewFile();
										}
										FileOutputStream fos = new FileOutputStream(df);
										byte[] buffer = new byte[1024];
										int flag = -1;
										System.out.print("下載檔案中...");

										// 構造結束標誌
										byte[] endstr = "檔案下載結束".getBytes("utf-8");
										byte[] sendend = new byte[1024];
										for (int i = 0; i < endstr.length; i++) {
											sendend[i] = endstr[i];
										}

										// 當未讀取到結束標誌的時候就一直讀取
										while ((flag = input.read(buffer)) != -1
												&& !new String(buffer, "utf-8").equals(new String(sendend, "utf-8"))) {
											fos.write(buffer);
											buffer = new byte[1024];
										}
										fos.close();
										System.out.println("檔案下載結束!\n");
										continue;
									}
								} else {
									System.out.println("伺服器上暫無檔案可以下載!");
									continue;
								}
							}

							case 3: { // 退出上傳下載
								out.close();
								input.close();
								f = false;
								System.out.println("客戶端已退出檔案上傳下載模組。");
								break;
							}

							default: {
								out.close();
								input.close();
								break;
							}

							}
						}

					} else { // 登入失敗
						System.out.println("登入驗證失敗!\n");
						if (socket != null) {
							try {
								socket.close();
							} catch (IOException e) {
								socket = null;
								//e.printStackTrace();
								System.out.println("客戶端 finally 異常:" + e.getMessage());
							}
						}
						continue;
					}

				} catch (IOException e) {
					System.out.println("客戶端登入異常:" + e.getMessage());
				} finally {
					break;
				}

			}

			case 3: {
				System.out.println("--------------------關閉客戶端--------------------");
				f1 = false;
				break;
			}

			default: {
				break;
			}

			}
		}

	}
}

君科沃特