美文网首页Java
Java通讯及Server

Java通讯及Server

作者: 洋芋掉到碗里去了 | 来源:发表于2018-02-11 11:45 被阅读12次

    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用法与优势

    相关文章

      网友评论

        本文标题:Java通讯及Server

        本文链接:https://www.haomeiwen.com/subject/ysektftx.html