簡體   English   中英

Java網絡:即興的Socket / InputStream

[英]Java networking: evented Socket/InputStream

我正在Java的套接字上實現一個面向事件的層,我想知道是否有辦法確定是否有待讀取的數據。

我的常規方法是從套接字讀入緩沖區,並在緩沖區填充給定量的字節時調用提供的回調(如果每次到達時都需要觸發回調,則可以為0),但是我懷疑Java已經在為我做緩沖了。

InputStream的available()方法是否可靠? 我應該read()並在Socket上做自己的緩沖嗎? 或者還有另一種方式嗎?

不久,沒有。 available()不可靠(至少不適合我)。 我建議使用與SelectorSelectionKey連接的java.nio.channels.SocketChannel 這個解決方案有點基於事件,但比普通的套接字更復雜。

對於客戶:

  1. 構造套接字通道( socket ),打開一個選擇器( selector = Selector.open(); )。
  2. 使用非阻塞socket.configureBlocking(false);
  3. 注冊連接器socket.register(selector, SelectionKey.OP_CONNECT);
  4. 連接socket.connect(new InetSocketAddress(host, port));
  5. 看看是否有新的selector.select();
  6. 如果“new”表示連接成功,則為OP_READ注冊選擇器; 如果“new”指的是可用的數據,只需從套接字中讀取即可。

但是,為了使它具有異步性,您需要設置一個單獨的線程(盡管套接字被創建為非阻塞,但線程仍會阻塞),它會檢查是否已經到達某些東西。

對於服務器,有ServerSocketChannel ,您可以使用OP_ACCEPT

作為參考,這是我的代碼(客戶端),應該給你一個提示:

 private Thread readingThread = new ListeningThread();

 /**
  * Listening thread - reads messages in a separate thread so the application does not get blocked.
  */
 private class ListeningThread extends Thread {
  public void run() {
   running = true;
   try {
    while(!close) listen();
    messenger.close();
   }
   catch(ConnectException ce) {
    doNotifyConnectionFailed(ce);
   }
   catch(Exception e) {
//    e.printStackTrace();
    messenger.close();
   }
   running = false;
  }
 }

 /**
  * Connects to host and port.
  * @param host Host to connect to.
  * @param port Port of the host machine to connect to.
  */
 public void connect(String host, int port) {
  try {
   SocketChannel socket = SocketChannel.open();
   socket.configureBlocking(false);
   socket.register(this.selector, SelectionKey.OP_CONNECT);
   socket.connect(new InetSocketAddress(host, port));
  }
  catch(IOException e) {
   this.doNotifyConnectionFailed(e);
  }
 }

 /**
  * Waits for an event to happen, processes it and then returns.
  * @throws IOException when something goes wrong.
  */
 protected void listen() throws IOException {
  // see if there are any new things going on
  this.selector.select();
  // process events
  Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
  while(iter.hasNext()) {
   SelectionKey key = iter.next();
   iter.remove();
   // check validity
   if(key.isValid()) {
    // if connectable...
    if(key.isConnectable()) {
     // ...establish connection, make messenger, and notify everyone
     SocketChannel client = (SocketChannel)key.channel();
     // now this is tricky, registering for OP_READ earlier causes the selector not to wait for incoming bytes, which results in 100% cpu usage very, very fast
     if(client!=null && client.finishConnect()) {
      client.register(this.selector, SelectionKey.OP_READ);
     }
    }
    // if readable, tell messenger to read bytes
    else if(key.isReadable() && (SocketChannel)key.channel()==this.messenger.getSocket()) {
     // read message here
    }
   }
  }
 }

 /**
  * Starts the client.
  */
 public void start() {
  // start a reading thread
  if(!this.running) {
   this.readingThread = new ListeningThread();
   this.readingThread.start();
  }
 }

 /**
  * Tells the client to close at nearest possible moment.
  */
 public void close() {
  this.close = true;
 }

對於服務器:

 /**
  * Constructs a server.
  * @param port Port to listen to.
  * @param protocol Protocol of messages.
  * @throws IOException when something goes wrong.
  */
 public ChannelMessageServer(int port) throws IOException {
  this.server = ServerSocketChannel.open();
  this.server.configureBlocking(false);
  this.server.socket().bind(new InetSocketAddress(port));
  this.server.register(this.selector, SelectionKey.OP_ACCEPT);
 }

 /**
  * Waits for event, then exits.
  * @throws IOException when something goes wrong.
  */
 protected void listen() throws IOException {
  // see if there are any new things going on
  this.selector.select();
  // process events
  Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
  while(iter.hasNext()) {
   SelectionKey key = iter.next();
   // do something with the connected socket
   iter.remove();
   if(key.isValid()) this.process(key);
  }
 }

 /**
  * Processes a selection key.
  * @param key SelectionKey.
  * @throws IOException when something is wrong.
  */
 protected void process(SelectionKey key) throws IOException {
  // if incoming connection
  if(key.isAcceptable()) {
   // get client
   SocketChannel client = (((ServerSocketChannel)key.channel()).accept());
    try {
     client.configureBlocking(false);
     client.register(this.selector, SelectionKey.OP_READ);
    }
    catch(Exception e) {
     // catch
    }
  }
  // if readable, tell messenger to read
  else if(key.isReadable()) {
  // read
  }
 }

希望這可以幫助。

available()只會告訴您是否可以在不進入操作系統的情況下讀取數據。 它在這里不是很有用。

您可以根據需要執行阻止或非阻塞讀取。 當沒有要讀取的數據時,只會返回非阻塞讀取,這可能就是您想要的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM