美文网首页Java网络编程
Java网络编程模型(一)

Java网络编程模型(一)

作者: Super昕 | 来源:发表于2019-02-12 17:51 被阅读0次

        本系列主要介绍java网络编程的模型,沿着模型的进化线结合案例分析学习。本文主要介绍基于OIO的网络编程模型。

    基于BIO(OIO)的网络编程模型

        BIO(OIO)是指阻塞输入输出流(旧输入输出流),基于阻塞输入输出流的网络编程初始具有以下特点:
    1、服务端启动后会阻塞直到监测到有客户端连接;
    2、客户端发起连接请求,获取输入流读取数据,如果没有数据可读取将会一直处于阻塞状态;
    3、所有连接处理都在一个线程中进行,因此在连接数较多时,连接请求需要排队等候;

    案例——服务端:

    package javanio.oionet;
    import java.io.OutputStreamWriter;
    import java.io.Writer;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Date;
    /**
     * @author 54353
     * OIO网络模型服务端:
     * 向客户端写入服务端接收时间
     */
    public class TestServer {
        public static void main(String[] args) {
            try (ServerSocket serverSocket=new ServerSocket(12121)){
                while(true) {//1 循环等待连接
                    //2 accept()阻塞直到有连接进来,返回对等端socket对象
                    Socket client=serverSocket.accept();
                    System.out.println("接收到客户端请求!!");
                    Writer bWriter=new OutputStreamWriter(client.getOutputStream(),"ASCII");
                    Date date=new Date();
                    //3 输出流中写入时间
                    while(true) {//确保任务不停进行
                        bWriter.write(date.toString()+'\r'+'\n');
                        //4 确保数据写入
                        bWriter.flush();
                        System.out.println("数据传输完成!");
                    }
                }
            } catch (Exception e) {
                System.out.println("端口可能被占用!");
            }
        }
    }
    

    案例——客户端

    package javanio.oionet;
    
    import java.io.InputStreamReader;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    public class TestClient2 {
    
        public static void main(String[] args) {
            Socket socket=null;
            try {
                socket=new Socket("127.0.0.1", 12121);
                InputStreamReader reader=new InputStreamReader(socket.getInputStream(),"ASCII");
                System.out.println("读取服务端传送过来的数据!");
                for(int c=reader.read();c!=-1;c=reader.read()) {
                    System.out.print((char)c);
                }
            } catch (UnknownHostException e) {
                System.out.println("请求的主机地址不存在!");
            }catch (Exception e) {
                e.printStackTrace();
            }finally {
                if (socket!=null) {
                    try {
                        socket.close();
                    } catch (Exception e2) {
                        e2.printStackTrace();
                    }
                }
            }
        }
    
    }
    

        这里客户端同样的demo有三个,启动服务端,启动客户端后,服务端输出结果如下:


    server.png

    客户端2输出结果如下:


    client2.png

    客户端3输出结果如下:


    client3.png

        TestClient2,TestClient3启动后,TestClient2不断输出服务端写入的时间,TestClient3却没有输出任何的时间数据。这是因为所有客户端连接共享一个服务端线程,服务端在不断向TestClient2输出数据,TestClient3必须等到TestClient2的请求结束,服务端向其输出数据后才能输出时间数据。这也是本文一开始提到的第三个特点。
    针对这种模式存在的缺陷,基于OIO提出了多线程的模型,也即是服务端针对每一个请求开启一个新的线程去处理数据:
    服务端的改进:

    package javanio.oionet;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.io.Writer;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Date;
    /**
     * @author 54353
     * OIO网络模型服务端:
     * 向客户端写入服务端接收时间
     */
    public class TestServer2 {
        
         static class  Task implements Runnable{
              private Socket client;
              public Task(Socket client) {
                this.client=client;
                }
            @Override
            public void run() {
                System.out.println("接收到客户端请求!!");
                Writer bWriter;
                try {
                    bWriter = new OutputStreamWriter(client.getOutputStream(),"ASCII");
                    Date date=new Date();
                    //4 输出流中写入时间
                    while(true) {//任务不断进行
                        bWriter.write(date.toString()+'\r'+'\n');
                        //5 确保数据写入
                        bWriter.flush();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                
            }
             
         }
    
        public static void main(String[] args) {
            try (ServerSocket serverSocket=new ServerSocket(12122)){
                while(true) {//1 循环等待连接
                    //2 accept()阻塞直到有连接进来,返回对等端socket对象
                    Socket client=serverSocket.accept();
                    //3 一旦有连接进来,开启新的线程处理连接
                    new Thread(new Task(client)).start();
                }
            } catch (Exception e) {
                System.out.println("端口可能被占用!");
            }
        }
    
    }
    
    

        在运用多线程处理连接后,输出结果下:
    服务端输出


    server2.png

    几个客户端输出结果相同


    client2.png
    client3.png

        由此可见,运用多线程OIO模型可以解决,多请求同时发生时的排队等候问题,可以带来更好的客户体验。但是如果线程数过多,对内存的消耗大,同时线程数过多,线程轮转带来的消耗也会非常大,因此肆无忌惮的使用多线程去处理并发请求问题只能算是一种蛮干,对于高并发场景,更是会带来灾难性危害。

    关于Reactor模式

        Reactor模式也即响应模式,就本文所涉及的网络编程而言,只有获取到客户端的连接请求后,服务端才执行后续的程序。对于OIO阻塞输入输出流,显然还存在一个明显缺陷:如果一直未接收到客户端请求,服务端一直处于阻塞状态,后面与接入无关的代码也需要等待,这显然会浪费很多的CPU时间,因此从这点考虑,多线程的OIO模式仍存在进步空间。

    相关文章

      网友评论

        本文标题:Java网络编程模型(一)

        本文链接:https://www.haomeiwen.com/subject/yopheqtx.html