由於有其他的工作,網盤做了一部分不得不放手了,
我希望有時間的其他朋友可以繼續工作,雖然網絡上有很多現成的網盤代碼,不過還是希望自己能做一個,並借鑒一些優秀的思想來實現,下面說下實現過程,有些部分需要改進
一、數據庫的設計,目前只涉及到用戶表,當然還有其他的,你可以根據需要來增加
#用戶表
create table m_user(
id int primary key auto_increment,
name varchar(32) not null unique,
password char(32) not null,
`gender` enum('男','女') NOT NULL DEFAULT '男',
phone varchar(20),
email varchar(50) not null,
reg_date char(16) not null,
reg_ip varchar(15) not null,
last_login_date char(16),
last_login_ip varchar(15)
);
二、數據源的設置,我這里使用c3p0數據源,當然你可以使用dbcp或者其他的
配置c3p0-config.xml文件就可以了,網絡上有詳細的配置項,或者在我源碼里面下載,在最后公布下載地址
在這里我寫一個簡單的JdbcUtil,當然還可以編寫一些復雜的操作,工作不允許我繼續往下寫了,你可以集成一些操作,就像hibernate那樣
public class JdbcUtil { /** * 數據庫連接管理器 */ // private static Logger log = Logger.getLogger(JdbcUtil.class); /*初始化數據庫連接池*/ private static DataSource dataSource = new ComboPooledDataSource(); /*獲取數據源*/ public DataSource getDataSource(){ return dataSource; } /*獲取連接*/ public static Connection getConnection() throws SQLException{ return dataSource.getConnection(); } /*釋放連接*/ public static void free(ResultSet rs,PreparedStatement ps,Connection conn){ if(null != rs){ try { rs.close(); } catch (SQLException e) {} } if(null != ps){ try { ps.close(); } catch (SQLException e) {} } if(null != conn){ try { conn.close(); } catch (SQLException e) {} } } public static void free(PreparedStatement ps,Connection conn){ if(null != ps){ try { ps.close(); } catch (SQLException e) {} } if(null != conn){ try { conn.close(); } catch (SQLException e) {} } } }
三、我這里先說說服務端
1.socket線程池
池的作用想必大家都知道,循環利用資源,我這里的這個池只是簡單的池,沒有時間再完成一個復雜的工作了
cn.mike.server.ServerThread是一個負責處理用戶請求的線程,我們要創建一批這樣的線程,並由cn.mike.server.ServerThreadPool管理,代碼如下:
public class ServerThreadPool { /** * 服務端線程池 */ private final static Logger log = Logger.getLogger(ServerThreadPool.class); //線程組 public static LinkedList<ServerThread> threadPool = new LinkedList<ServerThread>(); private static int maxPoolSize;//最大連接數 private static int minPoolSize;//最小連接數 private static int initialPoolSize;//初始化連接數 private static int maxIdleTime;//連接的最大空閑時間,單位:秒 private static int acquireIncrement;//在當前連接數耗盡的時候,一次獲取的新的連接數 static int maxWaitUserTime;//線程等待用戶操作的最大時間,到達最大時間未傳送數據,則進行線程釋放 public ServerThreadPool(){ initProperties(); initThreadPool(); } /* * 初始化配置 */ public void initProperties(){ System.out.println("正在啟動線程池..."); System.out.println("正在加載線程池配置文件..."); Properties pro = new Properties(); HashMap<String, String> propertiesMap = new HashMap<String, String>(); try { pro.load(ServerThreadPool.class.getClassLoader().getResourceAsStream(ServerThreadPoolConfig.PROPS_FILE_RSRC_PATH)); propertiesMap.put(ServerThreadPoolConfig.MAX_POOL_SIZE, pro.getProperty(ServerThreadPoolConfig.MAX_POOL_SIZE)); propertiesMap.put(ServerThreadPoolConfig.MIN_POOL_SIZE, pro.getProperty(ServerThreadPoolConfig.MIN_POOL_SIZE)); propertiesMap.put(ServerThreadPoolConfig.INITIAL_POOL_SIZE, pro.getProperty(ServerThreadPoolConfig.INITIAL_POOL_SIZE)); propertiesMap.put(ServerThreadPoolConfig.MAX_IDLE_TIME, pro.getProperty(ServerThreadPoolConfig.MAX_IDLE_TIME)); propertiesMap.put(ServerThreadPoolConfig.ACQUIRE_INCREMENT, pro.getProperty(ServerThreadPoolConfig.ACQUIRE_INCREMENT)); propertiesMap.put(ServerThreadPoolConfig.MAX_WAIT_USER_TIME, pro.getProperty(ServerThreadPoolConfig.MAX_WAIT_USER_TIME)); if(null != propertiesMap.get(ServerThreadPoolConfig.MAX_POOL_SIZE)){ ServerThreadPool.maxPoolSize = Integer.parseInt(propertiesMap.get(ServerThreadPoolConfig.MAX_POOL_SIZE)); }else{ ServerThreadPool.maxPoolSize = 100; } if(null != propertiesMap.get(ServerThreadPoolConfig.MIN_POOL_SIZE)){ ServerThreadPool.minPoolSize = Integer.parseInt(propertiesMap.get(ServerThreadPoolConfig.MIN_POOL_SIZE)); }else{ ServerThreadPool.minPoolSize = 5; } if(null != propertiesMap.get(ServerThreadPoolConfig.INITIAL_POOL_SIZE)){ ServerThreadPool.initialPoolSize = Integer.parseInt(propertiesMap.get(ServerThreadPoolConfig.INITIAL_POOL_SIZE)); }else{ ServerThreadPool.initialPoolSize = 5; } if(null != propertiesMap.get(ServerThreadPoolConfig.MAX_IDLE_TIME)){ ServerThreadPool.maxIdleTime = Integer.parseInt(propertiesMap.get(ServerThreadPoolConfig.MAX_IDLE_TIME)); }else{ ServerThreadPool.maxIdleTime = 10; } if(null != propertiesMap.get(ServerThreadPoolConfig.ACQUIRE_INCREMENT)){ ServerThreadPool.acquireIncrement = Integer.parseInt(propertiesMap.get(ServerThreadPoolConfig.ACQUIRE_INCREMENT)); }else{ ServerThreadPool.acquireIncrement = 1; } if(null != propertiesMap.get(ServerThreadPoolConfig.MAX_WAIT_USER_TIME)){ ServerThreadPool.maxWaitUserTime = Integer.parseInt(propertiesMap.get(ServerThreadPoolConfig.MAX_WAIT_USER_TIME)); }else{ ServerThreadPool.maxWaitUserTime = 60000; } } catch (Exception e) { log.error("線程池配置文件加載出錯,請確保文件threadPool.properties存在,並正確配置!"); System.exit(1); } System.out.println("線程池配置加載成功,配置信息如下:"); System.out.println("#################################"); System.out.println("最大連接數:"+ServerThreadPool.maxPoolSize); System.out.println("最小連接數:"+ServerThreadPool.minPoolSize); System.out.println("初始化連接數:"+ServerThreadPool.initialPoolSize); System.out.println("連接的最大空閑時間:"+ServerThreadPool.maxIdleTime+" 秒"); System.out.println("在當前連接數耗盡的時候,一次獲取的新的連接數:"+ServerThreadPool.acquireIncrement); System.out.println("線程等待用戶操作的最大時間:"+ServerThreadPool.maxWaitUserTime+" 毫秒"); System.out.println("#################################"); } /* * 初始化服務線程 */ public void initThreadPool(){ for(int i=0;i<ServerThreadPool.initialPoolSize;i++){ ServerThread st = new ServerThread(); st.start(); threadPool.add(st); } } /* * 線程池動態調整器 */ public void poolAdjust(){ } }
一些配置規范我把它放在cn.mike.server.ServerThreadPoolConfig里
public class ServerThreadPoolConfig { /* * 線程池配置 */ //配置文件路徑 public final static String PROPS_FILE_RSRC_PATH = "threadPool.properties"; //最大連接數 public final static String MAX_POOL_SIZE = "maxPoolSize"; //最小連接數 public final static String MIN_POOL_SIZE = "minPoolSize"; //初始化連接數 public final static String INITIAL_POOL_SIZE= "initialPoolSize"; //連接的最大空閑時間,單位:秒 public final static String MAX_IDLE_TIME = "maxIdleTime"; //在當前連接數耗盡的時候,一次獲取的新的連接數 public final static String ACQUIRE_INCREMENT = "acquireIncrement"; //線程等待用戶操作的最大時間,到達最大時間未傳送數據,則進行線程釋放 public final static String MAX_WAIT_USER_TIME = "maxWaitUserTime"; }
threadPool.properties文件用於配置線程池的一些選項,我這里的設置可能不夠完整,你可以根據需要增加
還有一個重要問題,這里需要開啟一個線程來管理線程池里線程的數量,實現動態調整,這工作我並沒完成,希望你能把它完成
cn.mike.server.Server是一個用於接收用戶請求的分配器,處理一些初始化工作
public class Server { /** * 25米 網盤 服務端 */ private final static Logger log = Logger.getLogger(Server.class); private int listenPort = 8594;//監聽端口 private static ServerSocket ss; static LinkedList<Socket> taskQueue = new LinkedList<Socket>();//任務隊列 public static Map<String,Session> sessionMap = new HashMap<String,Session>(); /*初始化線程池*/ ServerThreadPool stp = new ServerThreadPool(); /*數據庫連接工具*/ public JdbcUtil jdbcUtil = new JdbcUtil(); private int maxWaitUserTime = ServerThreadPool.maxWaitUserTime;//最大等待操作時間 public static void main(String[] args) { System.out.println("正在啟動服務器..."); Server server = new Server(); new TaskHandle().start(); server.init(); } public void init(){ // 初始化數據庫連接池 System.out.println("正在初始化數據庫連接池..."); try { System.out.println("#################################"); JdbcUtil.getConnection().close(); System.out.println("#################################"); System.out.println("數據庫連接池創建成功!"); } catch (SQLException e1) { log.error("數據庫連接池創建失敗!"); System.exit(1); } /*開啟監聽服務*/ try { ss = new ServerSocket(listenPort); System.out.println("服務器啟動成功,正在監聽端口:"+listenPort); while(true){ Socket socket = ss.accept(); socket.setSoTimeout(maxWaitUserTime);//設置最大連接時長 System.out.println("發現客戶端連接,IP:"+socket.getRemoteSocketAddress()); process(socket);//轉入線程池處理 } } catch (IOException e) { System.out.println("服務器啟動失敗,請確保端口:"+listenPort+"不被其他程序占用!"); } } /* * 服務線程處理客戶端請求 */ public void process(Socket socket){ if(ServerThreadPool.threadPool.size()>0){//如果池中還有服務線程 ServerThreadPool.threadPool.removeFirst().startWork(socket); } else if(taskQueue.size()<1000){//若沒有,並且隊列長度小於1000,則加入任務隊列 taskQueue.add(socket); } else{ try { socket.close(); } catch (IOException e) { log.error("關閉客戶端socket失敗!"); } } } } /* *開啟定時器,處理任務隊列,每隔 500 毫秒查看有沒有空閑連接 */ class TaskHandle extends Thread{ static LinkedList<Socket> taskQueue = Server.taskQueue; public TaskHandle(){ System.out.println("隊列任務處理器開啟.."); } public void run() { try { while(true){ Thread.sleep(500); if(taskQueue.size()>0 && ServerThreadPool.threadPool.size()>0){//如果池中還有服務線程,則處理任務隊列 ServerThreadPool.threadPool.removeFirst().startWork(Server.taskQueue.removeFirst()); } } } catch (InterruptedException e) { e.printStackTrace(); } } }
可能有些東西讀者不是很明白,這需要對socket編程熟悉點,當然我的設計也有些問題,希望讀者能提改進意見,有時間就修改
服務端啟動如下:
四、下面說說客戶端
客戶端就是登陸界面和管理網盤界面
1.登陸界面:cn.mike.client.ClientLogin
不是很漂亮,勉強用着吧,當然你可以設計得漂亮點
登陸成功后是這樣的:
大概做到這里了,數據的上傳下載等沒時間實現了,感興趣的朋友可以繼續我的工作
源碼我放到CSDN下面:http://download.csdn.net/detail/mzlqh/4514886