天天看点

解决NIO服务端select一直为-1 && 服务端一直有可读事件

  在进行NIO编程的时候,发现客户端只往服务端发送了一条消息后,就下线了,但是服务端select方法的返回值总是1,出现了死循环,经过调试发现,Selectionkeys方法返回的总是进行读操作的SocketChannel,也就是说注册的SocketChannel一直有读的就绪事件,可是客户端已经关闭了。总结来说就是:客户端主动关闭连接,服务端会一直出现可读事件,read一直返回-1,在网上查找了大量的博客后,解决了这个问题。

  解决的方案是服务端主动关闭通道SocketChannel,当关闭后,select就不会再继续关注该通道,这样select就不会总是返回1了

  很明显,可以解决问题,但是如果我的客户端没有下线,而是想继续发消息怎么办?这就要依靠read的返回值来进行判断了,如果read的返回值是-1,则主动关闭通道

while ((read = socketChannel.read(buffer)) > 0) {
                            System.out.println("read:"+read);
                            buffer.flip();
                            System.out.println(socketChannel.getRemoteAddress()+":"+new String(buffer.array()));
                        }
                        if(read == -1) {
                            System.out.println("read:"+read);
                            socketChannel.close();
                        }
           

最后附上测试的服务端和客户端源码:

package selector;

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

/**
 *
 */
public class Server {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.打开通道
        ServerSocketChannel server = ServerSocketChannel.open();
        //2.绑定端口
        server.socket().bind(new InetSocketAddress(8888));
        //3.得到选择器
        Selector selector = Selector.open();
        //4.设置非阻塞,并注册到选择器
        server.configureBlocking(false);
        server.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            int select = selector.select();
            System.out.println("select:"+select);
            Thread.sleep(500);
            if(select > 0) {
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey next = iterator.next();
                    if(next.isAcceptable()) {
                        System.out.println("accpet");
                        ServerSocketChannel serverSocketChannel = ((ServerSocketChannel) next.channel());
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector,SelectionKey.OP_READ);
                    } else if(next.isReadable()) {
                        System.out.println("read");
                        SocketChannel socketChannel = ((SocketChannel) next.channel());
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        buffer.clear();
                        int read = 0;
                        while ((read = socketChannel.read(buffer)) > 0) {
                            System.out.println("read:"+read);
                            buffer.flip();
                            System.out.println(socketChannel.getRemoteAddress()+":"+new String(buffer.array()));
                        }
                        if(read == -1) {
                            System.out.println("read:"+read);
                            socketChannel.close();
                        }
                        buffer.clear();
                    }
                    iterator.remove();
                    System.out.println("开始下次select");
                }
            } else {
                System.out.println("去干其他事情");
            }
        }
    }
}
           
package selector;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

/**
 *
 */
public class Client {
    public static void main(String[] args) throws IOException, InterruptedException {
        //拿到通道
        SocketChannel client = SocketChannel.open();
        //2.设置非阻塞模式
        client.configureBlocking(false);
        //3.连接服务器
        client.connect(new InetSocketAddress("127.0.0.1", 8888));

        //
        while (!client.finishConnect()) {
            System.out.println("建立连接中...");
        }
        ByteBuffer buffer = ByteBuffer.allocate(100);
        String msg = "this is one";
        buffer.put(msg.getBytes());
        buffer.flip();
        client.write(buffer);
        buffer.clear();


        //测试客户端发两次数据
        Thread.sleep(5000);
        msg = "this is two";
        buffer.put(msg.getBytes());
        buffer.flip();
        client.write(buffer);
        buffer.clear();
        Thread.sleep(10000);
        client.close();
    }
}
           

继续阅读