NIO--Socket通道

背景介绍:

传统模式下的IO主要有两个缺点:
1、一个Socket连接,需要一个单独的线程来实现,而线程的增多会加剧服务器的开销(我们都知道一个线程大约占4MB左右的内存)。就算使用线程池也无法高效率解决,比如线程池的队列是有限制的。
2、而传统的网络IO(IO分三种:网路IO、磁盘IO、文件IO)是阻塞的,比如accept()、listen()、read、write()方法都是阻塞的。知道有连接接入、直到有可供读取的数据…

NIO的核心是基于Reactor模式,IO调用不会被阻塞,而是注册感兴趣的IO事件。如可读数据到达,新的套接字连接等等。注册的新的事件发生时,系统再进行通知等等。其核心就是selector。

通过一个selector可实时监听多个通道,当通道中有我们注册的事情发生时再进行执行。非阻塞IO指的是IO事件本身是不阻塞的,但是获取IO的方法select()是阻塞的。NIO是面向缓冲区的。

在这里插入图片描述

本篇简单介绍Socket通道的几个API及使用。

1 ServerSocketChannel 通道的使用

1.1 模拟socket服务端,管理多个socket请求

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class ServerSocketChannelDemo {
    public static void main(String[] args) throws IOException, InterruptedException {
        int port = 7777;
        ByteBuffer buffer = ByteBuffer.wrap("hello every one!".getBytes());

        ServerSocketChannel ssc = ServerSocketChannel.open();
        // 绑定
        ssc.socket().bind(new InetSocketAddress(port));
        // 设定非阻塞模式
        ssc.configureBlocking(false);
        // while循环模拟一直监听7777端口
        while (true){
            System.out.println("Waiting for connections!");
            // ServerSocketChannel的accept方法会返回SocketChannel对象,这个对象可以在非阻塞模式下运行
            // 如果ServerSocketChannel在非阻塞模式下运行,当没有连接接入的时候,accept方法会返回一个空对象
            SocketChannel accept = ssc.accept(); // 如果是阻塞模式,accept方法会一直阻塞
            if (accept==null){
                System.out.println("没有连接接入");
                Thread.sleep(2000);
            }else {
                System.out.println("有连接接入,地址:"+ (accept.getRemoteAddress()));
                // 往buffer中读数据
                buffer.rewind(); // 指针归0
                accept.write(buffer);
                accept.close();
            }
        }
    }
}

1.2 运行main方法,启动服务端

在这里插入图片描述

1.3 通过网页发起客户端请求

在这里插入图片描述

1.4 再次查看控制台,请求已到达

在这里插入图片描述


2、DatagramChannel 通道的使用

DatagramChannel:数据包。

1、SocketChannel 模拟连接导向的流协议,如TCP/IP。
2、DatagramChannel模拟包导向的无连接协议,如UDP/IP。

DatagramChannel是无连接的,每一个数据包(datagram)都是一个自包含的实体,
拥有它自己的数据地址,不依赖于其他数据包的数据负载。

 与面向流的socket不同,DatagramChannel可以发送不同的数据包给不同的目的地址。
 同样也可以接收来自任意地址的数据包,每个到达的数据包都包含关于它来自何处的地址。

UDP不存在真正意义上的连接,这里的连接指的是向特定的服务地址 用read或write 接收发送数据包。

2.1 模拟基于DatagramChannel 发送和接收数据。

import org.junit.Test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.charset.StandardCharsets;

public class DatagramChannelDemo {
    /**
     * 发送端
     *
     * @throws IOException
     * @throws InterruptedException
     */
    @Test
    public void testSendDatagram() throws IOException, InterruptedException {
        DatagramChannel datagramChannel = DatagramChannel.open();
        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 7777);
        // 模拟循环发送
        while (true) {
            ByteBuffer buffer = ByteBuffer.wrap("hello girl!".getBytes(StandardCharsets.UTF_8));
            datagramChannel.send(buffer, address);
            System.out.println("发包完毕");
            Thread.sleep(2000);
        }
    }

    /**
     * 接收端
     *
     * @throws IOException
     */
    @Test
    public void testReceiveDatagram() throws IOException {
        DatagramChannel datagramChannel = DatagramChannel.open();
        InetSocketAddress address = new InetSocketAddress(7777);
        // 地址绑定,监听发送端端口
        datagramChannel.bind(address);
        // 接收
        ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
        while (true) {
            receiveBuffer.clear();
            // receive方法将内容接收到缓冲区
            SocketAddress socketAddress = datagramChannel.receive(receiveBuffer);
            receiveBuffer.flip();
            System.out.println(socketAddress.toString());
            System.out.println(StandardCharsets.UTF_8.decode(receiveBuffer));
        }
    }

}

2.2 依次启动发送端、监听端代码,运行结果如下:

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值