用逻辑实现java中的非阻塞IO

用逻辑实现java中的非阻塞IO,java,nio,nonblocking,Java,Nio,Nonblocking,我正在创建一个可处理>1000个连接的服务器。我决定在我的服务器上使用非阻塞IO。我在互联网上找到了一些代码,基本上是一个echo服务器。我认为一切都很好,但我不理解服务器中的一些概念 import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import jav

我正在创建一个可处理>1000个连接的服务器。我决定在我的服务器上使用非阻塞IO。我在互联网上找到了一些代码,基本上是一个echo服务器。我认为一切都很好,但我不理解服务器中的一些概念

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
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.util.*;

public class EchoServer {
     private InetAddress addr;
     private int port;
     private Selector selector;
     private Map<SocketChannel,List<byte[]>> dataMap;

public EchoServer(InetAddress addr, int port) throws IOException {
    this.addr = addr;
    this.port = port;
    dataMap = new HashMap<SocketChannel,List<byte[]>>();
    startServer();
}

private void startServer() throws IOException {
    // create selector and channel
    this.selector = Selector.open();
    ServerSocketChannel serverChannel = ServerSocketChannel.open();
    serverChannel.configureBlocking(false);

    // bind to port
    InetSocketAddress listenAddr = new InetSocketAddress(this.addr, this.port);
    serverChannel.socket().bind(listenAddr);
    serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);

    log("Echo server ready. Ctrl-C to stop.");

    // processing
    while (true) {
        // wait for events
        this.selector.select();

        // wakeup to work on selected keys
        Iterator keys = this.selector.selectedKeys().iterator();
        while (keys.hasNext()) {
            SelectionKey key = (SelectionKey) keys.next();

            // this is necessary to prevent the same key from coming up 
            // again the next time around.
            keys.remove();

            if (! key.isValid()) {
                continue;
            }

            if (key.isAcceptable()) {
                this.accept(key);
            }
            else if (key.isReadable()) {
                this.read(key);
            }
            else if (key.isWritable()) {
                this.write(key);
            }
        }
    }
}

private void accept(SelectionKey key) throws IOException {
    ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
    SocketChannel channel = serverChannel.accept();
    channel.configureBlocking(false);

    // write welcome message
    channel.write(ByteBuffer.wrap("Welcome, this is the echo server\r\n".getBytes("US-     ASCII")));

    Socket socket = channel.socket();
    SocketAddress remoteAddr = socket.getRemoteSocketAddress();
    log("Connected to: " + remoteAddr);

    // register channel with selector for further IO
    dataMap.put(channel, new ArrayList<byte[]>());
    channel.register(this.selector, SelectionKey.OP_READ);
}

private void read(SelectionKey key) throws IOException {
    SocketChannel channel = (SocketChannel) key.channel();

    ByteBuffer buffer = ByteBuffer.allocate(8192);
    int numRead = -1;
    try {
        numRead = channel.read(buffer);
    }
    catch (IOException e) {
        e.printStackTrace();
    }

    if (numRead == -1) {
        this.dataMap.remove(channel);
        Socket socket = channel.socket();
        SocketAddress remoteAddr = socket.getRemoteSocketAddress();
        log("Connection closed by client: " + remoteAddr);
        channel.close();
        key.cancel();
        return;
    }

    byte[] data = new byte[numRead];
    System.arraycopy(buffer.array(), 0, data, 0, numRead);
    log("Got: " + new String(data, "US-ASCII"));

    // write back to client
    doEcho(key, data);
}

private void write(SelectionKey key) throws IOException {
    SocketChannel channel = (SocketChannel) key.channel();
    List<byte[]> pendingData = this.dataMap.get(channel);
    Iterator<byte[]> items = pendingData.iterator();
    while (items.hasNext()) {
        byte[] item = items.next();
        items.remove();
        channel.write(ByteBuffer.wrap(item));
    }
    key.interestOps(SelectionKey.OP_READ);
}

private void doEcho(SelectionKey key, byte[] data) {
    SocketChannel channel = (SocketChannel) key.channel();
    List<byte[]> pendingData = this.dataMap.get(channel);
    pendingData.add(data);
    key.interestOps(SelectionKey.OP_WRITE);
}

private static void log(String s) {
    System.out.println(s);
}

    public static void main(String[] args) throws Exception {
         new EchoServer(null, 8989);
    }
}
import java.io.IOException;
导入java.net.InetAddress;
导入java.net.InetSocketAddress;
导入java.net.Socket;
导入java.net.SocketAddress;
导入java.nio.ByteBuffer;
导入java.nio.channels.SelectionKey;
导入java.nio.channels.Selector;
导入java.nio.channels.ServerSocketChannel;
导入java.nio.channels.SocketChannel;
导入java.util.*;
公共类EchoServer{
专用地址地址地址;
专用int端口;
专用选择器;
私有地图数据地图;
公共EchoServer(InetAddress地址,int端口)引发IOException{
this.addr=addr;
this.port=端口;
dataMap=newhashmap();
startServer();
}
私有void startServer()引发IOException{
//创建选择器和通道
this.selector=selector.open();
ServerSocketChannel serverChannel=ServerSocketChannel.open();
serverChannel.configureBlocking(false);
//绑定到端口
InetSocketAddress listenadr=新的InetSocketAddress(this.addr,this.port);
serverChannel.socket().bind(listenadr);
serverChannel.register(this.selector,SelectionKey.OP_ACCEPT);
日志(“Echo服务器就绪。Ctrl-C停止”);
//加工
while(true){
//等待事件
this.selector.select();
//唤醒以使用选定的关键点
迭代器键=this.selector.selectedKeys().Iterator();
while(keys.hasNext()){
SelectionKey=(SelectionKey)keys.next();
//这是防止出现相同密钥所必需的
//下次再来。
键。移除();
如果(!key.isValid()){
继续;
}
if(key.isAcceptable()){
这个。接受(键);
}
else if(key.isReadable()){
这个。读(键);
}
else if(key.isWritable()){
这个。写(键);
}
}
}
}
私有void accept(SelectionKey)引发IOException{
ServerSocketChannel serverChannel=(ServerSocketChannel)key.channel();
SocketChannel通道=serverChannel.accept();
信道配置阻塞(假);
//写欢迎信
channel.write(ByteBuffer.wrap(“欢迎,这是echo服务器”\r\n.getBytes(“US-ASCII”));
套接字=channel.Socket();
SocketAddress remoteAddr=socket.getRemoteSocketAddress();
日志(“连接到:”+remoteAddr);
//用选择器注册通道,以便进一步输入输出
put(channel,newarraylist());
通道寄存器(this.selector,SelectionKey.OP_READ);
}
私有无效读取(SelectionKey)引发IOException{
SocketChannel=(SocketChannel)key.channel();
ByteBuffer buffer=ByteBuffer.allocate(8192);
int numRead=-1;
试一试{
numRead=channel.read(缓冲区);
}
捕获(IOE异常){
e、 printStackTrace();
}
如果(numRead==-1){
this.dataMap.remove(通道);
套接字=channel.Socket();
SocketAddress remoteAddr=socket.getRemoteSocketAddress();
日志(“客户端关闭的连接:“+remoteAddr”);
channel.close();
键。取消();
返回;
}
字节[]数据=新字节[numRead];
System.arraycopy(buffer.array(),0,data,0,numRead);
log(“Got:+新字符串(数据,US-ASCII”);
//回写客户
doEcho(密钥、数据);
}
私有无效写入(SelectionKey)引发IOException{
SocketChannel=(SocketChannel)key.channel();
List pendingData=this.dataMap.get(通道);
迭代器项=pendingData.Iterator();
while(items.hasNext()){
字节[]项=项。下一步();
项目。删除();
channel.write(ByteBuffer.wrap(项));
}
key.interesttops(选择key.OP_READ);
}
私有void doEcho(SelectionKey,字节[]数据){
SocketChannel=(SocketChannel)key.channel();
List pendingData=this.dataMap.get(通道);
pendingData.add(数据);
key.interesttops(选择key.OP_WRITE);
}
私有静态无效日志(字符串s){
系统输出打印项次;
}
公共静态void main(字符串[]args)引发异常{
新的EchoServer(空,8989);
}
}
对于这段代码,我有几个问题。第一,如果我读10个字节,但在读100个字节之前我不想做任何事情,我将如何实现它?另外,假设我只想在计数器达到某个数字时写入,我将如何实现非阻塞?这段代码的特点是,无论字节缓冲区有多大,它都会产生回声。如何更改它,使其仅在有100个字节时才会回显?如果计数器有一定的大小,我怎么能写?谢谢

在read方法中放置if(numRead<100){dorest}else{return}是否可以解决第一个问题?
另外,将if(counter>100){do rest}else{return}放在write方法中第二种方法是否有效?

您必须对该部分进行编码,基本上您需要跟踪读取的字节,不断将读取的字节添加到临时缓冲区,一旦达到所需的限制,就可以将该缓冲区传递给工作线程

我建议你使用它,它提供了所有你想要的东西。 看


希望这有助于

读取时,非阻塞模式的Did通道返回-1??对于您的问题,您可以设置bytebuffer限制:-

例如:-

ByteBuffer buff = ByteBuffer.allocate(1024);
buff.clear();

buff.limit(your_limit);//is this what you want??

while(buff.remaining>0&&channel.read(buff)); // if will reach till your limit only.

System.out.println(new String(buff.array()));
希望这有帮助