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<Void> 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; } } }