Bronya
  • 3
  • 新人请关照

Java NIO相关,Selector 服务端第二次访问读取不到数据,求解答~~

我的意图是客户端先向服务端发送数据,然后服务端向客户端发送数据,完成一次数据交换。但是客户端第一次访问的时候是可以的,第二次访问时服务端不能获取数据了?这个问题困扰我两天了,刚学NIO 完全不明白为什么😫

环境:jdk 1.8,win 10.

另外客户端是不是一般都不这么写?是不是直接用socket比较好?
客户端用Socket试了一下发现第二次访问服务端还是不能接受到数据...

服务端代码:

package cs;

import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

public class Server {
    public static void main(String[] args) throws Exception {
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        ssc.socket().bind(new InetSocketAddress(8888));

        Selector selector = Selector.open();
        ssc.register(selector, SelectionKey.OP_ACCEPT);

        ByteBuffer buffer = ByteBuffer.allocate(1024);

        while (true) {
            int readyNum = selector.select();
            if (readyNum == 0) {
                System.out.println("------------------");
                continue;
            }

            Iterator<SelectionKey> it = selector.selectedKeys().iterator();

            while (it.hasNext()) {
                SelectionKey key = it.next();
                it.remove();

                if (key.isAcceptable()) {
                    // 接受连接
                    SocketChannel accept = ((ServerSocketChannel) key.channel()).accept();
                    accept.configureBlocking(false);
                    accept.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    // 通道可读
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    System.out.println("有可读通道...");

                    while (clientChannel.read(buffer) > 0) {
                        buffer.flip();
                        byte[] bytes = new byte[buffer.limit()];
                        buffer.get(bytes);
                        System.out.println(new String(bytes));
                        buffer.clear();
                    }

                    //这里如果条件改成等-1的话第二次访问会空循环
                    if (clientChannel.read(buffer) <= 0) {
                        key.interestOps(SelectionKey.OP_WRITE);
                    }
                } else if (key.isWritable()) {
                    // 通道可写
                    System.out.println("写");
                    SocketChannel channel = (SocketChannel) key.channel();
                    buffer.clear();
                    buffer.put("OVER".getBytes());
                    buffer.flip();
                    channel.write(buffer);

                    channel.close();
                }
            }
        }
    }
}

客户端代码:

package cs;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;

@SuppressWarnings("all")
public class Client {
    public static void main(String[] args) throws Exception {
        SocketChannel channel = SocketChannel.open(new InetSocketAddress(8888));
        channel.configureBlocking(false);

        ByteBuffer buffer = ByteBuffer.allocate(1024);

        Selector selector = Selector.open();
        channel.register(selector, SelectionKey.OP_WRITE);

        while (true) {
            int numReady = selector.select();
            if (numReady == 0) {
                continue;
            }

            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next();
                it.remove();
                if (key.isWritable()) {
                    System.out.println("可写");
                    SocketChannel channel1 = (SocketChannel) key.channel();
                    buffer.put("测试文本".getBytes());

                    buffer.flip();
                    channel1.write(buffer);


                    buffer.clear();
                    channel.shutdownOutput();
                    key.interestOps(SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    System.out.println("可读");
                    buffer.clear();
                    SocketChannel readChannel = (SocketChannel) key.channel();
                    //客户端每次能收到数据
                    while (readChannel.read(buffer) > 0) {
                        buffer.flip();
                        byte[] bytes = new byte[buffer.limit()];
                        buffer.get(bytes);
                        System.out.println(new String(bytes));
                        buffer.clear();
                    }
                    if (readChannel.read(buffer) == -1) {
                        readChannel.close();
                        return;
                    }
                }
            }
            System.out.println("===");
        }
    }
}
阅读 297
评论 更新于 2019-07-30
    2 个回答
    Bronya
    • 3
    • 新人请关照

    服务端key.isAcceptable()分支,每次读数据前需要清空缓冲:buffer.clear();
    中间混合各种奇奇怪怪的问题让我忽略了这一点。。。

    评论 赞赏 2019-07-31
      1. 为什么 it.remove? (更正:这里记错了,确实需要 remove)
      2. 写完为什么要 close?
      评论 赞赏 2019-07-31
        撰写回答

        登录后参与交流、获取后续更新提醒