基於Socket的服務端多線程模式——服務端和客戶端代碼

本文代碼來源於《實戰java高併發程序設計》葛一鳴 郭超 著

學習這本書的過程中,感覺這一部分比較重要,自己做下總結,也希望能給大家提供些幫助。

      本代碼模擬簡單的Echo服務器,對於Echo服務器,他會讀取客戶端的一個輸入,並將這個輸入原封不動地返回給客戶端。雖然實例很簡單,但麻雀雖小五臟俱全,需要一套完整的Socket處理機制。適合拿來學習。下面貼出本例代碼。

 1、服務端代碼:

       服務器會爲每一個客戶端連接啓動一個線程,這個新的線程會全心全意爲這個客戶端服務。同時,爲了接受客戶端連接,服務器還會額外使用一個派發線程,如上圖:

package socket.demo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 
 * @author FHY
 * 多線程應用的服務端代碼
 *
 */
public class MultiThreadEchoServer {
	//創建一個線程池,不限制連接的線程數量
	public static ExecutorService tp = Executors.newCachedThreadPool();
	
	static class HandleMsg implements Runnable{
		Socket clientSocket;
		public HandleMsg(Socket clientSocket){
			this.clientSocket = clientSocket;
		}
		
		@Override
		public void run() {
			BufferedReader is = null;
			PrintWriter os = null;	
			try{
				//輸入流 (使用了裝飾模式)
				is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
				//輸出流
				os = new PrintWriter(clientSocket.getOutputStream(), true);
				String inputLine = null;
				long b = System.currentTimeMillis();
				while ((inputLine = is.readLine())!= null ){
					os.println(inputLine);
				}
				long e = System.currentTimeMillis();
				System.out.println("Spend: "+(e-b));
			}catch(IOException e){
				e.printStackTrace();				
			}finally{
				try{
					if(is != null) is.close();
					if(os != null) os.close();
					clientSocket.close();
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		}
	}

	
	public static void main(String[] args) {
		ServerSocket echoSocket = null;
		Socket clientSocket = null;
		try{
			//設置服務端的端口號
			echoSocket = new ServerSocket(8000);
		}catch(IOException e){
			System.out.println(e);
		}
		
		while(true){
			try{
				//接受客戶端
				clientSocket = echoSocket.accept();
				System.out.println(clientSocket.getRemoteSocketAddress() + " connet!");
				tp.execute(new HandleMsg(clientSocket));
			}catch(IOException e){
				System.out.println(e);
			}
		}
	}

}

       這就是一個支持多線程的服務端的核心內容。它的特點是,在相同可支持的線程範圍內,可以儘量多地支持客戶端的數量,同時和單線程服務器相比,它可以更好的使用多核CPU。

2、客戶端代碼:

package socket.demo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
 * 
 * @author FHY
 * 多線程應用的客戶端代碼
 *
 */

public class ClientSocketDemo {
	public static void main(String[] args) {	
		Socket client = null;
		PrintWriter writer = null;
		BufferedReader reader = null;
		try{
			client = new Socket();
			//客戶端連接到服務端
			client.connect(new InetSocketAddress("localhost", 8000), 1000*6);
			writer = new PrintWriter(client.getOutputStream(), true);
			writer.println("Hello!");
			writer.flush();
			
			reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
			System.out.println("from server:" +reader.readLine());
		}catch(UnknownHostException e){
			e.printStackTrace();
		}catch (IOException e) {
			e.printStackTrace();
		}finally {
			try{
				if(writer != null) writer.close();
				if(reader != null) reader.close();
				if(client != null) client.close();
			}catch(IOException e){
				System.out.println(e);
			}
			
		}
	}	
}

總結:這種多線程的服務器開發模式是及其常用的,對於絕大多數應用來說,這種模式可以很好地工作,但是如果你想讓程序工作地更加有效,就必須知道這種模式的一個重大弱點——那就是它傾向於讓CPU進行IO等待。

使用java的NIO就可以將上面的網絡IO等待時間從業務處理線程中抽取出來。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章