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);
    }
}

热门相关:桃花   豪门重生盛世闲女   最强反套路系统   最强装逼打脸系统   法医王妃不好当!