java socket 线程池echo服务器

2016-03-26  本文已影响768人  snoweek

java socket系列文章
java socket 单线程echo服务器
java socket 多线程echo服务器
java socket 线程池echo服务器

java socket 多线程echo服务器这篇文章中,改善了单线程echo服务器中只能处理一个客户端请求的弊端,利用一个客户端对应一个线程的方法,达到处理多个客户端的目的。

但是,每个新线程都会消耗系统资源:

  1. 创建一个线程会占用 CPU 周期,而且每个线程都会建立自己的数据结构(如,栈),也要消耗系统内存。
  2. 当一个线程阻塞时,JVM 将保存其状态,选择另外一个线程运行,并在上下文转换(context switch)时恢复阻塞线程的状态。随着线程数的增加,线程将消耗越来越多的系统资源,这将最终导致系统花费更多的时间来处理上下文转换盒线程管理,更少的时间来对连接进行服务。

在这种情况下,加入一个额外的线程实际上可能增加客户端总服务的时间。当线程数目过多的时候,必然导致每个客户端请求的处理时间增长,使用户体验明显变差。

我们可以通过限制线程总数并重复使用线程来避免这个问题。我们让服务器在启动时创建一个由固定线程数量组成的线程池,线程池的工作原理:

  1. 当一个新的客户端连接请求传入服务器,它将交给线程池中的一个线程处理,
  2. 该线程处理完这个客户端之后,又返回线程池,继续等待下一次请求。
  3. 如果连接请求到达服务器时,线程池中所有的线程都已经被占用,它们则在一个队列中等待,直到有空闲的线程可用。

与一客户一线程服务器一样,线程池服务器首先创建一个 ServerSocket 实例。
然后创建 N 个线程,每个线程反复循环,从(共享的)ServerSocket 实例接收客户端连接。当多个线程同时调用一个 ServerSocket 实例的 accept()方法时,它们都将阻塞等待,直到一个新的连接成功建立,然后系统选择一个线程,为建立起的连接提供服务,其他线程则继续阻塞等待。
线程在完成对一个客户端的服务后,继续等待其他的连接请求,而不终止。如果在一个客户端连接被创建时,没有线程在 accept()方法上阻塞(即所有的线程都在为其他连接服务),系统则将新的连接排列在一个队列中,直到下一次调用 accept()方法。

客户端代码:
public class Client {
    public static void main(String[] args) throws IOException {      
        Socket client = new Socket("127.0.0.1", 20012);  
        //客户端请求与本机在20011端口建立TCP连接 
        client.setSoTimeout(10000);             
        BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); 
        //获取键盘输入        
        PrintStream out = new PrintStream(client.getOutputStream());          
        //获取Socket的输出流,用来发送数据到服务端                  
        BufferedReader buf =  new BufferedReader(new InputStreamReader(client.getInputStream()));  
        //获取Socket的输入流,用来接收从服务端发送过来的数据 
        boolean flag = true;  
        while(flag){  
            System.out.print("输入信息:");  
            String str = input.readLine();  
            out.println(str);  
            //发送数据到服务端   
            if("bye".equals(str)){  
                flag = false;  
            }else{  
                try{  
                    //从服务器端接收数据有个时间限制(系统自设,也可以自己设置),超过了这个时间,便会抛出该异常  
                    String echo = buf.readLine();  
                    System.out.println(echo);  
                }catch(SocketTimeoutException e){  
                    System.out.println("Time out, No response");  
                }  
            }  
        }  
        input.close();  
        if(client != null){  
            //如果构造函数建立起了连接,则关闭套接字,如果没有建立起连接,自然不用关闭  
            client.close(); //只关闭socket,其关联的输入输出流也会被关闭  
        }  
    }  
}

服务器端代码
public class ThreadPoolServer {
    public static void main(String[] args) {
        ServerSocket server;
        ExecutorService executor=Executors.newFixedThreadPool(2);
        try {
            server = new ServerSocket(20012);
               Socket client = null;
                while(true){ 
                    System.out.println("服务器端等待客户端发起连接请求");     
                    client = server.accept();
                    System.out.println("客户端向服务器端发起了连接请求,且连接成功");
                    executor.execute(new Handler(client));                 
                }
        } catch (IOException e) {
            e.printStackTrace();
        }  
    }
}

线程处理代码
public class Handler extends Thread{
    private Socket client; 
    PrintStream out;
    BufferedReader buf;
    public Handler(Socket client){
        this.client = client; 
        try {
            out = new PrintStream(client.getOutputStream());
            buf = new BufferedReader(new InputStreamReader(client.getInputStream())); 
            
        } catch (IOException e) {
            e.printStackTrace();
        }               
    } 
    public void run(){
         try{  
             boolean flag =true;  
             while(flag){                
                 String str =  buf.readLine();  
                 if(str == null || "".equals(str)){  
                     flag = false;  
                 }else{  
                     if("bye".equals(str)){  
                         flag = false;  
                     }else{  
                        System.out.println("服务器从客户端接受到的数据:"+str);
                         out.println("echo:" + str);  
                     }  
                 }  
             }  
             out.close();  
             client.close();  
         }catch(Exception e){  
             e.printStackTrace();  
         }  
     }      
}

此项目的完整代码可以到我的github,java-socket进行下载。

上一篇下一篇

猜你喜欢

热点阅读