NIO完全なネットワークプログラミング

NIO完全なネットワークプログラミング

1. Selector--セレクタボス

セレクタ
        セレクタ
        サーバーは、セレクタプログラムリスニング操作を実行しているスレッド実行することが
        、新しい接続は、データの書き込み、データの読み込み、接続された
セレクタの一般的な方法を:

public static Selector Open();
	得到一个选择器对象
public int select(long timeout);
	监听所有注册通道,存在IO流操作是,会将对应的信息SelectionKey存入到内部的集合中,参数是一个超时时间
public Set<SelectionKey> selectionKeys();
	返回当前Selector内部集合中保存的所有SelectionKey

ここに画像を挿入説明

2.SelectionKey

SelectionKeyは
        、チャネルとネットワークセレクタとの関係を示しています

int OP_ACCEPT; 16 需要连接
int OP_CONNECT; 8  已经连接
int OP_READ; 1  读取操作
int OP_WRITE; 4 写入操作

SelectionKey

public abstract Selector selector();
	得到与之关联的 Selector 对象
public abstract SelectableChannel channel();
	得到与之关联的通道
public final Object attachment();
	得到与之关联的共享数据
public abstract SelectionKey interestOps(int ops);
	设置或改变监听事件
public final boolean isAcceptable();
	是否可以 accept
public final boolean isReadable();
	是否可以读
public final boolean isWritable();
	是否可以写

3.ServerSocketChannel

ServerSocketChannel
        に対応するチャネルソケットサーバプログラムチャネル
一般的な方法:

public static ServerSocketChannel open();
	开启服务器ServerSocketChannel通道,等于开始服务器程序
public final ServerSocketChannel bind(SocketAddress local);
	设置服务器端端口号
public final SelectableChannel configureBlocking(boolean block);
	设置阻塞或非阻塞模式, 取值 false 表示采用非阻塞模式
public SocketChannel accept();
	[非阻塞]
		获取一个客户端连接,并且得到对应的操作通道
public final SelectionKey register(Selector sel, int ops);
	[重点方法]
		注册当前选择器,并且选择监听什么事件

4.SocketChannel

SocketChannel
        チャンネルソケットは、クライアントに対応するオブジェクトの
一般的な方法:

public static SocketChannel open();
	打卡一个Socket客户端Channel对象
public final SelectableChannel configureBlocking(boolean block)
	这里可以设置是阻塞状态,还是非阻塞状态
	false,表示非阻塞
public boolean connect(SocketAddress remote);
	连接服务器
public boolean finishConnect();
	如果connect连接失败,可以通过finishConnect继续连接
public int write(ByteBuffer buf);
	写入数据到缓冲流中
public int read(ByteBuffer buf);	、
	从缓冲流中读取数据
public final SelectionKey register(Selector sel, int ops, Object attechment);
	注册当前SocketChannel,选择对应的监听操作,并且可以带有Object attachment参数
public final void close();
	关闭SocketChannel

5. NIOは、クライアントとサーバーを完了

5.1クライアントを完了

package com.qfedu.a_tcpnio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 符合TCP协议,非阻塞IO NIO完成对应的客户端代码
 *
 * @author Anonymous 2020/3/16 15:10
 */
public class TcpNioClient {
    public static void main(String[] args) throws IOException, InterruptedException {
        // 1. 得到一个网络通道
        SocketChannel socket = SocketChannel.open();

        // 2. 设置当前NIO采用的方式为非阻塞方式
        socket.configureBlocking(false);

        // 3. 确定服务器IP地址和对应程序端口号,创建一个InetSocketAddress对象
        InetSocketAddress address = new InetSocketAddress("192.168.31.154", 8848);

        // 4. 连接服务器
        if (!socket.connect(address)) {
            // 如果是false,表示连接失败,保持申请连接的状态
            while (!socket.finishConnect()) {
                // 因为采用NIO非阻塞方式,在获取等待连接的状态下,可以去做当前程序的其他操作。
                System.out.println("保持呼叫服务器状态,但是我还能做点别的事情~~~ 等待2s继续申请连接~~~");
                Thread.sleep(2000);
            }
        }

        // 5. 准备一个数据存入到缓冲区
        ByteBuffer buffer = ByteBuffer.wrap("你好,服务器,我在等你...".getBytes());

        // 6. 通过SocketChannel 符合TCP协议Socket要求Channel对象发送
        socket.write(buffer);

        new Scanner(System.in).nextLine();
    }
}

5.2完全なサーバー

  1. サーバーの電源をオンにし
            ServerScoketChannel

  2. ビッグブラザーオープンセレクター
            セレクターオブジェクト

  3. サーバーのServerSocketChannelバインドリスニングポート番号
            8848のポート

  4. 提供ブロックされていない
            configureBlocking(偽)

  5. セレクタ注册- >のServerSocketChannel
             レジスタ(セレクタ、OP_ACCEPT)。

  6. セレクタ兄は仕事を始めた
    、ソケットが対応して登録接続入門6.1
    6.2モニターの書き込みイベント
             データを読み込み6.2.1。クライアントがサーバーにデータを送信
             6.2.2書き込みデータ。クライアントにデータを送信します

package com.qfedu.a_tcpnio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * 使用ServerSocketChannel NIO非阻塞方式,完成服务端代码
 *
 * 1. 开启服务器
 * 	    ServerScoketChannel
 * 2. 开启Selector 大哥
 * 	    Selector对象
 * 3. 服务器ServerSocketChannel bind监听端口号
 * 	    8848端口
 * 4. 设置非阻塞状态
 * 	    configureBlocking(false)
 * 5. ServerSocketChannel 注册--> Selector
 * 	    register(selector, OP_ACCEPT);
 *
 * 6. Selector大哥开始忙活
 * 	    6.1 获取连接,注册对应的Socket
 * 	    6.2 监听读写事件
 * 		    6.2.1 读数据。客户端发送数据到服务器
 * 		    6.2.2 写数据。发送数据数据给客户端
 *
 *
 * @author Anonymous 2020/3/16 15:44
 */
public class TcpNioServer {
    public static void main(String[] args) throws IOException {
        // 1. 开启服务器
        ServerSocketChannel serverSocket = ServerSocketChannel.open();

        // 2. 开启Selector 大哥
        Selector selector = Selector.open();

        // 3. 服务端代码绑定端口号
        serverSocket.bind(new InetSocketAddress(8848));

        // 4. 设置非阻塞状态
        serverSocket.configureBlocking(false);

        // 5. ServerSocketChannel 注册--> Selector 返回值是一个SelectionKey
        // 并且明确当前Selector监听SelectionKey.OP_ACCEPT,监听连接服务器
        serverSocket.register(selector, SelectionKey.OP_ACCEPT);

        // 6. 大哥干活
        while (true) {
            // 6.1 获取连接,注册对应的Socket
            if (0 == selector.select(1000)) {
                // 0 == selector.select(1000) 表示没有连接到客户端
                System.out.println("ServerSocket提示,当前没有客户端搭理我,我自己默默的画圈圈~~~");
                continue;
            }

            // 6.2 监听读写事件
            // 得到当前Selector中所有的SelectionKey
            Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
            while (selectionKeys.hasNext()) {

                SelectionKey selectionKey = selectionKeys.next();
                // 6.2.1 判断客户端是一个连接请求 OP_ACCEPT
                if (selectionKey.isAcceptable()) {
                    System.out.println("客户端请求连接!!!");
                    // 获取对应的Socket,只不过这里是获取对应的SocketChannel
                    SocketChannel socket = serverSocket.accept();
                    // 设置当前对应客户端的SocketChannel对象是一个非阻塞状态
                    socket.configureBlocking(false);
                    /*
                     注册当前Socket对象
                     selector 注册到当前的Selector【核心】
                     SelectionKey.OP_READ 选择当前Socket监听的操作内容是OP_READ 从当前Socket中读取数据
                     ByteBuffer.allocate(1024 * 4) attachment 补充参数,这里是给予当前Socket对象一个4KB 字节缓冲区对象
                     */
                    socket.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024 * 4));
                }

                // 6.2.2 判断客户端目前是可读状态,获取客户端发送给服务器的数据
                if (selectionKey.isReadable()) {
                    // 从SelectionKey 中获取对应的SocketChannel对象
                    SocketChannel socket = (SocketChannel) selectionKey.channel();

                    // 因为使用的是NIO,涉及到Channel和ByteBuffer,数据在缓冲区中
                    ByteBuffer buffer = (ByteBuffer) selectionKey.attachment();

                    // 读取数据。利用SocketChannel从缓冲中ByteBuffer中读取数据。
                    socket.read(buffer);

                    System.out.println("客户端发送数据:" + new String(buffer.array()));
                }

                // 处理完进行一个移除当前SelectionKey操作
                selectionKeys.remove();
            }
        }


    }
}
公開された28元の記事 ウォン称賛62 ビュー10000 +

おすすめ

転載: blog.csdn.net/qq_41986648/article/details/104906928