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();
            }
        }
    }
}

 

posted @ 2020-12-12 14:18  郭星  阅读(218)  评论(0)    收藏  举报