网络编程和NIO

  • 查看本机IP地址,在控制台输入:

  • ipconfig

  • 检查网络是否连通,在控制台输入:

  • ping 空格 IP地址
    ping 220.181.57.216
    ping www.baidu.com

端口号:用两个字节表示的整数,它的取值范围是0~65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。

InetAddress类的方法

  • static InetAddress getLocalHost() 获得本地主机IP地址对象

  • static InetAddress getByName(String host) 根据IP地址字符串或主机名获得对应的IP地址对象

  • String getHostName();获得主机名

  • String getHostAddress();获得IP地址字符串

  • Socket : 一个该类的对象就代表一个客户端程序。

    • Socket(String host, int port) 根据ip地址字符串和端口号创建客户端Socket对象

      • 注意事项:只要执行该方法,就会立即连接指定的服务器程序,如果连接不成功,则会抛出异常。 如果连接成功,则表示三次握手通过。

    • OutputStream getOutputStream(); 获得字节输出流对象

    • InputStream getInputStream();获得字节输入流对象

    • void close();关闭Socket, 会自动关闭相关的流

    • 补充:关闭通过socket获得的流,会关闭socket,关闭socket,同时也会关闭通过socket获得的流

  • ServerSocket : 一个该类的对象就代表一个服务器端程序。

    • ServerSocket(int port); 根据指定的端口号开启服务器。

    • Socket accept(); 等待客户端连接并获得与客户端关联的Socket对象 如果没有客户端连接,该方法会一直阻塞

    • void close();关闭ServerSocket

TCP通信案例1

客户端:
1.创建Socket对象,指定要连接的服务器的ip地址和端口号
2.通过Socket对象获得字节输出流对象
3.使用字节输出流写字符串数据到连接通道中
4.释放资源

服务器:
1.创建ServerSocket对象,指定服务器的端口号
2.调用accpet()方法,接收客户端请求,建立连接,返回Socket对象
3.通过返回的Socket对象获得字节输入流
4.读数据
5.释放资源

客户端代码实现

public class Client {
    public static void main(String[] args) throws Exception{
        //1.创建Socket对象,指定要连接的服务器的ip地址和端口号
        Socket socket = new Socket("127.0.0.1",6666);

        //2.通过Socket对象获得字节输出流对象
        OutputStream os = socket.getOutputStream();

        //3.使用字节输出流写字符串数据到连接通道中
        os.write("服务器你好,今晚约吗?".getBytes());

        //4.释放资源
        socket.close();
    }
}

服务端代码实现

public class Server {
    public static void main(String[] args) throws Exception{
        //1.创建ServerSocket对象,指定服务器的端口号
        ServerSocket ss = new ServerSocket(6666);

        //2.调用accpet()方法,接收客户端请求,建立连接,返回Socket对象
        Socket socket = ss.accept();

        //3.通过返回的Socket对象获得字节输入流
        InputStream is = socket.getInputStream();

        //4.读数据
        byte[] bys = new byte[1024];
        int len = is.read(bys);
        System.out.println("服务器接收到的信息:"+new String(bys,0,len));

        //5.释放资源
        socket.close();
        ss.close();
    }
}

TCP通信案例2

  • 客户端向服务器发送字符串数据,服务器回写字符串数据给客户端(模拟聊天)

客户端:
1.创建Socket对象,指定要连接的服务器的ip地址和端口号
2.通过Socket对象获得字节输出流对象
3.使用字节输出流写字符串数据到连接通道中
4.通过Socket对象获得字节输入流对象
5.读服务器回写的数据
6.释放资源

服务器:
1.创建ServerSocket对象,指定服务器的端口号
2.调用accpet()方法,接收客户端请求,建立连接,返回Socket对象
3.通过返回的Socket对象获得字节输入流
4.读数据
5.通过返回的Socket对象获得字节输出流
6.使用字节输出流写字符串数据到连接通道中
7.释放资源

实现

TCP客户端代码

public class Client {
    public static void main(String[] args) throws Exception{
        //1.创建Socket对象,指定要连接的服务器的ip地址和端口号
        Socket socket = new Socket("127.0.0.1",6666);

        //2.通过Socket对象获得字节输出流对象
        OutputStream os = socket.getOutputStream();

        //3.使用字节输出流写字符串数据到连接通道中
        os.write("服务器你好,今晚约吗?".getBytes());

        //4.通过Socket对象获得字节输入流对象
        InputStream is = socket.getInputStream();

        //5.读服务器回写的数据
        byte[] bys = new byte[1024];
        int len = is.read(bys);
        System.out.println("客户端接收到的信息:"+new String(bys,0,len));

        //6.释放资源
        socket.close();
    }
}

  • 服务端代码实现

public class Server {
    public static void main(String[] args) throws Exception{
        //1.创建ServerSocket对象,指定服务器的端口号
        ServerSocket ss = new ServerSocket(6666);

        //2.调用accpet()方法,接收客户端请求,建立连接,返回Socket对象
        Socket socket = ss.accept();

        //3.通过返回的Socket对象获得字节输入流
        InputStream is = socket.getInputStream();

        //4.读数据
        byte[] bys = new byte[1024];
        int len = is.read(bys);
        System.out.println("服务器接收到的信息:"+new String(bys,0,len));

        //5.通过返回的Socket对象获得字节输出流
        OutputStream os = socket.getOutputStream();

        //6.使用字节输出流写字符串数据到连接通道中
        os.write("客户端你好,今晚小树林见!".getBytes());

        //7.释放资源
        socket.close();
        ss.close();
    }
}

扩展模拟循环聊天

public class Server {
    public static void main(String[] args) throws Exception{
        //1.创建ServerSocket对象,指定服务器的端口号
        ServerSocket ss = new ServerSocket(6666);

        //2.调用accpet()方法,接收客户端请求,建立连接,返回Socket对象
        Socket socket = ss.accept();

        // 循环
        while (true) {
            //3.通过返回的Socket对象获得字节输入流
            InputStream is = socket.getInputStream();

            //4.读数据
            byte[] bys = new byte[1024];
            int len = is.read(bys);
            System.out.println("服务器接收到的信息:" + new String(bys, 0, len));

            //5.通过返回的Socket对象获得字节输出流
            OutputStream os = socket.getOutputStream();

            //6.使用字节输出流写字符串数据到连接通道中
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入您要对客户端说的话:");
            String msg = sc.next();
            os.write(msg.getBytes());

            //7.释放资源
            //socket.close();
            //ss.close();
        }
    }
}
客户端

public class Client {
    public static void main(String[] args) throws Exception{
        //1.创建Socket对象,指定要连接的服务器的ip地址和端口号
        Socket socket = new Socket("127.0.0.1",6666);

        // 循环
        while (true) {
            //2.通过Socket对象获得字节输出流对象
            OutputStream os = socket.getOutputStream();

            //3.使用字节输出流写字符串数据到连接通道中
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入您要对服务器说的话:");
            String msg = sc.next();

            os.write(msg.getBytes());

            //4.通过Socket对象获得字节输入流对象
            InputStream is = socket.getInputStream();

            //5.读服务器回写的数据
            byte[] bys = new byte[1024];
            int len = is.read(bys);
            System.out.println("客户端接收到的信息:"+new String(bys,0,len));

            //6.释放资源
            //socket.close();
        }
    }
}

文件上传案例

public class Server {
    public static void main(String[] args) throws Exception {
        //服务器:
        //1.创建ServerSocket对象,指定服务器的端口号
        ServerSocket ss = new ServerSocket(7777);

        //2.调用accept方法,接收请求建立连接,返回Socket对象
        Socket socket = ss.accept();

        //3.通过返回的Socket对象获得字节输入流对象
        InputStream is = socket.getInputStream();

        //4.创建字节输出流对象,关联目的地文件路径
        FileOutputStream fos = new FileOutputStream("day12\\bbb\\mm2.jpg");

        //5.定义一个byte数组,用来存储读取到的字节数据
        byte[] bys = new byte[8192];

        //5.定义一个int变量,用来存储读取到的字节个数
        int len;

        //6.循环读
        while ((len = is.read(bys)) != -1) {
            //7.写数据
            fos.write(bys,0,len);
        }

        //8.释放资源
        fos.close();
        socket.close();
        ss.close();
    }
}

客户端

public class Client {
    public static void main(String[] args) throws Exception{
        //客户端:
        //1.创建Socket对象,指定要连接的服务器的ip地址和端口号
        Socket socket = new Socket("127.0.0.1",7777);

        //2.创建字节输入流对象,关联要上传的文件路径
        FileInputStream fis = new FileInputStream("day12\\aaa\\mm.jpg");

        //3.通过Socket对象获得字节输出流对象
        OutputStream os = socket.getOutputStream();

        //4.定义一个byte数组,用来存储读取到的字节数据
        byte[] bys = new byte[8192];

        //5.定义一个int变量,用来存储读取到的字节个数
        int len;

        //6.循环读
        while ((len = fis.read(bys)) != -1) {
            //7.写数据
            os.write(bys,0,len);
        }
        //8.释放资源
        fis.close();
        socket.close();

    }
}

文件上传成功后服务器回写字符串数据

public class Server {
    public static void main(String[] args) throws Exception {
        //服务器:
        //1.创建ServerSocket对象,指定服务器的端口号
        ServerSocket ss = new ServerSocket(7777);

        //2.调用accept方法,接收请求建立连接,返回Socket对象
        Socket socket = ss.accept();

        //3.通过返回的Socket对象获得字节输入流对象
        InputStream is = socket.getInputStream();

        //4.创建字节输出流对象,关联目的地文件路径
        FileOutputStream fos = new FileOutputStream("day12\\bbb\\mm4.jpg");

        //5.定义一个byte数组,用来存储读取到的字节数据
        byte[] bys = new byte[8192];

        //5.定义一个int变量,用来存储读取到的字节个数
        int len;

        System.out.println("服务器1");
        //6.循环读
        // 循环读客户端写过来的数据
        while ((len = is.read(bys)) != -1) {// 卡
            //7.写数据
            fos.write(bys,0,len);
        }
        System.out.println("服务器2");

        //8.通过Socket获得字节输出流对象
        OutputStream os = socket.getOutputStream();

        //9.回写上传成功信息给客户端
        os.write("文件上传成功!".getBytes());

        //10.释放资源
        fos.close();
        socket.close();
        ss.close();
    }
}
客户端

public class Client {
    public static void main(String[] args) throws Exception{
        //客户端:
        //1.创建Socket对象,指定要连接的服务器的ip地址和端口号
        Socket socket = new Socket("127.0.0.1",7777);

        //2.创建字节输入流对象,关联要上传的文件路径
        FileInputStream fis = new FileInputStream("day12\\aaa\\mm.jpg");

        //3.通过Socket对象获得字节输出流对象
        OutputStream os = socket.getOutputStream();

        //4.定义一个byte数组,用来存储读取到的字节数据
        byte[] bys = new byte[8192];

        //5.定义一个int变量,用来存储读取到的字节个数
        int len;

        //6.循环读
        while ((len = fis.read(bys)) != -1) {
            //7.写数据
            os.write(bys,0,len);
        }

        // 问题原因:客户端不再写数据过来了,而服务器不知道,所以服务器一直在等待读客户端写过来的数据
        // 解决办法: 客户端要告诉服务器,客户端不再写数据过来了
        // 禁止客户端写数据的功能
        socket.shutdownOutput();

        System.out.println("客户端1");

        //8.通过Socket获得字节输入流对象
        InputStream is = socket.getInputStream();

        //9.读服务器回写的信息
        int len2 = is.read(bys);// 卡
        System.out.println("服务器回写的信息是:"+new String(bys,0,len2));

        //10.释放资源
        fis.close();
        socket.close();

    }
}

优化文件上传案例

public class Server {
    public static void main(String[] args) throws Exception {
        //服务器:
        //1.创建ServerSocket对象,指定服务器的端口号
        ServerSocket ss = new ServerSocket(7777);

        // 循环
        while (true) {

            //2.调用accept方法,接收请求建立连接,返回Socket对象
            Socket socket = ss.accept();

            // 开启线程,实现上传任务
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        //3.通过返回的Socket对象获得字节输入流对象
                        InputStream is = socket.getInputStream();

                        //4.创建字节输出流对象,关联目的地文件路径
                        FileOutputStream fos = new FileOutputStream("day12\\bbb\\" + System.currentTimeMillis() + ".jpg");

                        //5.定义一个byte数组,用来存储读取到的字节数据
                        byte[] bys = new byte[8192];

                        //5.定义一个int变量,用来存储读取到的字节个数
                        int len;

                        System.out.println("服务器1");
                        //6.循环读
                        // 循环读客户端写过来的数据
                        while ((len = is.read(bys)) != -1) {// 卡
                            //7.写数据
                            fos.write(bys, 0, len);
                        }
                        System.out.println("服务器2");

                        //8.通过Socket获得字节输出流对象
                        OutputStream os = socket.getOutputStream();

                        //9.回写上传成功信息给客户端
                        os.write("文件上传成功!".getBytes());

                        //10.释放资源
                        fos.close();
                        socket.close();
                        //ss.close();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }).start();

        }
    }
}

NIO

  • 同步: 调用方法之后,必须要得到一个返回值

  • 异步: 调用方法之后,没有返回值,但是会有回调函数,回调函数指的是满足条件之后会自动执行的方法

阻塞与非阻塞:在进行阻塞操作时,当前线程会处于阻塞状态,无法从事其他任务,只有当条件就绪才能继续,比如ServerSocket新连接建立完毕,或者数据读取、写入操作完成;而非阻塞则是不管IO操作是否结束,直接返回,相应操作在后台继续处理

Buffer类(缓冲区)

方式一:在堆中创建缓冲区:public static ByteBuffer allocate(int capacity)

方式二: 在系统内存创建缓冲区:public static ByteBuffer allocatDirect(int capacity)方式三:通过数组创建缓冲区:public static ByteBuffer wrap(byte[] arr)

添加数据-put

  • public ByteBuffer put(byte b):向当前可用位置添加数据。

  • public ByteBuffer put(byte[] byteArray):向当前可用位置添加一个byte[]数组

  • public ByteBuffer put(byte[] byteArray,int offset,int len):添加一个byte[]数组的一部分

  • public byte[] array(); 获取封装的字节数组

限制-limit

public class Test2_limit {
    public static void main(String[] args) {
        // 创建ByteBuffer对象
        ByteBuffer b = ByteBuffer.allocate(10);
        System.out.println("limit:" + b.limit());// 10

        // 添加单个byte数据
        b.put((byte) 10);
        b.put((byte) 20);
        b.put((byte) 30);
        System.out.println("limit:" + b.limit());// 10

        // 修改limit
        b.limit(3);
        System.out.println("limit:" + b.limit());// 3

        //b.put((byte) 30);// 报错,因为3索引这个位置是不能使用了BufferOverflowException异常
    }
}

位置-position

  • 有两个相关方法:

    • public int position():获取当前可写入位置索引。

    • public Buffer position(int p):更改当前可写入位置索引。

    • public class Test5_mark {
          public static void main(String[] args) {
              // 创建ByteBuffer对象
              ByteBuffer b = ByteBuffer.allocate(10);

              // 添加单个byte数据
              b.put((byte) 10);
              b.put((byte) 20);
              b.put((byte) 30);
              System.out.println("position:"+b.position());// 3

              // 标记一下
              b.mark();

              b.put((byte) 10);
              b.put((byte) 20);
              b.put((byte) 30);
              System.out.println("position:"+b.position());// 6

              // 重置一下
              b.reset();
              System.out.println("position:"+b.position());// 3

              // 往索引为3的位置添加元素
              b.put((byte) 100);
              // [10,20,30,100,20,30,0,0,0,0]
              System.out.println("封装的数组:"+ Arrays.toString(b.array()));

          }
      }

    • clear和flip

    • - public Buffer clear():还原缓冲区的状态。
        - 将position设置为:0
        - 将限制limit设置为容量capacity;
        - 丢弃标记mark。
      - public Buffer flip():缩小limit的范围。
        - 将当前position位置设置为0;
        - 将limit设置为当前position位置;
    • Channel(通道)

    • public int read(ByteBuffer b) 读数据,把读到的数据放在b数组中,返回读取到的字节个数,如果读到文件的末尾就返回-1

    • public int write(ByteBuffer b) 写数据,把数据写到目的地文件中,写的是position到limit之间的数据

    • FileChannel:从文件读写数据的 输入流和输出流

    • DatagramChannel:读写UDP网络协议数据 Datagram

    • SocketChannel:读写TCP网络协议数据 Socket

    • ServerSocketChannel:可以监听TCP连接 ServerSocket

    • FileChannel类的基本使用

    • FileInputStream fis=new FileInputStream("数据源文件路径");
      FileOutputStream fos=new FileOutputStream("目的地文件路径");
      //获得传输通道channel
      FileChannel inChannel=fis.getChannel();
      FileChannel outChannel=fos.getChannel();
    • 使用FileChannel类完成文件的复制

    • public int read(ByteBuffer b) 读数据,把读到的数据放在b数组中,返回读取到的字节个数,如果读到文件的末尾就返回-1

    • public int write(ByteBuffer b) 写数据,把数据写到目的地文件中

FileChannel结合MappedByteBuffer实现高效读写

  • 可以将文件直接映射至内存把硬盘中的读写变成内存中的读写, 所以可以提高大文件的读写效率。

  • 可以调用FileChannel的map()方法获取一个MappedByteBuffer,map()方法的原型:

    MappedByteBuffer map(MapMode mode, long position, long size);

    说明:将节点中从position开始的size个字节映射到返回的MappedByteBuffer中。

MapMode中定义的静态常量,这里以FileChannel举例: 1). FileChannel.MapMode.READ_ONLY:得到的镜像只能读不能写(只能使用get之类的读取Buffer中的内容);

2). FileChannel.MapMode.READ_WRITE:得到的镜像可读可写(既然可写了必然可读),对其写会直接更改到存储节点;

3). FileChannel.MapMode.PRIVATE:得到一个私有的镜像,其实就是一个(position, size)区域的副本罢了,也是可读可写,只不过写不会影响到存储节点,就是一个普通的ByteBuffer了!!

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.创建RandomAccessFile对象,r表示读,rw表示读写
        RandomAccessFile r1 = new RandomAccessFile("H:\\a.zip", "r");
        RandomAccessFile r2 = new RandomAccessFile("day\aaa\\a.zip", "rw");

        // 2.根据RandomAccessFile对象获取FileChanel对象
        FileChannel c1 = r1.getChannel();
        FileChannel c2 = r2.getChannel();

        // 3.获取数据源文件的字节大小
        long size = c1.size();

        // 定义一个变量,表示每次平均映射的字节大小
        long everySize = 500 * 1024 * 1024;

        // 映射的总次数:
        long count = size % everySize == 0 ? size / everySize : size / everySize + 1;

        // 循环映射
        for (long i = 0; i < count; i++) {
            //起始位置:
            long start = i * everySize;

            //每次真正映射的字节大小:
            long trueSize = size - start > everySize ? everySize : size - start;

            // 4.映射操作
            MappedByteBuffer m1 = c1.map(FileChannel.MapMode.READ_ONLY, start, trueSize);
            MappedByteBuffer m2 = c2.map(FileChannel.MapMode.READ_WRITE, start, trueSize);

            // 5.复制字节数据
            for (long j = 0; j < trueSize; j++) {
                byte b = m1.get();
                m2.put(b);
            }
        }

        // 6.释放资源
        c2.close();
        c1.close();
        r2.close();
        r1.close();


    }
}

ServerSocketChannel和SocketChannel创建连接

public class Server {
    public static void main(String[] args) throws Exception{
        // 1.打开服务器通道
        ServerSocketChannel ssc = ServerSocketChannel.open();

        // 2.绑定端口号
        ssc.bind(new InetSocketAddress(8888));

        // 3.等待连接
        System.out.println("等待连接...");
        SocketChannel socketChannel = ssc.accept();// 阻塞
        System.out.println("连接成功...");
    }
}

public class Server {
    public static void main(String[] args) throws Exception{
        // 1.打开服务器通道
        ServerSocketChannel ssc = ServerSocketChannel.open();

        // 2.绑定端口号
        ssc.bind(new InetSocketAddress(8888));

        // 设置非阻塞
        ssc.configureBlocking(false);

        // 3.等待连接
        System.out.println("等待连接...");
        SocketChannel socketChannel = ssc.accept();// 非阻塞
        System.out.println("连接成功...");
    }
}

NIO网络编程收发信息

public class Server {
    public static void main(String[] args) throws Exception{
        // 1.打开服务器通道
        ServerSocketChannel ssc = ServerSocketChannel.open();

        // 2.绑定端口号
        ssc.bind(new InetSocketAddress(8888));

        // 3.建立连接
        SocketChannel sc = ssc.accept();

        // 4.接收数据
        ByteBuffer b = ByteBuffer.allocate(1024);
        int len = sc.read(b);
        System.out.println("服务器接收到的数据是:"+new String(b.array(),0,len));

        // 5.释放资源
        sc.close();
        ssc.close();

    }
}

public class Client {
    public static void main(String[] args) throws Exception{
        // 1.打开客户端通道
        SocketChannel sc = SocketChannel.open();

        // 2.连接服务器
        sc.connect(new InetSocketAddress("127.0.0.1",8888));

        // 3.写数据给服务器
        byte[] bytes = "服务器你好,今晚约吗?".getBytes();
        ByteBuffer b = ByteBuffer.wrap(bytes);
        sc.write(b);

        // 4.释放资源
        sc.close();

    }
}

Selector(选择器)

选择器Selector是NIO中的重要技术之一。它与SelectableChannel联合使用实现了非阻塞的多路复用。使用它可以节省CPU资源,提高程序的运行效率。

"多路"是指:服务器端同时监听多个“端口”的情况。每个端口都要监听多个客户端的连接。

选择器Selector是NIO中的重要技术之一。它与SelectableChannel联合使用实现了非阻塞的多路复用。使用它可以节省CPU资源,提高程序的运行效率。

"多路"是指:服务器端同时监听多个“端口”的情况。每个端口都要监听多个客户端的连接。

如果不使用“多路复用”,服务器端需要开很多线程处理每个端口的请求。如果在高并发环境下,造成系统性能下降。

使用了多路复用,只需要一个线程就可以处理多个通道,降低内存占用率,减少CPU切换时间,在高并发、高频段业务环境下有非常重要的优势

选择器Selector的获取和注册

Selector selector = Selector.open();

channel.configureBlocking(false);// 一定要设置非阻塞
SelectionKey key =channel.register(selector,SelectionKey.OP_ACCEPT);

public class Test1 {
    public static void main(String[] args) throws Exception{
        // 1.打开服务器通道
        ServerSocketChannel ssc = ServerSocketChannel.open();

        // 2.绑定端口号
        ssc.bind(new InetSocketAddress(6666));

        // 3.设置非阻塞
        ssc.configureBlocking(false);

        // 4.获取选择器
        Selector selector = Selector.open();

        // 5.把服务器通道注册到选择器上
        System.out.println(1);
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println(2);

    }
}

public class Test2 {
    public static void main(String[] args) throws Exception{
        // 1.打开服务器通道
        ServerSocketChannel ssc1 = ServerSocketChannel.open();
        ServerSocketChannel ssc2 = ServerSocketChannel.open();
        ServerSocketChannel ssc3 = ServerSocketChannel.open();

        // 2.绑定端口号
        ssc1.bind(new InetSocketAddress(7777));
        ssc2.bind(new InetSocketAddress(8888));
        ssc3.bind(new InetSocketAddress(9999));

        // 3.设置非阻塞
        ssc1.configureBlocking(false);
        ssc2.configureBlocking(false);
        ssc3.configureBlocking(false);

        // 4.获取选择器
        Selector selector = Selector.open();

        // 5.把服务器通道注册到选择器上
        ssc1.register(selector, SelectionKey.OP_ACCEPT);
        ssc2.register(selector, SelectionKey.OP_ACCEPT);
        ssc3.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println(1);

    }
}

Selector的select()方法:---->面试

public class Test1_select方法 {
    public static void main(String[] args) throws Exception{
        // 1.打开服务器通道
        ServerSocketChannel ssc1 = ServerSocketChannel.open();
        ServerSocketChannel ssc2 = ServerSocketChannel.open();
        ServerSocketChannel ssc3 = ServerSocketChannel.open();

        // 2.绑定端口号
        ssc1.bind(new InetSocketAddress(7777));
        ssc2.bind(new InetSocketAddress(8888));
        ssc3.bind(new InetSocketAddress(9999));

        // 3.设置非阻塞
        ssc1.configureBlocking(false);
        ssc2.configureBlocking(false);
        ssc3.configureBlocking(false);

        // 4.获取选择器
        Selector selector = Selector.open();

        // 5.把服务器通道注册到选择器上
        ssc1.register(selector, SelectionKey.OP_ACCEPT);
        ssc2.register(selector, SelectionKey.OP_ACCEPT);
        ssc3.register(selector, SelectionKey.OP_ACCEPT);

        while (true){
            System.out.println(1);
            // 监听客户端的请求
            selector.select();
            System.out.println(2);
        }

    }

}

Selector的keys()方法

public class Test1_select方法 {
    public static void main(String[] args) throws Exception {
        // 1.打开服务器通道
        ServerSocketChannel ssc1 = ServerSocketChannel.open();
        ServerSocketChannel ssc2 = ServerSocketChannel.open();
        ServerSocketChannel ssc3 = ServerSocketChannel.open();

        // 2.绑定端口号
        ssc1.bind(new InetSocketAddress(7777));
        ssc2.bind(new InetSocketAddress(8888));
        ssc3.bind(new InetSocketAddress(9999));

        // 3.设置非阻塞
        ssc1.configureBlocking(false);
        ssc2.configureBlocking(false);
        ssc3.configureBlocking(false);

        // 4.获取选择器
        Selector selector = Selector.open();

        // 5.把服务器通道注册到选择器上
        ssc1.register(selector, SelectionKey.OP_ACCEPT);
        ssc2.register(selector, SelectionKey.OP_ACCEPT);
        ssc3.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("已注册的所有通道集合:" + selector.keys().size());

        while (true) {
            System.out.println(1);
            // 监听客户端的请求
            selector.select();
            System.out.println(2);
            System.out.println("已注册的所有通道集合:" + selector.keys().size());
        }

    }

}

Selector的selectedKeys()方法

public class Test2_selectedKeys方法 {
    public static void main(String[] args) throws Exception {
        // 1.打开服务器通道
        ServerSocketChannel ssc1 = ServerSocketChannel.open();
        ServerSocketChannel ssc2 = ServerSocketChannel.open();
        ServerSocketChannel ssc3 = ServerSocketChannel.open();

        // 2.绑定端口号
        ssc1.bind(new InetSocketAddress(7777));
        ssc2.bind(new InetSocketAddress(8888));
        ssc3.bind(new InetSocketAddress(9999));

        // 3.设置非阻塞
        ssc1.configureBlocking(false);
        ssc2.configureBlocking(false);
        ssc3.configureBlocking(false);

        // 4.获取选择器
        Selector selector = Selector.open();

        // 5.把服务器通道注册到选择器上
        ssc1.register(selector, SelectionKey.OP_ACCEPT);
        ssc2.register(selector, SelectionKey.OP_ACCEPT);
        ssc3.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("已注册的所有通道集合:" + selector.keys().size());

        while (true) {
            System.out.println(1);
            // 监听客户端的请求
            selector.select();
            System.out.println(2);
            // 处理客户端的请求
            // 获取已连接的所有通道集合
            Set<SelectionKey> keys = selector.selectedKeys();

            // 循环遍历所有通道集合
            for (SelectionKey key : keys) {
                // SelectionKey-->其实就是封装了ServerSocketChannel
                // 通过SelectionKey获得ServerSocketChannel
                // SelectableChannel是ServerSocketChannel的父类
                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                // 处理客户端的请求,建立连接
                SocketChannel sc = ssc.accept();
                // 接收客户端发过来的数据
                ByteBuffer b = ByteBuffer.allocate(1024);
                int len = sc.read(b);
                System.out.println("服务器接收到的数据是:" + new String(b.array(), 0, len));
                // 处理完了,释放资源
                sc.close();
            }


            System.out.println("已注册的所有通道集合:" + selector.keys().size());
        }

    }

}

Selector多路复用

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.打开服务器通道
        ServerSocketChannel ssc1 = ServerSocketChannel.open();
        ServerSocketChannel ssc2 = ServerSocketChannel.open();
        ServerSocketChannel ssc3 = ServerSocketChannel.open();

        // 2.绑定端口号
        ssc1.bind(new InetSocketAddress(7777));
        ssc2.bind(new InetSocketAddress(8888));
        ssc3.bind(new InetSocketAddress(9999));

        // 3.设置非阻塞
        ssc1.configureBlocking(false);
        ssc2.configureBlocking(false);
        ssc3.configureBlocking(false);

        // 4.获取选择器
        Selector selector = Selector.open();

        // 5.把服务器通道注册到选择器上
        ssc1.register(selector, SelectionKey.OP_ACCEPT);
        ssc2.register(selector, SelectionKey.OP_ACCEPT);
        ssc3.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("已注册的所有通道集合:" + selector.keys().size());

        while (true) {
            System.out.println(1);
            // 监听客户端的请求
            selector.select();
            System.out.println(2);
            // 处理客户端的请求
            // 获取已连接的所有通道集合
            Set<SelectionKey> keys = selector.selectedKeys();

            // 循环遍历所有通道集合
            Iterator<SelectionKey> it = keys.iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next();
                // SelectionKey-->其实就是封装了ServerSocketChannel
                // 通过SelectionKey获得ServerSocketChannel
                // SelectableChannel是ServerSocketChannel的父类
                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                // 处理客户端的请求,建立连接
                SocketChannel sc = ssc.accept();// null
                // 接收客户端发过来的数据
                ByteBuffer b = ByteBuffer.allocate(1024);
                int len = sc.read(b);
                System.out.println("服务器接收到的数据是:" + new String(b.array(), 0, len));
                // 处理完了,释放资源
                sc.close();

                // 移除keys集合中已处理的服务器通道--->迭代器
                it.remove();
            }


            System.out.println("已注册的所有通道集合:" + selector.keys().size());
        }

    }

}

NIO2-AIO(异步、非阻塞)

public class Server {
    public static void main(String[] args) throws Exception{
        // 1.打开异步服务器通道
        AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open();

        // 2.绑定端口号
        assc.bind(new InetSocketAddress(7777));

        // 3.接收请求,建立连接
        System.out.println(1);
        assc.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
           @Override
           public void completed(AsynchronousSocketChannel result, Object attachment) {
               // 连接成功,就会来到这里
               System.out.println(2);
           }

           @Override
           public void failed(Throwable exc, Object attachment) {
                // 连接失败,就会来到这里
               System.out.println(3);
           }
       });
        System.out.println(4);

        while (true){

        }

    }
}

AIO 异步非阻塞连接和异步读

public class Server {
    public static void main(String[] args)throws Exception{
        // 1.打开异步服务器通道
        AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open();

        // 2.绑定端口号
        assc.bind(new InetSocketAddress(7777));

        // 3.接收请求,建立连接
        System.out.println(1);
        assc.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel asc, Object attachment) {
                // 连接成功
                System.out.println(2);

                // 创建ByteBuffer对象
                ByteBuffer b = ByteBuffer.allocate(1024);
                // 异步读
                asc.read(b, null, new CompletionHandler<Integer, Object>() {
                    @Override
                    public void completed(Integer len, Object attachment) {
                        // 读成功
                        System.out.println(5);
                        System.out.println("服务器接收到的信息:"+new String(b.array(),0,len));
                    }

                    @Override
                    public void failed(Throwable exc, Object attachment) {
                        // 读失败
                        System.out.println(6);
                    }
                });

                System.out.println(7);
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                // 连接失败
                System.out.println(3);
            }
        });

        System.out.println(4);

        // 为了让程序不结束
        while (true){

        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值