一、bio写的时间服务器

1、一个请求对应一个线程版本

server端代码

通义灵码和github copilot_通义灵码和github copilot

通义灵码和github copilot_java_02

package com.spring.test.service.netty.server.bio;

import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author <a href="">尚晓飞</a>
 * @date 7:47 PM 2019/7/19
 */
public class BIOTimerServer {

    public static void main(String[] args) {
        //启动服务端
        BIOTimerServer server=new BIOTimerServer();
        server.startServer();
    }


    private int port = 8080;

    private ServerSocket serverSocket;

    public void startServer() {
        try {
            System.out.println("=====服务初始化开始=====");
            serverSocket = new ServerSocket(port);
            System.out.println("=====服务初始化完毕=====");
            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println("=====服务端接收一个请求=====");
                new SocketHandlerThread(socket).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (this.serverSocket != null) {
                try {
                    this.serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

/**
 * socket处理线程
 */
class SocketHandlerThread extends Thread {

    private Socket socket;

    SocketHandlerThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        System.out.println("======请求处理开始========");
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            //step1:初始化输入流(读请求)
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //step2:初始化输出流(写响应)
            out = new PrintWriter(this.socket.getOutputStream(),true);
            //step3:读取请求内容
            StringBuilder req = new StringBuilder();
            String temp;
            while (true){
               temp= in.readLine();
               if(StringUtils.isNotBlank(temp)){
                   req.append(temp);
                   break;
               }
            }
            //step4:处理请求
            System.out.println("请求内容为=>" + req.toString());
            String response = DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss");
            System.out.println("响应内容为=>" + response);
            //step5:响应请求
            out.println(response);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                out.close();
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("======请求处理结束========");
    }
}

View Code

client端代码

通义灵码和github copilot_通义灵码和github copilot

通义灵码和github copilot_java_02

package com.spring.test.service.netty.server.bio;

import org.apache.commons.lang3.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * @author <a href="">尚晓飞</a>
 * @date 6:19 PM 2019/7/21
 */
public class BIOTimerClient {

    public static void main(String[] args) {
        //客户端发送请求
        BIOTimerClient bioTimerClient=new BIOTimerClient(8080,"127.0.0.1");
        String resp=bioTimerClient.req("query Current Time");
        System.out.println("响应内容为=>"+resp);

    }

    private int port;
    private String localhost;

    public BIOTimerClient(int port, String address) {
        this.port = port;
        this.localhost = address;
    }


    public String req(String req) {
        Socket socket = null;
        BufferedReader in = null;
        PrintWriter out = null;
        StringBuilder response;
        try {
            socket = new Socket(this.localhost, this.port);
            //发送请求的输出流
            out = new PrintWriter(socket.getOutputStream(),true);
            //接收响应的输入流
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //发送请求
            System.out.println("==开始发送请求==>" + req);
            out.println(req);
            //读取响应
            response = new StringBuilder();
            String temp;
            while (StringUtils.isNotBlank(temp = in.readLine())) {
                response.append(temp);
            }
            System.out.println("==客户端接收响应成功==");
            return response.toString();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            if(in!=null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(out!=null){
                out.close();
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

View Code

1、服务端使用线程池版本

server端代码

通义灵码和github copilot_通义灵码和github copilot

通义灵码和github copilot_java_02

package com.spring.test.service.netty.server.bio;

import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * @author <a href="">尚晓飞</a>
 * @date 7:47 PM 2019/7/19
 */
public class BIOTimerServer {

    public static void main(String[] args) {
        //启动服务端
        BIOTimerServer server = new BIOTimerServer();
        server.startServer();
    }


    private int port = 8080;

    private ServerSocket serverSocket;

    /**
     * 初始化一个工作线程池,用来处理客户端的请求
     * 优点:相对于第一版的一个请求一个工作线程,不会出现客户单大量请求,导致服务端开启大量线程,把服务端资源耗尽的问题。
     * 缺点:固定的线程池大小,代表服务端有一定的处理极限(并发数就是线程池的最大数),一但出现服务端或客户端的I/O因为网络原因出现阻塞
     *     则服务端线程资源就会被长时间占用,线程池占满,服务端无法处理新的请求,客户端就会出现超时或访问被拒绝的情况
     */
    private Executor executor= Executors.newFixedThreadPool(20);

    public void startServer() {
        try {
            System.out.println("=====服务初始化开始=====");
            serverSocket = new ServerSocket(port);
            System.out.println("=====服务初始化完毕=====");
            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println("=====服务端接收一个请求=====");
                //模拟一个工作线程池,用来处理客户端请求
                executor.execute(new SocketHandlerThread(socket));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (this.serverSocket != null) {
                try {
                    this.serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

/**
 * socket处理线程
 */
class SocketHandlerThread extends Thread {

    private Socket socket;

    SocketHandlerThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        System.out.println("======请求处理开始========");
        BufferedReader in = null;
        PrintWriter out = null;
        try {
            //step1:初始化输入流(读请求)
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //step2:初始化输出流(写响应)
            out = new PrintWriter(this.socket.getOutputStream(), true);
            //step3:读取请求内容
            StringBuilder req = new StringBuilder();
            String temp;
            while (true) {
                temp = in.readLine();
                if (StringUtils.isNotBlank(temp)) {
                    req.append(temp);
                    break;
                }
            }
            //step4:处理请求
            System.out.println("请求内容为=>" + req.toString());
            String response = DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss");
            System.out.println("响应内容为=>" + response);
            //step5:响应请求
            out.println(response);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                out.close();
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("======请求处理结束========");
    }
}

View Code

client端代码

通义灵码和github copilot_通义灵码和github copilot

通义灵码和github copilot_java_02

package com.spring.test.service.netty.server.bio;

import org.apache.commons.lang3.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * @author <a href="">尚晓飞</a>
 * @date 6:19 PM 2019/7/21
 */
public class BIOTimerClient {

    public static void main(String[] args) {
        //客户端发送请求
        BIOTimerClient bioTimerClient=new BIOTimerClient(8080,"127.0.0.1");
        String resp=bioTimerClient.req("query Current Time");
        System.out.println("响应内容为=>"+resp);

    }

    private int port;
    private String localhost;

    public BIOTimerClient(int port, String address) {
        this.port = port;
        this.localhost = address;
    }


    public String req(String req) {
        Socket socket = null;
        BufferedReader in = null;
        PrintWriter out = null;
        StringBuilder response;
        try {
            socket = new Socket(this.localhost, this.port);
            //发送请求的输出流
            out = new PrintWriter(socket.getOutputStream(),true);
            //接收响应的输入流
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //发送请求
            System.out.println("==开始发送请求==>" + req);
            out.println(req);
            //读取响应
            response = new StringBuilder();
            String temp;
            while (StringUtils.isNotBlank(temp = in.readLine())) {
                response.append(temp);
            }
            System.out.println("==客户端接收响应成功==");
            return response.toString();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            if(in!=null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(out!=null){
                out.close();
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

View Code

二、nio写的时间服务器

server端代码

通义灵码和github copilot_通义灵码和github copilot

通义灵码和github copilot_java_02

package com.spring.test.service.netty.server.nio;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author <a href="">尚晓飞</a>
 * @date 7:24 PM 2019/7/21
 */
public class NIOTimerServer extends Thread {


    public static void main(String[] args) {
        //启动服务端
        NIOTimerServer server=new NIOTimerServer(8080);
        server.start();
    }


    private int port;
    /**
     * 多路复用器
     */
    private Selector selector;
    /**
     * 服务端通信的管道
     */
    private ServerSocketChannel serverSocketChannel;
    /**
     * 服务端是否停止
     */
    private volatile boolean isStop;


    public NIOTimerServer(int port) {
        try {
            System.out.println("========初始化服务端开始=========");
            this.port=port;
            //新建多路复用器
            selector = Selector.open();
            //新建服务端管道,用于监听客户端连接
            serverSocketChannel = ServerSocketChannel.open();
            //将I/O设置为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            //绑定端口号
            serverSocketChannel.socket().bind(new InetSocketAddress(this.port), 1024);
            //将serverSocketChannel注册到多路复用器上,用于监听客户端接入的OP_ACCEPT事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("========初始化服务端结束==========");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @Override
    public void run() {
        while (!isStop) {
            //通过多路复用器,监听网络事件
            try {
                //如果没有网络事件,则阻塞(1秒钟退出阻塞),若存在网络事件,则直接向下一句代码执行。
                selector.select(1000);
                //获取已准备就绪的网络事件
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                //遍历已经就绪的网络事件,并进行处理
                Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();
                SelectionKey selectionKey;
                while (selectionKeyIterator.hasNext()) {
                    selectionKey = selectionKeyIterator.next();
                    selectionKeyIterator.remove();
                    handlerKey(selectionKey);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if(selector!=null){
            try {
                selector.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


    private void handlerKey(SelectionKey selectionKey) throws IOException {
        //判断selectionKey是否是有效的
        if (selectionKey.isValid()) {
            if (selectionKey.isAcceptable()) {
                //客户端新接入
                ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
                SocketChannel socketChannel = serverSocketChannel.accept();
                socketChannel.configureBlocking(false);
                //注册监听读取请求的网络事件
                socketChannel.register(selector, SelectionKey.OP_READ);
            }

            if (selectionKey.isReadable()) {
                //当前网络事件,是读取客户端的请求内容
                SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                int readBytes = socketChannel.read(byteBuffer);
                if (readBytes > 0) {
                    //读取的客户端请求内容
                    byteBuffer.flip();
                    byte[] bytes=new byte[byteBuffer.remaining()];
                    byteBuffer.get(bytes);
                    String requestBoy=new String(bytes,"utf-8");
                    System.out.println("客户端请求内容=>"+requestBoy);
                    //处理请求
                    String response = DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss");
                    //响应请求
                    doResponse(socketChannel,response);
                }else if(readBytes<0){
                    //客户端链路关闭
                    selectionKey.cancel();
                    socketChannel.close();
                }else {
                    //读到0字节忽略
                }
            }
        }
    }


    private void doResponse(SocketChannel socketChannel,String response) throws IOException {
        if(StringUtils.isNotBlank(response)){
            byte[] resp=response.getBytes();
            ByteBuffer byteBuffer=ByteBuffer.allocate(resp.length);
            byteBuffer.put(resp);
            byteBuffer.flip();
            socketChannel.write(byteBuffer);
        }
    }
}

View Code

client端代码

通义灵码和github copilot_通义灵码和github copilot

通义灵码和github copilot_java_02

package com.spring.test.service.netty.server.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author <a href="">尚晓飞</a>
 * @date 8:48 PM 2019/7/21
 */
public class NIOTimerClient extends  Thread{

    public static void main(String[] args) {
        NIOTimerClient nioTimerClient=new NIOTimerClient("127.0.0.1",8080);
        nioTimerClient.start();
    }


    private String address;
    private int port;
    private Selector selector;
    private SocketChannel socketChannel;
    private volatile boolean isStop;

    NIOTimerClient(String address,int port){
        this.address=address;
        this.port=port;
        try {
            selector= Selector.open();
            socketChannel=SocketChannel.open();
            socketChannel.configureBlocking(false);
        }catch (Exception e){

        }
    }


    @Override
    public void run() {
       try {
           //向服务端发送请求
           doConnection();
       }catch (Exception e){
           e.printStackTrace();
       }

       while (!isStop){
           try {
            selector.select(1000);
            Set<SelectionKey> selectionKeys= selector.selectedKeys();
            Iterator<SelectionKey> selectionKeyIterator= selectionKeys.iterator();
            SelectionKey selectionKey=null;
            while (selectionKeyIterator.hasNext()){
                selectionKey=selectionKeyIterator.next();
                selectionKeyIterator.remove();
                try {
                    handler(selectionKey);
                }catch (Exception e){
                    if(selectionKey!=null){
                        selectionKey.cancel();
                        if(selectionKey.channel()!=null){
                            selectionKey.channel().close();
                        }
                    }
                }

            }
           }catch (Exception e){
                e.printStackTrace();
           }finally {

           }
       }

       if(selector!=null){
           try {
               selector.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
    }

    /**
     * 向服务端发起连接
     * @throws IOException
     */
    private void doConnection() throws IOException {
        if(socketChannel.connect(new InetSocketAddress(address,port))){
            //连接成功
            socketChannel.register(selector, SelectionKey.OP_READ);
            doRequest(socketChannel);
        }else {
            socketChannel.register(selector,SelectionKey.OP_CONNECT);
        }
    }

    /**
     * 向服务端发起请求
     */
    private void doRequest(SocketChannel socketChannel) throws IOException {
        byte[] request="query current time!".getBytes();
        ByteBuffer byteBuffer=ByteBuffer.allocate(request.length);
        byteBuffer.put(request);
        byteBuffer.flip();
        socketChannel.write(byteBuffer);
        if(!byteBuffer.hasRemaining()){
            System.out.println("向服务端请求发送成功");
        }
    }


    private void handler(SelectionKey selectionKey) throws IOException {
        if(selectionKey.isValid()){
            SocketChannel socketChannel= (SocketChannel) selectionKey.channel();
            //当前是网络连接完成事件
            if(selectionKey.isConnectable()){
                if(socketChannel.finishConnect()){
                    socketChannel.register(selector,SelectionKey.OP_READ);
                    doRequest(socketChannel);
                }else {
                    //连接失败
                    System.out.println("连接失败");
                }
            }

            //当前是网络读取响应事件
            if(selectionKey.isReadable()){
                ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
                int readResp=socketChannel.read(byteBuffer);
                if(readResp>0){
                    byteBuffer.flip();
                    byte[] bytes=new byte[byteBuffer.remaining()];
                    byteBuffer.get(bytes);
                    String resp=new String(bytes,"utf-8");
                    System.out.println("服务端响应为=>"+resp);
                    isStop=true;
                }else if(readResp<0){
                    //对端链路关闭
                    selectionKey.cancel();
                    socketChannel.close();
                }else {
                    //读到0字节忽略
                }

            }
        }
    }
}

View Code

 NIO比BIO的编程复杂度高,为什么应用却广泛使用?

  • 网络连接I/O非阻塞:客户端的链接操作是异步的,可以通过在多路复用器注册OP_CONNECT等后续结果,不需要像BIO客户端那样被同步阻塞。
  • 网络读写I/O非阻塞,异步化:SocketChannel的读写操作都是异步的,如果没有可读写的数据,它不会同步等待,直接返回。这样I/O通信线程就可以处理其他链路,不需要同步等待这个链路可用。
  • 网络连接数无限制:线程模型优化:由于JDK的Selector在Liunx等主流操作系统上通过epoll实现,它没有句柄数的限制(只受限于操作系统的最大句柄数或者对单个进程的句柄限制),这意味着一个Selector线程可以同时处理成千上万个客户端连接,而且性能不会随着客户端的增加而线性下降。因此,它非常适合做高性能,高负载的网络服务器。

 

三、AIO写的时间服务器

1、server代码

通义灵码和github copilot_通义灵码和github copilot

通义灵码和github copilot_java_02

package com.spring.test.service.netty.server.aio;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.CountDownLatch;

/**
 * 
 * @date 12:55 PM 2019/8/11
 */
public class AsyncTimeServerHandler implements Runnable {

    private int port;
    CountDownLatch countDownLatch;
    AsynchronousServerSocketChannel asynchronousServerSocketChannel;


    public static void main(String[] args) {
        //启动时间服务器
        Thread timerServer=new Thread(new AsyncTimeServerHandler(8080));
        timerServer.start();
    }


    public AsyncTimeServerHandler(int port) {
        this.port = port;
        try {
            asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();
            asynchronousServerSocketChannel.bind(new InetSocketAddress(port));
            System.out.println("======The time server is start in port:" + port + "======");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void run() {
        countDownLatch = new CountDownLatch(1);
        doAccept();
        try {
            countDownLatch.await();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    public void doAccept() {
        //获取客户单接入的请求
        asynchronousServerSocketChannel.accept(this,new AcceptCompletionHandler());
    }
}

/**
 * 获取客户端连接的回调函数
 */
class AcceptCompletionHandler implements CompletionHandler<AsynchronousSocketChannel, AsyncTimeServerHandler> {

    @Override
    public void completed(AsynchronousSocketChannel result, AsyncTimeServerHandler attachment) {
        //用于接收下一个客户端接入的请求
        attachment.asynchronousServerSocketChannel.accept(attachment, this);
        //处理当前请求
        ByteBuffer buffer=ByteBuffer.allocate(1024);
        result.read(buffer,buffer,new ReadCompletionHandler(result));
    }

    @Override
    public void failed(Throwable exc, AsyncTimeServerHandler attachment) {
        //接收请求失败,则把服务端关闭
        exc.printStackTrace();
        attachment.countDownLatch.countDown();
    }
}


/**
 * 读取连接的请求内容回调函数
 */
class ReadCompletionHandler  implements CompletionHandler<Integer,ByteBuffer>{

    private AsynchronousSocketChannel asynchronousSocketChannel;

    public ReadCompletionHandler(AsynchronousSocketChannel asynchronousSocketChannel) {
        this.asynchronousSocketChannel = asynchronousSocketChannel;
    }

    @Override
    public void completed(Integer result, ByteBuffer attachment) {
        attachment.flip();
        byte[] requestBody=new byte[attachment.remaining()];
        attachment.get(requestBody);
        try {
            String req=new String(requestBody,"UTF-8");
            System.out.println("The time server receive order:"+req);
            String currentTime="QUERY TIME ORDER".equals(req)? DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss"):"BAD ORDER";
            doWrite(currentTime);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void failed(Throwable exc, ByteBuffer attachment) {
        //关闭请求
        try {
            this.asynchronousSocketChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    private void doWrite(String currentTime){
        if(StringUtils.isNotBlank(currentTime)){
            byte[] bytes=currentTime.getBytes();
            ByteBuffer writeBuffer=ByteBuffer.allocate(bytes.length);
            writeBuffer.put(bytes);
            writeBuffer.flip();
            asynchronousSocketChannel.write(writeBuffer, writeBuffer, new CompletionHandler<Integer, ByteBuffer>() {
                @Override
                public void completed(Integer result, ByteBuffer attachment) {
                    //如果没有发送完,则继续发送
                    if(attachment.hasRemaining()){
                        asynchronousSocketChannel.write(attachment,attachment,this);
                    }
                }

                @Override
                public void failed(Throwable exc, ByteBuffer attachment) {
                    //如果发送失败,则关闭channel
                    try {
                        asynchronousSocketChannel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

View Code

2、client代码

通义灵码和github copilot_通义灵码和github copilot

通义灵码和github copilot_java_02

package com.spring.test.service.netty.server.aio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.CountDownLatch;

/**
 * @author
 * @date 2:33 PM 2019/8/11
 */
public class AsyncTimeClientHandler implements CompletionHandler<Void,AsyncTimeClientHandler>,Runnable{

    private AsynchronousSocketChannel asynchronousSocketChannel;
    private String host;
    private int port;
    private CountDownLatch countDownLatch;


    public static void main(String[] args) {
        //启动客户单发送请求
        Thread timerClient=new Thread(new AsyncTimeClientHandler("127.0.0.1",8080));
        timerClient.start();
    }




    public AsyncTimeClientHandler(String host,int port){
        this.host=host;
        this.port=port;
        try {
            asynchronousSocketChannel=AsynchronousSocketChannel.open();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        countDownLatch=new CountDownLatch(1);
        asynchronousSocketChannel.connect(new InetSocketAddress(host,port),this,this);
        try {
            countDownLatch.await();
        }catch (Exception e){
            e.printStackTrace();
        }
        try {
            asynchronousSocketChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void completed(Void result, AsyncTimeClientHandler attachment) {
        //连接成功,发送请求
        byte[] req="QUERY TIME ORDER".getBytes();
        ByteBuffer writeBuffer=ByteBuffer.allocate(req.length);
        writeBuffer.put(req);
        writeBuffer.flip();
        asynchronousSocketChannel.write(writeBuffer, writeBuffer, new CompletionHandler<Integer, ByteBuffer>() {
            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                if(attachment.hasRemaining()){
                    //半包写,则继续发送,将请求数据完整发送给服务端
                    asynchronousSocketChannel.write(attachment,attachment,this);
                }else{
                    //写成功了,则需要读取服务端响应
                    ByteBuffer readBuffer=ByteBuffer.allocate(1024);
                    asynchronousSocketChannel.read(readBuffer, readBuffer, new CompletionHandler<Integer, ByteBuffer>() {
                        @Override
                        public void completed(Integer result, ByteBuffer attachment) {
                            //读取服务端响应成功
                            attachment.flip();
                            byte[] bytes=new byte[attachment.remaining()];
                            attachment.get(bytes);
                            String responseBody;
                            try {
                                //读取响应成功
                                responseBody=new String(bytes,"UTF-8");
                                System.out.println("Now is:"+responseBody);
                                //读取响应成功,关闭客户端连接
                                countDownLatch.countDown();
                            }catch (Exception e){
                                e.printStackTrace();
                            }

                        }

                        @Override
                        public void failed(Throwable exc, ByteBuffer attachment) {
                            //读取服务端响应失败
                            try {
                                asynchronousSocketChannel.close();
                                countDownLatch.countDown();
                            }catch (Exception e){
                                e.printStackTrace();
                            }
                        }
                    });
                }
            }

            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                //向服务端发送请求报文失败,关闭客户端
                try {
                    asynchronousSocketChannel.close();
                    countDownLatch.countDown();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    public void failed(Throwable exc, AsyncTimeClientHandler attachment) {
        //向服务端发起连接失败
        try {
            asynchronousSocketChannel.close();
            countDownLatch.countDown();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

View Code

 

四、netty写时间服务器

jdkversion:1.8

nettyversion:5.0.0.Alpha1

1、server代码

通义灵码和github copilot_通义灵码和github copilot

通义灵码和github copilot_java_02

package com.spring.test.service.netty.nettydemo.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @date 4:13 PM 2019/8/11
 */
public class TimerServer {


    public static void main(String[] args) {
        TimerServer timerServer=new TimerServer();
        timerServer.bind(8080);
    }

    public void bind(int port){
        //step1:配置服务端的NIO线程组(一个线程组用于服务端接收客户端连接,一个线程组用于进行socketChannel的网络读写)
        EventLoopGroup bossGroup=new NioEventLoopGroup();
        EventLoopGroup workerGroup=new NioEventLoopGroup();
        try {
            //netty用于启动NIO服务的辅助启动类,目的四降低服务端开发复杂度
            ServerBootstrap serverBootstrap=new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,1024)
                    .childHandler(new ChildChannelHandler());
            //step2:绑定端口,同步等待成功
            ChannelFuture f=serverBootstrap.bind(port).sync();

            //step3:等待服务端监听端口关闭
            f.channel().closeFuture().sync();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //step4:优雅退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }



    }
}



package com.spring.test.service.netty.nettydemo.server;


import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;

/**
 * @author 
 * @date 4:21 PM 2019/8/11
 */
public class ChildChannelHandler extends ChannelInitializer<SocketChannel>{
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        socketChannel.pipeline().addLast(new TimerServerHandler());
    }
}





package com.spring.test.service.netty.nettydemo.server;


import org.apache.commons.lang.time.DateFormatUtils;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

/**
 * @author 
 * @date 4:26 PM 2019/8/11
 */
public class TimerServerHandler extends ChannelHandlerAdapter{

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //读取请求
        ByteBuf buffer= (ByteBuf) msg;
        byte[] req=new byte[buffer.readableBytes()];
        buffer.readBytes(req);
        String reqbody=new String(req,"utf-8");
        System.out.println("The time server receive order:"+reqbody);
        String currentTime="QUERY TIME ORDER".equals(reqbody)? DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss"):"BAD ORDER";

        //请求响应
        ByteBuf resp=Unpooled.copiedBuffer(currentTime.getBytes());
        System.out.println("The time server resp order:"+currentTime);
        //将相应结果,异步发送给客户端
        ctx.write(resp);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //将消息发送队列中的消息写入socketChannel中发送给对方。
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

View Code

2、client代码

通义灵码和github copilot_通义灵码和github copilot

通义灵码和github copilot_java_02

package com.spring.test.service.netty.nettydemo.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * @author 
 * @date 5:41 PM 2019/8/11
 */
public class TimerClient {

    public static void main(String[] args) throws InterruptedException {
        TimerClient timerClient=new TimerClient();
        timerClient.connect(8080,"127.0.0.1");
    }

    public void connect(int port,String host) throws InterruptedException {
        //step1:配置NIO客户端线程组
        EventLoopGroup group=new NioEventLoopGroup();
        try {
            Bootstrap bootstrap=new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY,true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new TimerClientHandler());
                        }
                    });

            //发起异步连接操作(调用同步方法,等待连接成功)
            ChannelFuture f=bootstrap.connect(host,port).sync();

            //等待客户端链路关闭
            f.channel().closeFuture().sync();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //优雅的退出,释放NIO线程组
            group.shutdownGracefully();
        }
    }
}



package com.spring.test.service.netty.nettydemo.client;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

/**
 * @author 
 * @date 5:49 PM 2019/8/11
 */
public class TimerClientHandler extends ChannelHandlerAdapter {

    private final ByteBuf firstMessage;

    public TimerClientHandler(){
        byte[] req="QUERY TIME ORDER".getBytes();
        firstMessage= Unpooled.buffer(req.length);
        firstMessage.writeBytes(req);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("释放资源");
        ctx.close();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //发送请求
        ctx.writeAndFlush(firstMessage);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //读取响应
        ByteBuf buf= (ByteBuf) msg;
        byte[] resp=new byte[buf.readableBytes()];
        buf.readBytes(resp);
        String responseBody=new String(resp,"utf-8");
        System.out.println("Now time is:"+responseBody);

    }
}

View Code