zoukankan      html  css  js  c++  java
  • Socket网络通信之BIO

    Socket网络通信之BIO

    如果要让两台计算机实现通信,需要的条件:ip,port,协议。

    目前我们用的最多的就是TCP/IP协议和UDP协议。TCP三次握手,所以比较慢,且安全;UDP速度快,但是可能丢包,不能保证安全。

    网络通讯基本都是通过Socket来通讯的。(客户端的Socket类;服务端的ServerSocket类)

    客户端和服务端这样建立连接:第一步客户端发起建立连接的请求,第二部服务端收到请求建立连接的请求,并同意和该客户端建立连接,并响应给客户端,第三步客户端收到服务端响应的建立连接的消息,并确认和服务端建立连接,通过这样三部客户端和服务端就真正的建立了连接,服务端和客户端就可以开始通讯,交互了.通过这样三次的握手交互服务端和客户端就成功的建立了连接,如下图所示

    而JAVA中实现通信的IO主要是:同步阻塞IO(BIO)、同步非阻塞IO(NIO)、异步非阻塞IO(AIO)

    同步阻塞IO(BIO)— 原生态

    public class BioServer {
    
        private  static Charset charset = Charset.forName("UTF-8");
        public static void main(String[] args) {
            int port = 1100;
    
            try ( ServerSocket  socketServer = new ServerSocket(port)){
                while (true){
                    //接收连接,如果没有连接建立,这里会阻塞
                    Socket socket = socketServer.accept();
                    BufferedReader reader = new BufferedReader(
                            new InputStreamReader(socket.getInputStream(), charset)
                    );
                    String msg = null;
                    //连接进来后,会在这里等待客户端发送消息。
                    while ((msg =reader.readLine())!=null){
                        System.out.println(msg);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    BioServer

    如上为server代码,下面为client代码

    public class BioClient implements  Runnable{
        private  String address;
        private  int  port;
    
        public static void main(String[] args) {
            BioClient client = new BioClient("localhost", 1100);
            client.run();
        }
    
        @Override
        public void run() {
            try(Socket socket = new Socket(address, port);
                OutputStream outputStream = socket.getOutputStream()) {
    
                Scanner scanner = new Scanner(System.in);
                System.out.println("请输入:");
                String msg = scanner.nextLine();
                outputStream.write(msg.getBytes("UTF-8"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public int getPort() {
            return port;
        }
    
        public void setPort(int port) {
            this.port = port;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
        public BioClient(String address, int port) {
            super();
            this.address=address;
            this.port = port;
        }
    }
    BioClient

    首先用debug启动BioServer。
    可以看到在debug模式下,下一步不能再执行,程序阻塞在连接处,等待连接。

    启动客户端BioClient以后,可以看到服务端已经连接,debug可以下一步,如下图。

    执行下一步下一步之后,等待客户端发送信息处,又阻塞,程序debug再次执行不下去,如下图:

    现在如果在客户端输入信息,再次debug下一步,服务端就会收到相应的信息。
    这就是一个简单的BIO,从这个过程中,我们可以看到:服务器端代码必须要在连接处和接收消息处阻塞,只用得到了相应的信息,才会往下继续执行。如果多个客户端一起请求,大家都会阻塞在此处,这样的程序效率及其低下,甚至长期等待不能用。有什么办法能够解决这样的问题呢?。

    同步阻塞IO(BIO)— 多线程

    public class BIOServerV2 {
        private static Charset charset = Charset.forName("UTF-8");
        public static void main(String[] args) {
            int port = 1101;
            try (ServerSocket ss = new ServerSocket(port);) {
                while (true) {
                    Socket s = ss.accept();
                    // 开一个线程去处理这个连接
                    new Thread(new SocketProcess(s)).start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        static class SocketProcess implements Runnable {
            Socket s;
            public SocketProcess(Socket s) {
                super();
                this.s = s;
            }
    
            @Override
            public void run() {
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(s.getInputStream(), charset));) {
                    // 接收数据、打印
                    String mess = null;
                    while ((mess = reader.readLine()) != null) {
                        System.out.println(mess);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    BIOServerV2

    如上代码所示,我们稍微改动了一下服务器端的代码,客户端只需要改动和服务端一样的端口号就行,这里使用多线程的方式。

    这里是利用多线程的方式来实现,在连接处多开线程去处理,这样多个客户端连接时,就不会大家一致排队阻塞在此处,但是阻塞还是存在的。还有什么更好的办法来解决这样的问题吗?

    同步阻塞IO(BIO)— 线程池

    public class BIOServerV3 {
        private static Charset charset = Charset.forName("UTF-8");
        public static void main(String[] args) {
            int port = 1102;
            int threads = 100;
            ExecutorService tpool = Executors.newFixedThreadPool(threads);
    
            try (ServerSocket ss = new ServerSocket(port);) {
                while (true) {
                    Socket s = ss.accept();
                    // 丢到线程池中去跑
                    tpool.execute(new SocketProcess(s));
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            tpool.shutdown();
        }
    
        static class SocketProcess implements Runnable {
            Socket s;
    
            public SocketProcess(Socket s) {
                super();
                this.s = s;
            }
    
            @Override
            public void run() {
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(s.getInputStream(), charset));) {
                    // 接收数据、打印
                    String mess = null;
                    while ((mess = reader.readLine()) != null) {
                        System.out.println(mess);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    BIOServerV3

    如上所示,我们再次改动,改成以线程池的办法来连接。但是线程池也有问题:

    请求大于线程,没有足够的线程来处理,响应时间很长,甚至服务器拒绝。

    阻塞等待接收客户端的数据时,这段时间占着线程,而池中线程数是有限的,并发量大时,将导致没有线程处理请求,请求的响应时间长,甚至拒绝服务。

    从上面三部分的代码可以看出来,代码性能等问题,全是阻塞惹的祸,如果没有阻塞就好了,如果能不阻塞,在没有数据时,就去干点别的事情,有数据了才处理数据。

    详情请见下一个博客:Socket网络通信之NIO

  • 相关阅读:
    C语言实现快排
    C语言实现双向循环链表
    mysql插入数据后返回自增ID的方法
    golang flag包简单例子
    练习题 (六)
    练习题 (五)
    练习题 (四)
    练习题 (三)
    练习题 (二)
    练习题 (一)
  • 原文地址:https://www.cnblogs.com/xiangpeng/p/10231813.html
Copyright © 2011-2022 走看看