Java

Java通讯及Server

2018-02-11  本文已影响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用法与优势

上一篇下一篇

猜你喜欢

热点阅读