网络(五)——基于BIO、NIO、AIO、Netty实现的服务端和客户端

目录

    • 1、基于BIO的编程
    • 2、基于NIO的编程:
    • 3、基于AIO实现:
    • 4、基于Netty实现

1、基于BIO的编程

用到了ServerSocket的(服务端),Socket(客户端);

服务端:

  1. 创建的的的的ServerSocket的的实例; 2.绑定占用端口; 3.通过接受()来监听并等待客户端的链接; 4.如果有客户端连接,则创建套接字实例; 5.进行读用操作; 6.关闭资源;

客户端:

  1. 创建套接字实例; 2.通过连接()连接服务端; 3.进行读写操作; 4.关闭资源;

哪些操作是阻塞的?
接收(),连接(),读(),写();

下面的代码是基于BIOS实现的一个简单的客户端和服务端一直通话的代码,服务端代码中用到了线程池,你也可以看作这是一个伪异步IO的代码:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @ClassName TestDemo4
 * @Description 服务端 持久通话
 * @Author lzq
 * @Date 2019/5/18 17:54
 * @Version 1.0
 **/

/**
 * 读线程,服务端
 */
class SendServer extends Thread {
    private DataInputStream dis;
    private Socket socket;

    public SendServer(Socket socket) {
        try {
            this.socket = socket;
            dis = new DataInputStream(socket.getInputStream());
        } catch (IOException e) {
            close();
        }
    }

    /**
     * 读
     */
    private void read() {
        try {
            String s = dis.readUTF();
            System.out.print("<客户端>:"+s);
        } catch (IOException e) {
            close();
        }
    }

    @Override
    public void run() {
        while (true) {
            read();
        }
    }

    private void close() {
        try {
            socket.close();
            dis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 写线程,服务端
 */
class ReceptionServer extends Thread {
    private DataOutputStream dos;
    private Socket socket;
    private BufferedReader bufferedReader;

    public ReceptionServer(Socket socket) {
        try {
            this.socket = socket;
            dos = new DataOutputStream(socket.getOutputStream());
            bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 写
     */
    private void writer() {
        try {
            String s = bufferedReader.readLine();
            dos.writeUTF(s+"\n");
            dos.flush();
        } catch (IOException e) {
            close();
        }
    }

    @Override
    public void run() {
        while (true) {
            writer();
        }
    }

    private void close() {
        try {
            socket.close();
            dos.close();
            bufferedReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public class TestDemo4 {
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8888);
            System.out.println("服务端已启动......");
            int count = 1;
            ExecutorService threadPoolExecutor = Executors.newCachedThreadPool();
            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println("有新客户端连接......"+count+++"......");
                threadPoolExecutor.submit(new SendServer(socket));
                threadPoolExecutor.submit(new ReceptionServer(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 * @ClassName TestDemo3
 * @Description 客户端  持久通话
 * 要实现持久通话,必须把它的读写操作分离出来,交给特定的线程去处理
 * @Author lzq
 * @Date 2019/5/18 17:54
 * @Version 1.0
 **/

/**
 * 处理发送数据的线程
 */
class Send extends Thread{
    private Socket socket;
    private DataOutputStream dos;
    private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

    public Send(Socket socket) {
        try {
            this.socket = socket;
            this.dos = new DataOutputStream(socket.getOutputStream());
        } catch (IOException e) {
            close();
        }
    }

    /**
     * 获取控制台数据
     * @return
     */
    private String getData() {
        String s = "";
        try {
            s = reader.readLine();
        } catch (IOException e) {
            close();
        }
        return s;
    }

    /**
     * 发送数据
     */
    private void send() {
        String s = getData();
        if(s != null && !s.equals("")) {
            try {
                dos.writeUTF(s+"\n");
                dos.flush();
            } catch (IOException e) {
                close();
            }
        }
    }

    @Override
    public void run() {
        while (true) {
            send();
        }
    }

    private void close() {
        try {
            socket.close();
            dos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 读取数据
 */
class Reception extends Thread {
    private Socket socket;
    private DataInputStream dis;

    public Reception(Socket socket) {
        try {
            this.socket = socket;
            this.dis = new DataInputStream(socket.getInputStream());
        } catch (IOException e) {
            close();
        }
    }

    /**
     * 读取数据的方法
     */
    private void reception() {
        try {
            String s = dis.readUTF();
            System.out.print("<服务端>:"+s);
        } catch (IOException e) {
            close();
        }
    }

    @Override
    public void run() {
        while (true) {
            reception();
        }
    }

    private void close() {
        try {
            socket.close();
            dis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public class TestDemo3 {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket();
        socket.connect(new InetSocketAddress("127.0.0.1",8888));
        System.out.println("已连接服务器......");
        new Send(socket).start();
        new Reception(socket).start();
    }
}

运行结果:
网络(五)——基于BIO、NIO、AIO、Netty实现的服务端和客户端_第1张图片
网络(五)——基于BIO、NIO、AIO、Netty实现的服务端和客户端_第2张图片

2、基于NIO的编程:

用到ServerSocketChannel(服务端)、SocketChannel(客户端);
channel(通道) 、Buffer(缓存)、selector(IO复用器或选择器)

服务端:

  1. 创建ServerSocketChannel实例(open);
  2. 绑定端口(bind);
  3. 将实例设置为非阻塞(configureBlocking);
  4. 实例化选择器(open);
  5. 将ServerSocketChannel实例注册到Selector选择器,并关注可接收事件;
  6. 选择器监听有事件发生则返回;
  7. 遍历感兴趣的事件集合,判断是否有可接收事件;
  8. 有可接收发生,获取对应通道,调用accept()方法获取SocketChannel实例;
  9. 将SocketChannel实例设置为非阻塞,注册到选择器,并关注读/写事件;
  10. 进行循环第6步,遍历集合判断是否有可读/可写事件发生;
  11. 通过Buffer从Channel里面读取数据/写入数据;
  12. 关闭打开的资源,包括打开的Selector选择器、ServerSocketChannel实例、SocketChannel实例;

客户端:

  1. 创建socketChanel实例;
  2. 将socketChanel实例设置为非阻塞;
  3. 创建selector实例;
  4. 连接服务端,立即返回结果不成功,将chanel注册到选择器中,并关注可连接事件
  5. selector实例监听事件,有事件发生则返回;
  6. 如该事件是可连接事件,则完成当前连接(finishConnect);
  7. 发送消息,接收消息;
  8. 关闭资源;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @ClassName TestDemo7
 * @Description 服务端
 * @Author lzq
 * @Date 2019/5/22 21:20
 * @Version 1.0
 **/
public class TestDemo7 {
    private static Charset charset = Charset.forName("UTF-8");
    private static ExecutorService executor = Executors.newCachedThreadPool();

    public static void main(String[] args) {
        new TestDemo7().start();
    }

    private void start() {
        ServerSocketChannel server = null;
        Selector selector = null;
        try {
            //创建ServerSocketChannel实例
            server = ServerSocketChannel.open();
            //将它设置为非阻塞
            server.configureBlocking(false);
            //绑定端口
            server.bind(new InetSocketAddress(8888));
            //实例化选择器
            selector = Selector.open();
            //将ServerSocketChannel实例添加到选择器,并关注可接收事件
            server.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("服务端已启动......");


            while (selector.select() > 0) {  //select()这是一个阻塞方法
                Iterator iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey next = iterator.next();
                    iterator.remove();
                    if (next.isAcceptable()) {  //有可连接事件发生
                        System.out.println("有客户端连接......");
                        ServerSocketChannel channel = (ServerSocketChannel) next.channel();
                        SocketChannel socketChannel = channel.accept();  //到这是不会阻塞的
                        socketChannel.configureBlocking(false); //设置为非阻塞
                        socketChannel.register(selector, SelectionKey.OP_READ); //添加到选择器,关注可读事件
                    }

                    if (next.isReadable()) { //有可读事件发生
                        SocketChannel channel = (SocketChannel) next.channel();  //获取通道
                        //读
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                        StringBuilder stringBuilder = new StringBuilder();

                        while (channel.read(byteBuffer) > 0) {
                            byteBuffer.flip();
                            stringBuilder.append(charset.decode(byteBuffer));
                        }
                        if(stringBuilder.length() > 0) {
                            System.out.println("<客户端>" + stringBuilder.toString());
                        }
                        //写
                        String msg = "欢迎";
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        buffer.put(msg.getBytes()); //将数据放到缓冲区
                        buffer.flip();  //调换读写指针
                        channel.write(buffer);
                    }
                }
            }
        } catch (ClosedChannelException e) {

        } catch (IOException e) {

        } finally {
            try {
                if(server != null) {
                    server.close();
                }
                if(selector != null) {
                    selector.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
 }
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * @ClassName TestDemo8
 * @Description 客户端
 * @Author lzq
 * @Date 2019/5/23 12:48
 * @Version 1.0
 **/
public class TestDemo8 {
    public static void main(String[] args) {
        SocketChannel socketChannel = null;
        Selector selector = null;
        try {
            //创建SocketChannel实例的
            socketChannel = SocketChannel.open();
            //将创建的SocketChannel实例设置为非阻塞
            socketChannel.configureBlocking(false);
            //创建选择器
            selector = Selector.open();
            //将创建的实例添加到选择器,并选择关注连接事件
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            //连接服务器,只写这句代码是连接不上服务端的,还需要后面的finishConnect()操作
            socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888));
            while (selector.select() > 0) {
                Iterator iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey next = iterator.next();
                    iterator.remove();

                    if (next.isConnectable()) {  //有可连接事件发生
                        SocketChannel channel = (SocketChannel) next.channel(); //获取通道

                        if (channel.isConnectionPending()) {  //判断此通道上是否在进行连接操作
                            channel.finishConnect(); //完成套接字通道的连接过程
                        }
                        channel.configureBlocking(false);
                        channel.register(selector, SelectionKey.OP_WRITE); //添加到选择器,关注可写事件
                    }

                    if (next.isWritable()) {//有可写事件发生
                        SocketChannel channel = (SocketChannel) next.channel(); //获取通道

                        //写
                        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
                        System.out.println("----------------");
                        String s = bufferedReader.readLine();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        buffer.put(s.getBytes());
                        buffer.flip();
                        channel.write(buffer);

                        channel.register(selector, SelectionKey.OP_READ); //关注读事件
                    }

                    if (next.isReadable()) {
                        SocketChannel channel = (SocketChannel) next.channel(); //获取通道
                        //读
                        ByteBuffer buffer1 = ByteBuffer.allocate(1024);
                        channel.read(buffer1);  //将数据读到缓冲区
                        buffer1.flip();
                        System.out.println("<服务端>" + new String(buffer1.array()));

                        channel.register(selector, SelectionKey.OP_WRITE);
                    }
                }

            }
        } catch (ClosedChannelException e) {

        } catch (IOException e) {

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

运行结果:
网络(五)——基于BIO、NIO、AIO、Netty实现的服务端和客户端_第3张图片
网络(五)——基于BIO、NIO、AIO、Netty实现的服务端和客户端_第4张图片

3、基于AIO实现:

这个一般不会用,直接上代码:

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.Future;

/**
 * @ClassName Server
 * @Description 服务端
 * @Author lzq
 * @Date 2019/5/30 12:40
 * @Version 1.0
 **/
public class Server {
   public static void main(String[] args) {
        try {
            Server server = new Server();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Server() throws Exception {
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
        InetSocketAddress inetSocketAddress = new InetSocketAddress("localhost", 80);
        serverSocketChannel.bind(inetSocketAddress);

        Future accept;

        while (true) {
            // accept()不会阻塞。
            accept = serverSocketChannel.accept();

            System.out.println("=================");
            System.out.println("服务器等待连接...");
            AsynchronousSocketChannel socketChannel = accept.get();// get()方法将阻塞。

            System.out.println("服务器接受连接");
            System.out.println("服务器与" + socketChannel.getRemoteAddress() + "建立连接");

            ByteBuffer buffer = ByteBuffer.wrap("欢迎".getBytes());
            Future write = socketChannel.write(buffer);

            while(!write.isDone()) {
                Thread.sleep(10);
            }

            System.out.println("服务器发送数据完毕.");
            socketChannel.close();
        }
    }

}
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.Future;

/**
 * @ClassName Client
 * @Description 客户端
 * @Author lzq
 * @Date 2019/5/30 14:06
 * @Version 1.0
 **/
public class Client {
    public static void main(String[] args) {
        AsynchronousSocketChannel socketChannel = null;
        try {
            socketChannel = AsynchronousSocketChannel.open();
            InetSocketAddress inetSocketAddress = new InetSocketAddress("localhost", 80);
            Future connect = socketChannel.connect(inetSocketAddress);

            while (!connect.isDone()) {
                Thread.sleep(10);
            }

            System.out.println("建立连接" + socketChannel.getRemoteAddress());

            ByteBuffer buffer = ByteBuffer.allocate(1024);
            Future read = socketChannel.read(buffer);

            while (!read.isDone()) {
                Thread.sleep(10);
            }

            System.out.println("接收服务器数据:" + new String(buffer.array(), 0, read.get()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果:
网络(五)——基于BIO、NIO、AIO、Netty实现的服务端和客户端_第5张图片
在这里插入图片描述

4、基于Netty实现

import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel;

/**
 * @ClassName ServerTestDemo1
 * @Description 服务端
 * @Author lzq
 * @Date 2019/5/31 15:16
 * @Version 1.0
 **/
public class ServerTestDemo1 {
    public static void main(String[] args) {
        //1、创建一个线程组,用来接收客户端连接
        EventLoopGroup boosGroup = new NioEventLoopGroup();
        //2、创建一个线程组,用来处理各种IO操作
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        //3、创建服务器端启动助手,配置参数
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(boosGroup,workerGroup)  //4、设置两个线程组
                .channel(NioServerSocketChannel.class)  //5、使用它作为服务器端通道的实现
                .option(ChannelOption.SO_BACKLOG,128) //6、设置线程中等待连接的个数
                .childOption(ChannelOption.SO_KEEPALIVE,true)  //7、保持活动的连接状态
                .childHandler(new ChannelInitializer() {  //8、创建一个通道初始化对象
                    @Override
                    //9、往链中添加自定义的Handler类
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(new NettyServerHandler());
                    }
                });

        System.out.println("服务器已就绪...");
        ChannelFuture channelFuture = null;
        try {
            channelFuture = bootstrap.bind(9999).sync();//10、绑定端口,非阻塞
            System.out.println("服务端已启动...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            //11、关闭通道,关闭线程组
            try {
                if(channelFuture != null) {
                    channelFuture.channel().closeFuture().sync();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boosGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }


    }
}
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

/**
 * @ClassName NettyServerHandler
 * @Description 服务端用的业务处理类
 * @Author lzq
 * @Date 2019/5/31 15:13
 * @Version 1.0
 **/
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    //读取数据事件
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf)msg;
        System.out.println("<客户端>"+byteBuf.toString(CharsetUtil.UTF_8));
    }

    @Override
    //读取数据完毕事件
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("欢迎",CharsetUtil.UTF_8));
    }


    @Override
    //发生异常事件
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * @ClassName ClientTestDemo2
 * @Description 客户端
 * @Author lzq
 * @Date 2019/5/31 15:16
 * @Version 1.0
 **/
public class ClientTestDemo2 {
    public static void main(String[] args) {
        //1、创建线程组
        EventLoopGroup group = new NioEventLoopGroup();
        //2、创建一个启动助手,完成配置
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)   //3、配置线程组
        .channel(NioSocketChannel.class)  //4、设置客户端的实现类
        .handler(new ChannelInitializer() {  //5、创建初始化对象
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
                //6、往链中添加Handler
                socketChannel.pipeline().addLast(new NettyClientHandler());
            }
        });

        System.out.println("客户端已就绪...");
        ChannelFuture channelFuture = null;
        try {
            //7、启动客户端,连接服务器端
            channelFuture = bootstrap.connect("127.0.0.1", 9999).sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            //8、关闭连接
            try {
                if(channelFuture != null) {
                    channelFuture.channel().closeFuture().sync();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            group.shutdownGracefully();
        }
    }
}
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

/**
 * @ClassName NettyClientHandler
 * @Description 客户端用的业务处理类
 * @Author lzq
 * @Date 2019/5/31 15:15
 * @Version 1.0
 **/
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    //通道就绪事件
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("你好",CharsetUtil.UTF_8));
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx,Object msg)抛出异常{ 
        ByteBuf byteBuf =(ByteBuf)msg; 
        的System.out.println( “<服务端>” + byteBuf.toString(CharsetUtil.UTF_8)); 
    } 
} 

你可能感兴趣的:(网络)