java学习-io-socketChannel
根据 java.nio.channels.Channel 的规范定义 中 存在一个定义 称为 network io
对于 network, 在 java 1.7 中增加了一个新的接口 java.nio.channels.NetworkChannel,其定义规范了网络相关api
这里我们重点看一下 其socket相关扩展类
java.nio.channels.SocketChannel : 根据其支持的 connect 方法可以看出 当前抽象类的定义为 client
实现类为: sun.nio.ch.SocketChannelImpl
public abstract class SocketChannel
extends AbstractSelectableChannel // 表示其支持 selector
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel //根据实现关系可以看出 其支持字节流(ByteBuffer)操作,以及支持多(ByteBuffer)读写操作,另外支持 网络操作
/**
* 根据注释可以看出 其作用就是为了和server进行建立连接, 但相对应的 由于 no block 模式存在,如果当前Socket的模式为 no block,其直接返回 false, 然后需要调用 finishConnect 来完成 建立真正的连接
**/public abstract boolean connect(SocketAddress remote) throws IOException;
// 当前方法就是专门为了 no block 模式下完成连接建立而创建的方法public abstract boolean finishConnect() throws IOException;
java.nio.channels.ServerSocketChannel : 根据其支持的 accept 方法可以看出, 当前抽象类的定义为 server
实现类为:sun.nio.ch.ServerSocketChannelImpl
public abstract class ServerSocketChannel
extends AbstractSelectableChannel
implements NetworkChannel // 根据其实现关系可以看出,其支持 网络操作,但并不直接支持读写操作
根据其接口规范可以看出 ServerSocketChannel 并不支持读写操作, 其依赖于 accept 成功建立的连接通道
/**
* @return The socket channel for the new connection,
* or <tt>null</tt> if this channel is in non-blocking mode
* and no connection is available to be accepted // 需要特殊注意的一点在于,由于支持非阻塞式模式,因此在非阻塞模式下 并不会立即建立连接,而是会直接返回null,等待真正的 client <-> server 连接建立完成**/
public abstract SocketChannel accept() throws IOException; // 返回的是一个连接(通道),因此需要利用建立的通道执行相关的读写操作
对于 java.nio.channels.spi.AbstractSelectableChannel 支持通过以下方法设置 当前io 是否为 阻塞
/**
* Adjusts this channel's blocking mode.
*
* <p> If the given blocking mode is different from the current blocking
* mode then this method invokes the {@link #implConfigureBlocking
* implConfigureBlocking} method, while holding the appropriate locks, in
* order to change the mode. </p>
*/
public final SelectableChannel configureBlocking(boolean block)
throws IOException
"talk is cheap , show me the code"
server
/* * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved */ package com.xingguo.noblockio; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.util.Objects; /** * SocketChannelServerDemo * {@link java.nio.channels.ServerSocketChannel} * 测试socket 套接字技术 * * @author guoxing * @date 2020/12/11 7:51 PM * @since */ public class SocketChannelServerDemo { public static void main(String[] args) throws IOException, InterruptedException { /** * 利用{@link java.nio.channels.ServerSocketChannel} 创建一个server */ try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) { // 绑定一个端口,表示当前服务暴露端口 serverSocketChannel.bind(new InetSocketAddress(8080)); /** * {@link java.nio.channels.SocketChannel}支持 no block io * 支持手动设置开启 */ serverSocketChannel.configureBlocking(false); // 获取当前地址 SocketAddress localAddress = serverSocketChannel.getLocalAddress(); System.out.printf("当前服务地址为:%s\n", localAddress); // 设置当前要发送的内容 String message = "hello,world"; while (true) { // 在no block 连接模式下, {@link ServerSocketChannel#accept} {@code return null} ,等待client建立连接 // 因此可以通过当前方法判断是否存在连接 // 获取当前成功建立的连接 SocketChannel socketChannel = serverSocketChannel.accept(); if (Objects.nonNull(socketChannel)) { System.out.printf("当前连接的客户端地址为:%s;\n", socketChannel.getRemoteAddress()); // 将服务端要发送的数据转换为 bytebuffer ByteBuffer byteBuffer = ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8)); // 发送数据 socketChannel.write(byteBuffer); // 关闭当前连接 socketChannel.close(); // 终止循环 break; } else { // 当没有客户端连接时,线程休眠 Thread.sleep(1000L); } } } } }
client
/* * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved */ package com.xingguo.noblockio; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; /** * SocketChannelClientDemo * {@link java.nio.channels.SocketChannel} * * @author guoxing * @date 2020/12/11 8:15 PM * @since */ public class SocketChannelClientDemo { public static void main(String[] args) throws IOException { /** * 对于client 端实际就不需要像{@link java.nio.channels.ServerSocketChannel}一样麻烦 * 对于client 而言其就是一个普通的 {@link java.nio.channels.SocketChannel} */ try (SocketChannel socketChannel = SocketChannel.open()) { // 设置当前未 no block socketChannel.configureBlocking(false); /** * 由于使用的 no block 模式,因此在 {@link SocketChannel#connect(SocketAddress)}时会直接返回false,并不会真正建立连接 * 而是会等待{@link SocketChannel#finishConnect()}完成 nio 模式下的连接建立 */ // 指定要连接服务地址和端口 boolean connect = socketChannel.connect(new InetSocketAddress(8080)); // 完成 非阻塞式 连接建立 while (!socketChannel.finishConnect()) { System.out.println("等待连接建立....."); } // 判断当前连接状态 while (socketChannel.isConnected()) { // 创建一个 4k 的容器 ByteBuffer byteBuffer = ByteBuffer.allocate(4 * 1024); // 将接受到的数据读取出来写入到byteBuffer中 while (socketChannel.read(byteBuffer) != -1) { // 准备读取 byteBuffer中的数据 byteBuffer.flip(); while (byteBuffer.hasRemaining()) { System.out.print((char) byteBuffer.get()); } byteBuffer.clear(); } socketChannel.close(); } } } }