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

如果你不相信努力和时光,那么成果就会是第一个选择辜负你的。不要去否定你自己的过去,也不要用你的过去牵扯你现在的努力和对未来的展望。不是因为拥有希望你才去努力,而是去努力了,你才有可能看到希望的光芒。Java 网络编程 —— 实现非阻塞式的客户端,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

创建阻塞的 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);
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/200051.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!