背景介绍:

传统模式下的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 依次启动发送端、监听端代码,运行结果如下:

精彩文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: