Java Socket:单线程/多线程/线程池ServerSocket编程

  Socket将网络抽象成可读写的字节流,其对用户掩藏了底层繁杂的细节。Java使用Socket对象实现客户端网络流程:请求连接、发/收数据、关闭链接;使用ServerSocket对象实现服务端网络流程:绑定端口/本地接口、侦听请求、接受请求、收/发数据和关闭连接。下面总结了模拟daytime协议服务端的程序,分别用单线程、多线程和固定线程池的方式实现,每种实现方式在一切无视应用场景的情形下都难分伯仲,笔者看来对于简单的daytime服务器单线程实现足够,但是任何场景下都不要使用无限制线程数量的实现方式。

  • SingleThreadServer.java 单线程服务方式:
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

/**
 * Created by Cheney Hwang on 2017/5/1.
 * 简短协议适用于单线程短连接,处理快无额外开销
 */
public class SingleThreadServer {
    public final static int PORT=5555;

    public static void main(String[] args){
        try(ServerSocket server=new ServerSocket(PORT)){
            while(true){
                try(Socket socket=server.accept()){
                    Writer out=new OutputStreamWriter(socket.getOutputStream(),"ASCII");
                    Date now=new Date();
                    out.write(now.toString()+"\r\n");
                    //时间协议,添加不依赖于平台的换行符
                    out.flush();
                    out.close();

                }catch (IOException ex){
                    //忽略连接异常
                }
            }
        }catch (IOException ex){//端口占用或者无权使用将抛出异常
            System.err.println(ex);
        }
    }
}
  • MultiThreadServer.java 无限制数量多线程服务方式:
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

/**
 * Created by Cheney Hwang on 2017/5/1.
 * 每个客户连接请求都生成一个处理线程,可以防止慢客户端阻塞所有其他客户端
 * 这种服务方式现实是不可能采用的,容易遭受DOS攻击
 */
public class MultiThreadServer {
    public final static int PORT=5555;

    public static void main(String[] args){
        try(ServerSocket server=new ServerSocket(PORT)){
            while(true) {
                try {
                    Socket client = server.accept();
                    Thread task = new TimeThread(client);
                    task.start();

                } catch (IOException ex) {
                    //忽略连接异常
                }
            }
        }catch (IOException ex){//端口占用或者无权使用将抛出异常
            System.err.println(ex);
        }
    }

    private static class TimeThread extends Thread{
        private Socket client;

        public TimeThread(Socket client){
            this.client=client;
        }

        @Override
        public void run(){
            try{
                Writer out=new OutputStreamWriter(client.getOutputStream());
                Date now=new Date();
                out.write(now.toString()+"\r\n");
                //时间协议,添加不依赖于平台的换行符
                out.flush();
                out.close();

            }catch (IOException ex){
                System.err.println(ex);
            }finally {
                try {
                    client.close();
                }catch (IOException ex){
                    //忽略
                }
            }
        }
    }
}
  • FixedPoolServer.java 固定数量线程池服务方式:
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by Cheney Hwang on 2017/5/1.
 * 固定线程数量的服务方式可以拒绝客户端连接请求,但是自身不会崩溃
 */
public class FixedPoolServer {
    public final static int PORT = 5555;

    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(50);
        //固定大小线程池

        try (ServerSocket server = new ServerSocket(PORT)) {
            while (true) {
                try {
                    Socket client = server.accept();
                    Callable task = new TimeThread(client);
                    pool.submit(task);

                } catch (IOException ex) {
                    //忽略连接异常
                }
            }
        } catch (IOException ex) {//端口占用或者无权使用将抛出异常
            System.err.println(ex);
        }
    }

    private static class TimeThread implements Callable<Void> {
        private Socket client;

        public TimeThread(Socket client) {
            this.client = client;
        }

        @Override
        public Void call() {
            try {
                Writer out = new OutputStreamWriter(client.getOutputStream());
                Date now = new Date();
                out.write(now.toString() + "\r\n");
                //时间协议,添加不依赖于平台的换行符
                out.flush();
                out.close();

            } catch (IOException ex) {
                System.err.println(ex);
            } finally {
                try {
                    client.close();
                } catch (IOException ex) {
                    //忽略
                }
            }
            return null;
        }
    }
}

  • 下面是示例客户端:
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.Socket;

/**
 * Created by Cheney Hwang on 2017/5/1.
 */
public class Client {
    public final static String HOST="localhost";
    public final static int PORT=5555;

    public static void main(String[] args){
        try(Socket client=new Socket(HOST,PORT)){//指定服务器的地址和端口
            Reader in=new InputStreamReader(client.getInputStream());
            int c;
            StringBuilder builder=new StringBuilder();
            while ((c=in.read())!=-1){
                builder.append((char)c);
            }
            System.out.println(builder.toString());
            in.close();

        }catch (IOException ex){
            System.err.println("Cant connect to server!");
        }
    }
}

你可能感兴趣的:(Java)