Java网络编程模型(一)

    本系列主要介绍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模式仍存在进步空间。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 计算机网络概述 网络编程的实质就是两个(或多个)设备(例如计算机)之间的数据传输。 按照计算机网络的定义,通过一定...
    蛋炒饭_By阅读 4,997评论 0 10
  • 在一个方法内部定义的变量都存储在栈中,当这个函数运行结束后,其对应的栈就会被回收,此时,在其方法体中定义的变量将不...
    Y了个J阅读 9,872评论 1 14
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,800评论 1 32
  • “主上,这是暗隐楼送来的新一批暗卫,主人看有要留下的吗。”慕雨跪地向着一位一身深蓝色衣服正在研究棋盘的青年道。 慕...
    9daf83399845阅读 28,591评论 0 0
  • 高难度谈话读后感 之前两个晚上读了一遍《高难度谈话》,但并没有认真去读去思考,这次打算仔细阅读,并加上自己或者别人...
    Asensios阅读 1,079评论 0 0