基于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等待时间从业务处理线程中抽取出来。

猜你喜欢

转载自blog.csdn.net/fhy569039351/article/details/84726422