当前位置: 首页 > 面试题库 >

Java网络:事件化的Socket / InputStream

秦才
2023-03-14
问题内容

我正在Java套接字上实现面向事件的层,我想知道是否有一种方法可以确定是否有待读取的数据。

我通常的方法是从套接字读取一个缓冲区,并在给定字节数的缓冲区中填充缓冲区时调用提供的回调(如果每次到达时都需要触发该回调,则可以为0),但是我怀疑Java已经在为我做缓冲了。

available()InputStream 的方法对此是否可靠?我应该只是read()在套接字顶部做自己的缓冲吗?还是有另一种方法?


问题答案:

简短地说,不。available()是不可靠的(至少对我来说不是)。我建议java.nio.channels.SocketChannelSelector和一起使用SelectionKey。该解决方案在某种程度上基于事件,但是比普通套接字更加复杂。

对于客户:

  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. 如果“新”表示成功连接,则为OP_READ; 注册选择器。如果“新”指的是可用数据,则只需从套接字读取。

但是,为了使其异步,您需要设置一个单独的线程(尽管套接字被创建为非阻塞的,但无论如何该线程都会阻塞),以检查是否已到达某些线程。

对于服务器,这里有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
  }
 }

希望这可以帮助。



 类似资料:
  • 模块介绍 Socket介绍 介绍来自:https://www.liaoxuefeng.com/wiki/1252599548343744/1305207629676577 在开发网络应用程序的时候,我们又会遇到Socket这个概念。Socket是一个抽象概念,一个应用程序通过一个Socket来建立一个远程连接,而Socket内部通过TCP/IP协议把数据传输到网络: Hutool封装 JDK中提供

  • 什么是 Socket Socket(套接字):是在网络上运行两个程序之间的双向通信链路的一个端点。socket绑定到一个端口号,使得 TCP 层可以标识数据最终要被发送到哪个应用程序。 正常情况下,一台服务器在特定计算机上运行,​​并具有被绑定到特定端口号的 socket。服务器只是等待,并监听用于客户发起的连接请求的 socket 。 在客户端:客户端知道服务器所运行的主机名称以及服务器正在侦听

  • Swoole提供了底层的网络socket服务器实现。普通用户只需要实现协议或基于现有的协议进行二次开发。 底层Driver BlockTCP 阻塞的tcp/udp server, 请求按顺序执行,必须处理完一个请求才能继续处理新的请求。 SelectTCP 使用select做IO复用的异步非阻塞 server,可以同时维持多个TCP连接。select最大只能维持1024个连接,并且性能会随着连接数

  • 这一小节我们介绍Powershell中的Socket编程,网络编程是所有语言中绕不开的核心点,下面我们通过对代码的分析来让大家对PS中的Socket有一个初步的了解。 Scoket-Tcp编程 开始之前我们先想想为什么要学习socket编程,那么最直观的是端口扫描,那么还有可能是反弹shell之类的应用。进行Socket编程只需要调用.Net框架即可,这里先使用TCP来示例: 这里是去打开一个TC

  • 用途: 提供对网络通信的访问 Addressing, Protocol Families and Socket Types Looking up Hosts on the Network Finding Service Information Looking Up Server Addresses IP Address Representations TCP/IP Client and Serve

  • 本文向大家介绍Java套接字(Socket)网络编程入门,包括了Java套接字(Socket)网络编程入门的使用技巧和注意事项,需要的朋友参考一下 网络应用模式主要有: 主机/终端模式:集中计算,集中管理; 客户机/服务器(Client/Server,简称C/S)模式:分布计算,分布管理; 浏览器/服务器模式:利用Internet跨平台。 www(万维网)就是建立在客户机/服务器模式上,以HTML