首页 > 编程语言 >Java 网络编程 —— 实现非阻塞式的客户端

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

时间:2023-05-20 23:25:31浏览次数:42  
标签:throws Java 编程 socketChannel IOException key new public 客户端

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

标签:throws,Java,编程,socketChannel,IOException,key,new,public,客户端
From: https://www.cnblogs.com/Yee-Q/p/17417994.html

相关文章

  • 关于Java接口实现问题
    publicinterfaceInterfaceClass{/***jdk1.7只能有抽象方法,子类是**抽象类**时,方法就可以实现也可以不实现*/publicabstractvoidmethod();/***jdk1.8新增静态方法,默认方法**静态方法子类不能实现*/publicstaticvoidmethod1(){}/**......
  • 程序员喜欢用程 Mac 进行编程
     大部分情况都是公司发什么用什么。如果可以选,就选自己用得最顺手的,赶紧搞完收工。有公司,不知道上面出了什么问题,要求新换的电脑必须要给程序Mac,有人就是死扛不要Mac,不是因为Mac有多不好,也不是因为Windows有多好,就是懒得再学一套快捷键。当然和每天用的东西也有关系,天天......
  • c语言趣味编程
    三色球问题1#include<iostream>2usingnamespacestd;3intmain()4{5intcount=0;6for(inti=0;i<=3;i++)7{8for(intj=0;j<=3;j++)9{10for(intk=0;k<=6;k++)11{12......
  • Java中的包
    Java中的包包就是文件夹,用来管理各种不同的java类,方便后期维护规则:公司域名反写+包的作用,全部英文小写全类名:包名+类名 注意:使用同一个包中的类时,不需要导包使用java.lang包中的类时,不需要导包其他情况都需要导包如果同时使用两个包中的同名类,需要全类名......
  • java 实现对象排序,实现java对象排序的三种方式
    1.自然排序:要排序的对象类实现Comparable<>接口,重写其compareTo()方法,方法体中实现对象的比较大小规则2.自定义排序,需编写匿名内部类,先new一个Comparator接口的比较器对象c,同时实现compare()其方法;然后将比较器对象c传给Collections.sort()方法的参数列表中,实现排序功能,里面......
  • Scala:一门灵活多态的编程语言
    基本语法和数据类型Scala的语法有很多共性与Java,它们都是基于C语言的语法规则。Scala中的数据类型也与Java类似,包括整数、浮点数、布尔值和字符串。你可以像Java一样声明和使用这些数据类型。与Java不同的是,Scala中的数值类型默认是基于对象的,因此你可以像调用对象方法一样调用数值......
  • Java中的并发编程:线程池的使用与优化
    在Java编程中,处理并发任务是一项常见而重要的任务。合理地管理线程可以提高程序的性能和响应性。本文将介绍Java中线程池的使用和优化技巧,帮助开发者更好地处理并发编程的挑战。1.线程池的基本概念线程池是一种用于管理线程的技术,它通过预先创建一组线程,并将任务分配给这些线程来......
  • Dockfile练习一:给ubuntu1804设置Java环境
    [root@mondoopt]#catDockerfile#BaseimageFROMubuntu:18.04#MAINTAINERMAINTAINERzhangjq<[email protected]># 将宿主机的软件包,复制到容器里的/usr/local/src目录下面去ADDjdk-8u321-linux-x64.tar.gz/usr/local/src/# 将上面的容器软件包进行解压,解压到jdk1.8.......
  • 实验4 函数与异常处理编程
    1.实验任务11print(sum)2sum=423print(sum)45definc(n):6sum=n+17print(sum)8returnsum910sum=inc(7)+inc(7)11print(sum)问题:不是。line1中的sum是指Python的内置函数;line3中的sum指的是line2中的全局变量sum;line7中......
  • Java生成二维码及条形码工具
    一:前言二维码是一种可以存储信息的矩形图案,它可以在移动设备上进行扫描和读取信息。Java语言中有许多库可以用于生成和解码二维码,其中com.google.zxing是一种常用的库。com.google.zxing是一个开源的Java库,它可以用于生成和解码各种类型的二维码和条形码。这个库的优点是易......