悠扬随风 发表于 2023-5-21 00:18:18

Java 网络编程 —— 实现非阻塞式的客户端

创建阻塞的 EchoClient

客户程序一般不需要同时建立与服务器的多个连接,因此用一个线程,按照阻塞模式运行就能满足需求
public class EchoClient {
   
    private SocketChannel socketChannel = null;
   
    public EchoClient() throws IOException {
      socketChannel = SocketChannel.open();
      InetAddress ia = InetAddress,getLocalHost();
      InetSocketAddress isa = new InetSocketAddress(ia,8000);
      socketChannel.connect(isa); //连接服务器
    }
   
    public static void main(String args[])throws IOException {
      new EchoClient().talk();
    }
   
    private PrintWriter getWriter(Socket socket) throws IOException {
      OutputStream socketOut = socket.getOutputStream();
      return new PrintWriter(socketOut,true);
    }
   
    private BufferedReader getReader(Socket socket) throws IOException {
      InputStream socketIn = socket.getInputStream();
      return new BufferedReader(new InputStreamReader(socketIn));
    }
   
    public void talk() throws IOException {
      try {
            BufferedReader br = getReader(socketChannel.socket());
            PrintWriter pw = getWriter(socketChannel.socket());
            
            BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
            
            String msq = null;
            
            while((msg = localReader.readLine()) != null) {
                pw.println(msg);
                System.out.println(br.readLine());
                if(msq.equals("bye")) {
                  break;
                }
            }
      } catch(IOException e) {
            e.printStackTrace();
      } finally {
            try {
                socketChannel.close();
            } catch(IOException e) {
                e.printStackTrace();
            }
      }
    }
}
创建非阻塞的 EchoClient

对于客户与服务器之间的通信,按照它们收发数据的协调程度来区分,可分为同步通信和异步通信
同步通信指甲方向乙方发送了一批数据后,必须等接收到了乙方的响应数据后,再发送下一批数据。同步通信要求一个 IO 操作完成之后,才能完成下一个 IO 操作,用阻塞模式更容易实现
异步通信指发送数据和接收数据的操作互不干扰,各自独立进行。异步通信允许发送数据和接收数据的操作各自独立进行,用非阻塞模式更容易实现
值得注意的是,通信的两端并不要求都采用同样的通信方式,当一方采用同步通信时,另一方可以采用异步通信
public class EchoClient {
   
    private SocketChannel socketChannel = null;
    private ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
    private ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
    private Charset charset = Charset.forName("GBK");
    private Selector selector;
   
    public EchoClient() throws IOException {
      socketChannel = SocketChannel.open();
      InetAddress ia = InetAddress.getLocalHost();
      InetSocketAddress isa = new InetSocketAddress(ia, 8000);
      socketChannel.connect(isa); //采用阻塞模式连接服务器
      socketChannel.configureBlocking(false); //设置为非阻塞模式
      selector = Selector.open();
    }
   
    public static void main(String args[]) throws IOException {
      final EchoClient client = new EchoClient();
      Thread receiver=new Thread() {
                 public void run() {
                client.receiveFromUser(); //接收用户向控制台输入的数据
            }   
      };
      receiver.start();
      client.talk();
    }
   
    /** 接收用户从控制台输入的数据,放到sendBuffer中 */
    public void receiveFromUser() {
      try {
            BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
            String msg = null;
            while((msg = localReader.readLine()) != null) {
                synchronized(sendBuffer) {
                  sendBuffer.put(encode(msg + "\r\n"));
                }
                if (msg.equals("bye")) {
                  break;
                }
            }
      } catch(IOException e) {
            e.printStackTrace();
      }
    }
   
    //接收和发送数据
    public void talk() throws IOException {
      socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
      while (selector.select() > 0 ) {
            Set readyKeys = selector.selectedKeys();
            Iterator it = readyKeys.iterator();
            while (it.hasNext()) {
                SelectionKey key = null;
                try {
                  key = (SelectionKey) it.next();
                  it.remove();
                  if (key.isReadable()) {
                        receive(key);
                  }
                  if (key.isWritable()) {
                        send(key);
                  }
                } catch(IOException e) {
                  e.printStackTrace();
                  try {
                        if(key != null) {
                            key.cancel();
                            key.channel().close() ;
                        }
                  } catch(Exception ex) {
                        e.printStackTrace();
                  }
                }
            }
      }
    }
   
    public void send(SelectionKey key) throws IOException {
      //发送sendBuffer的数据
      SocketChannel socketChannel = (SocketChannel)key.channel();
      synchronized(sendBuffer) {
            sendBuffer.flip(); //把极限设为位置,把位置设为0
            socketChannel.write(sendBuffer); //发送数据
            sendBuffer.compact(); //删除已经发送的数据
      }
    }
   
    public void receive(SelectionKey key) throws IOException {
      //接收EchoServer发送的数据,把它放到receiveBuffer
      //如果receiveBuffer有一行数据,就打印这行数据,然后把它从receiveBuffer删除
      SocketChannel socketChannel = (SocketChannel) key.channel();
      socketChannel.read(receiveBuffer):
      
      receiveBuffer.flip();
      String receiveData = decode (receiveBuffer);
      
      if(receiveData.indexOf("\n") == -1) return;
      
      String outputData = receiveData.substring(0, receiveData.indexOf("\n") + 1):
      
      System.out.print(outputData);
      
      if(outputData.equals("echo:bye\r\n")) {
            key.cancel():
            socketChannel.close();
            selector.close();
            System.exit(0);
      }
      
      ByteBuffer temp = encode(outputData);
      receiveBuffer.position(temp.limit());
      receiveBuffer.compact(): //删除已经打印的数据
    }
   
    //解码
    public String decode(ByteBuffer buffer) {
      CharBuffer charBuffer= charset.decode(buffer);
      return charBuffer.toString();
    }
           
    //编码
        public ByteBuffer encode(String str) {
      return charset.encode(str);
    }
}

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Java 网络编程 —— 实现非阻塞式的客户端