1. 基于Socket实现的编程
1.1 实现的基本步骤
1. 创建一个ServerSocket的对象并监听端口(例如:6666)
2. 调用ServerSocket对象的accept方法,等待连接:
- 如果连接成功会返回一个Socket对象;
- 否则一直阻塞等待;
3. 为规范方法,创建ClientHandler和RequestHandler:
- ClientHandler用于处理Socket对象中所获取的InputStream和OutputStream,其需要实现Runnable的方法;
- RequestHandler用于返回从Socket中获得InputStream信息(命名为Requset),并返回"Hello"+Request的信息;
4. 对于ClientHandler:
- 处理请求:读取InputStream字节流信息,转成字符串形式,并解析;
- 处理响应:在RequestHandler中处理,并将其写入到OutputStream字节流中;
- 对于异常处理需要使用try, catch来获取,throw new RuntimeException(e);
5. 使用判断来等待连接并确定其是否中断。
1.2 代码实现
1. ClientHandler
public class ClientHandler implements Runnable { private final Socket clientSocket; private final RequestHandler requestHandler; public ClientHandler(Socket clientSocket, RequestHandler requestHandler) { this.clientSocket = clientSocket; this.requestHandler = requestHandler; } @Override public void run() { try (Scanner input = new Scanner(clientSocket.getInputStream())) { while (true) { String request = input.nextLine(); if (request.equals("quit")) { break; } System.out.println(String.format("Request from %s:%s", clientSocket.getRemoteSocketAddress(), request)); String response = requestHandler.handler(request); clientSocket.getOutputStream().write(response.getBytes()); } } catch (IOException e) { System.out.println("Caught exception: " + e); throw new RuntimeException(e); } } }
2. RequestHandler
public class RequestHandler { public String handler(String request) { return "Hello " + request + ".\n"; } }
3. SocketServer
public class SocketServer { private static RequestHandler sRequestHandler; public static void main(String[] args) throws IOException { sRequestHandler = new RequestHandler(); try (ServerSocket serverSocket = new ServerSocket(6666)) { System.out.println("Listening on" + serverSocket.getLocalSocketAddress()); while (true) { Socket clientSocket = serverSocket.accept(); System.out.println("Incoming Socket Address: " + clientSocket.getRemoteSocketAddress()); new ClientHandler(clientSocket, sRequestHandler).run(); } } } }
2. ThreadPool+Socket
Socket的方法实现简单,但是也会存在一定的问题,例如无法对于多用户的请求同时响应,这就需要使用ThreadPool来进行分配。
合理利用线程池能够带来三个好处。
- 第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。但是要做到合理的利用线程池,必须对其原理了如指掌。
ThreadPoolServer
public class ThreadPoolServer { public static void main(String[] args) throws IOException { ExecutorService executorService = Executors.newFixedThreadPool(5); RequestHandler requestHandler = new RequestHandler(); try (ServerSocket serverSocket = new ServerSocket(6666)) { System.out.println("Listening on" + serverSocket.getLocalSocketAddress()); while (true) { Socket clientSocket = serverSocket.accept(); System.out.println("Incoming Socket Address: " + clientSocket.getRemoteSocketAddress()); executorService.submit(new ClientHandler(clientSocket, requestHandler)); } } } }
3. Java NIO实现
虽然ThreadPool有其优势,但是ThreadPool毕竟有数量限制,例如上述代码中开启了5个,如果超出了5个就无法为其提供服务了。这就需要使用NIO方法来进行,从而达到异步操作的效果。
NIO(Non-blocking I/O,在Java领域,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服务器,成为解决高并发与大量连接、I/O处理问题的有效方式。
Java NIO 由以下几个核心部分组成:
- Channels
基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流。Buffers
数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。
Selectors
Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。
NioServer
public class NioServer { private static RequestHandler sRequestHandler; public static void main(String[] args) throws IOException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.configureBlocking(false); serverSocketChannel.bind(new InetSocketAddress(8888)); System.out.println("Listening on" + serverSocketChannel.getLocalAddress()); Selector selector = Selector.open(); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); while (true) { int selected = selector.select(); if (selected == 0) { continue; } Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); if (key.isAcceptable()) { ServerSocketChannel channel = (ServerSocketChannel) key.channel(); SocketChannel clientChannel = channel.accept(); System.out.println("Incoming Socket Address: " + clientChannel.getRemoteAddress()); clientChannel.configureBlocking(false); clientChannel.register(selector, SelectionKey.OP_READ); } if (key.isReadable()) { SocketChannel channel = (SocketChannel) key.channel(); String request = new String(byteBuffer.array()).trim(); byteBuffer.clear(); System.out.println(String.format("Request from %s:%s", channel.getRemoteAddress(), request)); sRequestHandler = new RequestHandler(); String responce = sRequestHandler.handler(request); channel.write(ByteBuffer.wrap(responce.getBytes())); } iterator.remove(); } } } }
参考并感谢
1. Java NIO浅析
2. Java NIO系列教程(一) Java NIO 概述
3. ThreadPool用法与优势
网友评论