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

打印 上一主题 下一主题

主题 883|帖子 883|积分 2649

创建阻塞的 EchoClient

客户程序一般不需要同时建立与服务器的多个连接,因此用一个线程,按照阻塞模式运行就能满足需求
  1. public class EchoClient {
  2.    
  3.     private SocketChannel socketChannel = null;
  4.    
  5.     public EchoClient() throws IOException {
  6.         socketChannel = SocketChannel.open();
  7.         InetAddress ia = InetAddress,getLocalHost();
  8.         InetSocketAddress isa = new InetSocketAddress(ia,8000);
  9.         socketChannel.connect(isa); //连接服务器
  10.     }
  11.    
  12.     public static void main(String args[])throws IOException {
  13.         new EchoClient().talk();
  14.     }
  15.    
  16.     private PrintWriter getWriter(Socket socket) throws IOException {
  17.         OutputStream socketOut = socket.getOutputStream();
  18.         return new PrintWriter(socketOut,true);
  19.     }
  20.    
  21.     private BufferedReader getReader(Socket socket) throws IOException {
  22.         InputStream socketIn = socket.getInputStream();
  23.         return new BufferedReader(new InputStreamReader(socketIn));
  24.     }
  25.    
  26.     public void talk() throws IOException {
  27.         try {
  28.             BufferedReader br = getReader(socketChannel.socket());
  29.             PrintWriter pw = getWriter(socketChannel.socket());
  30.             
  31.             BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
  32.             
  33.             String msq = null;
  34.             
  35.             while((msg = localReader.readLine()) != null) {
  36.                 pw.println(msg);
  37.                 System.out.println(br.readLine());
  38.                 if(msq.equals("bye")) {
  39.                     break;
  40.                 }
  41.             }
  42.         } catch(IOException e) {
  43.             e.printStackTrace();
  44.         } finally {
  45.             try {
  46.                 socketChannel.close();
  47.             } catch(IOException e) {
  48.                 e.printStackTrace();
  49.             }
  50.         }
  51.     }
  52. }
复制代码
创建非阻塞的 EchoClient

对于客户与服务器之间的通信,按照它们收发数据的协调程度来区分,可分为同步通信和异步通信
同步通信指甲方向乙方发送了一批数据后,必须等接收到了乙方的响应数据后,再发送下一批数据。同步通信要求一个 IO 操作完成之后,才能完成下一个 IO 操作,用阻塞模式更容易实现
异步通信指发送数据和接收数据的操作互不干扰,各自独立进行。异步通信允许发送数据和接收数据的操作各自独立进行,用非阻塞模式更容易实现
值得注意的是,通信的两端并不要求都采用同样的通信方式,当一方采用同步通信时,另一方可以采用异步通信
  1. public class EchoClient {
  2.    
  3.     private SocketChannel socketChannel = null;
  4.     private ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
  5.     private ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
  6.     private Charset charset = Charset.forName("GBK");
  7.     private Selector selector;
  8.    
  9.     public EchoClient() throws IOException {
  10.         socketChannel = SocketChannel.open();
  11.         InetAddress ia = InetAddress.getLocalHost();
  12.         InetSocketAddress isa = new InetSocketAddress(ia, 8000);
  13.         socketChannel.connect(isa); //采用阻塞模式连接服务器
  14.         socketChannel.configureBlocking(false); //设置为非阻塞模式
  15.         selector = Selector.open();
  16.     }
  17.    
  18.     public static void main(String args[]) throws IOException {
  19.         final EchoClient client = new EchoClient();
  20.         Thread receiver=new Thread() {
  21.                  public void run() {
  22.                 client.receiveFromUser(); //接收用户向控制台输入的数据
  23.             }   
  24.         };
  25.         receiver.start();
  26.         client.talk();
  27.     }
  28.    
  29.     /** 接收用户从控制台输入的数据,放到sendBuffer中 */
  30.     public void receiveFromUser() {
  31.         try {
  32.             BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
  33.             String msg = null;
  34.             while((msg = localReader.readLine()) != null) {
  35.                 synchronized(sendBuffer) {
  36.                     sendBuffer.put(encode(msg + "\r\n"));
  37.                 }
  38.                 if (msg.equals("bye")) {
  39.                     break;
  40.                 }
  41.             }
  42.         } catch(IOException e) {
  43.             e.printStackTrace();
  44.         }
  45.     }
  46.    
  47.     //接收和发送数据
  48.     public void talk() throws IOException {
  49.         socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
  50.         while (selector.select() > 0 ) {
  51.             Set readyKeys = selector.selectedKeys();
  52.             Iterator it = readyKeys.iterator();
  53.             while (it.hasNext()) {
  54.                 SelectionKey key = null;
  55.                 try {
  56.                     key = (SelectionKey) it.next();
  57.                     it.remove();
  58.                     if (key.isReadable()) {
  59.                         receive(key);
  60.                     }
  61.                     if (key.isWritable()) {
  62.                         send(key);
  63.                     }
  64.                 } catch(IOException e) {
  65.                     e.printStackTrace();
  66.                     try {
  67.                         if(key != null) {
  68.                             key.cancel();
  69.                             key.channel().close() ;
  70.                         }
  71.                     } catch(Exception ex) {
  72.                         e.printStackTrace();
  73.                     }
  74.                 }
  75.             }
  76.         }
  77.     }
  78.    
  79.     public void send(SelectionKey key) throws IOException {
  80.         //发送sendBuffer的数据
  81.         SocketChannel socketChannel = (SocketChannel)key.channel();
  82.         synchronized(sendBuffer) {
  83.             sendBuffer.flip(); //把极限设为位置,把位置设为0
  84.             socketChannel.write(sendBuffer); //发送数据
  85.             sendBuffer.compact(); //删除已经发送的数据
  86.         }
  87.     }
  88.    
  89.     public void receive(SelectionKey key) throws IOException {
  90.         //接收EchoServer发送的数据,把它放到receiveBuffer
  91.         //如果receiveBuffer有一行数据,就打印这行数据,然后把它从receiveBuffer删除
  92.         SocketChannel socketChannel = (SocketChannel) key.channel();
  93.         socketChannel.read(receiveBuffer):
  94.         
  95.         receiveBuffer.flip();
  96.         String receiveData = decode (receiveBuffer);
  97.         
  98.         if(receiveData.indexOf("\n") == -1) return;
  99.         
  100.         String outputData = receiveData.substring(0, receiveData.indexOf("\n") + 1):
  101.         
  102.         System.out.print(outputData);
  103.         
  104.         if(outputData.equals("echo:bye\r\n")) {
  105.             key.cancel():
  106.             socketChannel.close();
  107.             selector.close();
  108.             System.exit(0);
  109.         }
  110.         
  111.         ByteBuffer temp = encode(outputData);
  112.         receiveBuffer.position(temp.limit());
  113.         receiveBuffer.compact(): //删除已经打印的数据
  114.     }
  115.    
  116.     //解码
  117.     public String decode(ByteBuffer buffer) {
  118.         CharBuffer charBuffer= charset.decode(buffer);
  119.         return charBuffer.toString();
  120.     }
  121.            
  122.     //编码
  123.         public ByteBuffer encode(String str) {
  124.         return charset.encode(str);
  125.     }
  126. }
复制代码

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

悠扬随风

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表