NIO之通道
分类:
客户端通道
连接服务端
传递缓冲区(传递数据)
打开通道 SocketChannel
连接服务端(指定IP和端口号) InetSocketAddress
将缓冲区写到服务器 write
释放资源 close
代码实现:
public class test {
public static void main(String[] args) throws IOException {
//打开通道
SocketChannel open = SocketChannel.open();
//指定IP和端口号
open.connect(new InetSocketAddress("127.0.0.1",1024));
//写出数据
ByteBuffer wrap = ByteBuffer.wrap("hello".getBytes());
open.write(wrap);
//释放资源
open.close();
}
}
服务端通道
服务端通道之负责连接,不负责传输数据
1.打开一个服务端通道
2.绑定相应的端口号
3.通道默认是阻塞的,需要设置为非阻塞
4.现在没有选择器的加入,服务端需要不定时看下有没有数据发来
5.如果客户端发来数据了,则在服务端的通道内部,在创建一个客户端通道,相当于是客户端通道的延申
6.客户端将缓冲区通过通道传递给服务端
7.服务端创建一个空的缓冲区装数据并输出
客户端通道连接服务端,并传输数据
缓冲区,客户端发送的数据都在缓冲区中
服务端通道内部创建出来的客户端通道,相当于客户端通道的延申,用来传递数据
代码实现:
public class NioServer {
public static void main(String[] args) throws IOException {
// 1.打开一个服务端通道
ServerSocketChannel open = ServerSocketChannel.open();
// 2.绑定相应的端口号
ServerSocketChannel bind = open.bind(new InetSocketAddress(1024));
// 3.通道默认是阻塞的,需要设置为非阻塞
//如果传递一个true 表示通道为阻塞通道。。。默认值
//如果传递一个false,表示通道为非阻塞通道
open.configureBlocking(false);
// 4.现在没有选择器的加入,服务端需要不定时看下有没有数据发来
while (true) {
// 5.如果客户端发来数据了,则在服务端的通道内部,在创建一个客户端通道,相当于是客户端通道的延申
//此时设置了通道非阻塞,如果有客户端来连接,则在服务端通道内部,在创建一个客户端通道,相当于客户端通道延申
//如果调用方法的时候,没有客户端来接连,那么他会返回一个null
SocketChannel accept = open.accept();
if (open!=null){
// 6.客户端将缓冲区通过通道传递给服务端
// 7.服务端创建一个空的缓冲区装数据并输出
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//获取传递过来的数据,并把他们放到byteBuffer缓冲区中
//返回值:正数,表示本次督导的有效字节个数
//0:本次没有读到有效字节
//-1表述读到了末尾
int read = accept.read(byteBuffer);
System.out.println(new String(byteBuffer.array(),0,read));
accept.close();
}
}
}
}
练习:客户端发送数据给服务端,服务端接收会返回一个数据给客户端
代码:
服务端:
public class NioServer {
public static void main(String[] args) throws IOException {
ServerSocketChannel open = ServerSocketChannel.open();
ServerSocketChannel bind = open.bind(new InetSocketAddress(1024));
open.configureBlocking(false);
while (true){
SocketChannel accept = open.accept();
if (accept !=null){
System.out.println("此时有客户端来连接了");
//第二种方法 把延申通道也设置称为非阻塞的
accept.configureBlocking(false);
//获取客户端传来的数据,并把数据放在byteBuffer1这个缓冲区中
ByteBuffer allocate = ByteBuffer.allocate(1024);
//不能只读取一次 用循环
//accept.read(allocate);
int len;
//针对缓冲区来说 获取数据 flip方法 添加数据 clear
while ((len=accept.read(allocate))>0){
allocate.flip();
System.out.println(new String(allocate.array(),0,len));
allocate.clear();
}
System.out.println("接收数据完毕,回写一份数据");
ByteBuffer wrap = ByteBuffer.wrap("这是返回的数据".getBytes());
accept.write(wrap);
accept.close();
}
}
}
}
客户端代码:
public class NioClient {
public static void main(String[] args) throws IOException {
SocketChannel open = SocketChannel.open();
open.bind(new InetSocketAddress("127.0.0.1", 1024));
ByteBuffer wrap = ByteBuffer.wrap("这是传往服务端的内容".getBytes());
open.write(wrap);
//没有结束标记的话 就会一直卡死循环
open.shutdownOutput();
System.out.println("数据已经传往服务器");
ByteBuffer allocate = ByteBuffer.allocate(1024);
int len;
while ((len=open.read(allocate))!=-1){
allocate.flip();
System.out.println(new String(allocate.array(),0,len));
allocate.clear();
}
open.close();
}
}
NIO之选择器
作用:监视通道的状态
Selector 选择器对象
SelectionKey 绑定的Key
SelectableChannel 能使用的选择器通道
SocketChannel
ServerSocketChannel
一个服务端对应一个客户端
1.打开一个服务端通道
2.绑定对应的端口
3.通道默认是阻塞的,需要设置为非阻塞
4.打开一个选择器
5.如果有客户端来连接了,选择器就告诉服务端通道来连接
6.在服务端通道内部,在创建一个客户端通道,相当于是客户端通道的延申
7.如果客户端通道传递数据了,选择器就告诉延申的客户端通道来传输
如果一个服务端有多个客户端来连接,就会看哪一个服务端通道准备好了,就会让谁去连接
选择器改写服务端
选择器监视客户端通道
选择器监视服务端通道
选择器监视客户端延申通道
客户端代码:
public class NioClient {
public static void main(String[] args) throws IOException {
SocketChannel open = SocketChannel.open();
open.bind(new InetSocketAddress("127.0.0.1", 1024));
ByteBuffer wrap = ByteBuffer.wrap("这是传往服务端的内容".getBytes());
open.write(wrap);
//没有结束标记的话 就会一直卡死循环
open.shutdownOutput();
System.out.println("数据已经传往服务器");
ByteBuffer allocate = ByteBuffer.allocate(1024);
int len;
while ((len=open.read(allocate))!=-1){
allocate.flip();
System.out.println(new String(allocate.array(),0,len));
allocate.clear();
}
open.close();
}
}
服务端代码:
public class NioServer {
public static void main(String[] args) throws IOException {
//打开服务端通道
ServerSocketChannel opServerSocketChannelen = ServerSocketChannel.open();
//2.选择端口
ServerSocketChannel bind = opServerSocketChannelen.bind(new InetSocketAddress(1024));
//设置为非阻塞
opServerSocketChannelen.configureBlocking(false);
//选一个选择器
//Selector ---选择器
//SelectionKey -- 绑定通道后返回的key
//SelectableChannel---可以使用选择器的通道
Selector selector = Selector.open();
//绑定选择器和服务端的通道
opServerSocketChannelen.register(selector,SelectionKey.OP_ACCEPT);
//选择器会监视客户端通道的状态
while (true){
//选择器会监视客户端的状态
//返回值就表示此时有多少个客户端来连接
int count = selector.select();
if (count!=0){
System.out.println("有客户端来连接了");
//遍历所有的服务端通道,看哪一个准备号了,谁就会去连接
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
//neselectionKeyxt 一次表示每一个服务端的令牌
SelectionKey selectionKey = iterator.next();
if (selectionKey.isAcceptable()){
//可以通过令牌来获得一个已经就绪的服务端通道
ServerSocketChannel channel = (ServerSocketChannel)selectionKey.channel();
//客户端的延申通道
channel.accept();
//将客户端的延申通道改为非阻塞的
channel.configureBlocking(false);
channel.register(selector,SelectionKey.OP_READ);
//当客户端来连接的时候,所有的步骤已经执行完毕
}else if (selectionKey.isAcceptable()){
//当前通道以及做好了读取的准备(延申通道)
SocketChannel channel = (SocketChannel) selectionKey.channel();
ByteBuffer allocate = ByteBuffer.allocate(1024);
//channel.read(allocate);
int len;
while ((len=channel.read(allocate))>0){
allocate.flip();
System.out.println(new String(allocate.array(),0,len));
allocate.clear();
}
//给客户端的回写数据
channel.write(ByteBuffer.wrap("这是给客户端回写的数据".getBytes(StandardCharsets.UTF_8)));
channel.close();
}
iterator.remove();
}
}
}
}
}