输出流中flush方法,以及PrintStrem和PrintWriter的自动刷新,BufferOutputStream类的解读。

这是我的一个作业,因为没有用了PrintWriter,但是没有及时flush,导致服务端一直收不到客户端的数据。可跳过这部分去看下面的。

package mysocket;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class ServerThread extends Thread{


    @Override
    public void run() {
        try {
            ServerSocket serverSocket=new ServerSocket(3999);
            Socket socket=serverSocket.accept();
            System.out.println(socket.getInetAddress()+" port:"+socket.getPort());
            Scanner scan=new Scanner(socket.getInputStream());
            PrintWriter printWriter=new PrintWriter(socket.getOutputStream());//,true);
            scan.useDelimiter("\n");
            int flag=1;
            while(flag==1){
               // System.out.println("1111111111111");
                String val= scan.next().trim();
                System.out.println(val);
                if("byebye".equalsIgnoreCase(val)){
                    flag=0;
                    printWriter.println("ByeBye客户端");
                }else{

                    printWriter.println("服务端已收到ECHO"+val);
                }
                printWriter.flush();
    

            }

            scan.close();
            printWriter.close();
            socket.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
/*********************************************************************************************/

package mysocket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class ClientThread extends Thread{
    private static final BufferedReader KEYBOARD_INPUT = new BufferedReader(
            new InputStreamReader(System.in));
    public String inputString() throws IOException {
        System.out.print("客户端请输入:");
        String v=KEYBOARD_INPUT.readLine();
        return v;
    }
    @Override
    public void run(){
        try {
            Socket socket = new Socket("127.0.0.1",3999);
            PrintWriter printWriter=new PrintWriter(socket.getOutputStream());
            Scanner scan=new Scanner(socket.getInputStream());
            scan.useDelimiter("\n");
            int flag=1;
            while(flag==1){
                String val=inputString();
                printWriter.println(val);
                printWriter.flush();
                if("byebye".equals(val)){
                    flag=0;
                }
                if(scan.hasNext()){
                    System.out.println(scan.next());
                }
            }
            scan.close();
            printWriter.close();
            socket.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

/*********************************************************************************************/
package mysocket;

public class Main {
    public static void main(String[] args){
        ServerThread server=new ServerThread();
        server.start();

        ClientThread client=new ClientThread();
        client.start();
    }


}

Bug:由于使用PrintWriter,不手动刷新,又不设置自动刷新 即new PrintWriter(OutputString out,autoFlush true),导致服务端一直阻塞,拿不到客户端发来的数据,程序无法按照正常走下去。

flush方法

另外,flush方法,在FileOutputStream中,没有重写,而OutputStream中的flush方法有是一个空的方法。其实之前有一个误区是,以为都要flush的。其实如果没有使用到缓冲区,根本就不需要使用flush,write方法直接将数据交个底层操作系统写入。只有使用了Buffer的才需要flush。
flush方法有三种情况。
1.OutputStream中的flush是一个空的方法。
2.有一些实现类中,没有重写flush方法,直接是继承父类的。
3.flush方法被重写,如BufferedOutputStream

PrintWriter中的自动刷新,println()会自动刷新。

 public void println() {
        newLine();
    }

 
    public void println(boolean x) {
        synchronized (lock) {
            print(x);
            println();
        }
    }

 private void newLine() {
        try {
            synchronized (lock) {
                ensureOpen();
                out.write(lineSeparator);
                if (autoFlush)/*************************这里是关键********************/
                    out.flush();
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }

PrintWriter PrintStream自动刷新

newline()方法中的,out.flush是自动刷新的关键。
out所指向的对象可能是下面两种情况(多态)
1.没有重写flush的对象(即是不用flush的,没写入一个,都会直接交个操作系统写入的,这种类型的输出流自然不需要flush的,使用了也相当于没有使用,效果是一样的)
2.重写了flush的对象,如果自动刷新为true,则自然会把缓冲区中的刷出去。
所以会自动刷新。

BufferOutputStream类详解

源码如下:

public
class BufferedOutputStream extends FilterOutputStream {
    /**
     * The internal buffer where data is stored.
     */
    protected byte buf[];//缓冲区数组

    /**
     * The number of valid bytes in the buffer. This value is always
     * in the range <tt>0</tt> through <tt>buf.length</tt>; elements
     * <tt>buf[0]</tt> through <tt>buf[count-1]</tt> contain valid
     * byte data.
     */
    protected int count;//缓冲区有效字节数

    /**
     * Creates a new buffered output stream to write data to the
     * specified underlying output stream.
     *
     * @param   out   the underlying output stream.
     */
    public BufferedOutputStream(OutputStream out) {//默认定义一个8k的缓冲区
        this(out, 8192);
    }

    /**
     * Creates a new buffered output stream to write data to the
     * specified underlying output stream with the specified buffer
     * size.
     *
     * @param   out    the underlying output stream.
     * @param   size   the buffer size.
     * @exception IllegalArgumentException if size &lt;= 0.
     */
    public BufferedOutputStream(OutputStream out, int size) {
        super(out);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }

    /** Flush the internal buffer */
    private void flushBuffer() throws IOException {
        if (count > 0) {
            out.write(buf, 0, count);//真是有效的刷新写入。
            count = 0;//清空缓冲区
        }
    }

    /**
     * Writes the specified byte to this buffered output stream.
     *
     * @param      b   the byte to be written.
     * @exception  IOException  if an I/O error occurs.
     */
    public synchronized void write(int b) throws IOException {
        if (count >= buf.length) {
            flushBuffer();//如果缓冲区满,则交给底层操作系统写入。
        }
        buf[count++] = (byte)b;
    }

    /**
     * @param      b     the data.
     * @param      off   the start offset in the data.
     * @param      len   the number of bytes to write.
     * @exception  IOException  if an I/O error occurs.
     */
    public synchronized void write(byte b[], int off, int len) throws IOException {
        if (len >= buf.length) {//如果新来的要写入缓冲区的数据的长度大于缓冲区的长度
            /* If the request length exceeds the size of the output buffer,
               flush the output buffer and then write the data directly.
               In this way buffered streams will cascade harmlessly. */
            flushBuffer();//先将缓冲去中的数据写入()
            out.write(b, off, len);
            //然后在用out(真实对象)的write方法写入。因为真实对象out的write方法,是直接交给操作系统写入的。
            //数据都大于缓冲区了,直接交给操作系统最快了,不用经过缓冲区了。
            return;
        }
        if (len > buf.length - count) {//如果剩余的缓冲区长度,不足以存放新来的数据。//先刷出
            flushBuffer();
        }
        System.arraycopy(b, off, buf, count, len);//这样缓冲区足以存放新来的数据,则将它复制进缓冲区。
        count += len;
    }


    public synchronized void flush() throws IOException {
        flushBuffer();
        out.flush();//暂时我的认知认为它没有用。
    }
}

其实BufferOutputStream只是对其他类进行包装装饰一次。对于没有重写过fuush方法的类,里面的write方法是直接交给操作系统区写入的,如果频繁的交给操作系统区写入,对资源的消耗是相当大的,因为要启动磁盘,找扇区,寻道等会相当耗时,如果为了写入一个或者几个字节的数据而启动一次,是不值得。缓冲区的概念也并不是很深奥的概念,就是一个大一点的数组,暂时存放数据,称之为缓冲,等到数据满之后一次性写入到磁盘中,这样就可以减少磁盘的IO次数,性能大大提高。BufferOutputStream中的buf就是缓冲区,可以看到BufferOutputStream中的write方法并不是直接就交给操作系统写入的,而是等待缓冲区满的时候在一次性写入(不是全部情况,全部情况看上面的源码)。
为什么我说BufferOutStream只是对其他的OuputStream 的一次包装装饰,因为BufferOutputStream中的write方法还是需要调用它所装饰的对象out的write方法。如下:

  /** Flush the internal buffer */
    private void flushBuffer() throws IOException {
        if (count > 0) {
            out.write(buf, 0, count);//真是有效的刷新写入。用的还是所装饰的对象out的write方式(是直接交个操作系统写入的)
            count = 0;//清空缓冲区
        }
    }

平时我们说BufferOutputStream等缓冲流能够加快速度,就是上面所说的把原本需要很多次IO的的操作,通过缓冲区暂时存下来,一次写入,这提升了性能。一个例子就是下面。

FileOutputStream fos=new FileOutputStream(...);
fos.write(1;
fos,write("aa");
fos.write(2);
fos.write("bb");

这个过程需要四次IO操作。如果使用BufferOutputStream只需要一次IO操作(因为默认缓冲区为8k,足以暂时存放这些数据)。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
在现有省、市港口信息化系统进行有效整合基础上,借鉴新 一代的感知-传输-应用技术体系,实现对码头、船舶、货物、重 大危险源、危险货物装卸过程、航管航运等管理要素的全面感知、 有效传输和按需定制服务,为行政管理人员和相关单位及人员提 供高效的管理辅助,并为公众提供便捷、实时的水运信息服务。 建立信息整合、交换和共享机制,建立健全信息化管理支撑 体系,以及相关标准规范和安全保障体系;按照“绿色循环低碳” 交通的要求,搭建高效、弹性、高可扩展性的基于虚拟技术的信 息基础设施,支撑信息平台低成本运行,实现电子政务建设和服务模式的转变。 实现以感知港口、感知船舶、感知货物为手段,以港航智能 分析、科学决策、高效服务为目的和核心理念,构建“智慧港口”的发展体系。 结合“智慧港口”相关业务工作特点及信息化现状的实际情况,本项目具体建设目标为: 一张图(即GIS 地理信息服务平台) 在建设岸线、港口、港区、码头、泊位等港口主要基础资源图层上,建设GIS 地理信息服务平台,在此基础上依次接入和叠加规划建设、经营、安全、航管等相关业务应用专题数据,并叠 加动态数据,如 AIS/GPS/移动平台数据,逐步建成航运管理处 "一张图"。系统支持扩展框架,方便未来更多应用资源的逐步整合。 现场执法监管系统 基于港口(航管)执法基地建设规划,依托统一的执法区域 管理和数字化监控平台,通过加强对辖区内的监控,结合移动平 台,形成完整的多维路径和信息追踪,真正做到问题能发现、事态能控制、突发问题能解决。 运行监测和辅助决策系统 对区域港口与航运业务日常所需填报及监测的数据经过科 学归纳及分析,采用统一平台,消除重复的填报数据,进行企业 输入和自动录入,并进行系统智能判断,避免填入错误的数据, 输入的数据经过智能组合,自动生成各业务部门所需的数据报 表,包括字段、格式,都可以根据需要进行定制,同时满足扩展 性需要,当有新的业务监测数据表需要产生时,系统将分析新的 需求,将所需字段融合进入日常监测和决策辅助平台的统一平台中,并生成新的所需业务数据监测及决策表。 综合指挥调度系统 建设以港航应急指挥中心为枢纽,以各级管理部门和经营港 口企业为节点,快速调度、信息共享的通信网络,满足应急处置中所需要的信息采集、指挥调度和过程监控等通信保障任务。 设计思路 根据项目的建设目标和“智慧港口”信息化平台的总体框架、 设计思路、建设内容及保障措施,围绕业务协同、信息共享,充 分考虑各航运(港政)管理处内部管理的需求,平台采用“全面 整合、重点补充、突出共享、逐步完善”策略,加强重点区域或 运输通道交通基础设施、运载装备、运行环境的监测监控,完善 运行协调、应急处置通信手段,促进跨区域、跨部门信息共享和业务协同。 以“统筹协调、综合监管”为目标,以提供综合、动态、实 时、准确、实用的安全畅通和应急数据共享为核心,围绕“保畅通、抓安全、促应急"等实际需求来建设智慧港口信息化平台。 系统充分整合和利用航运管理处现有相关信息资源,以地理 信息技术、网络视频技术、互联网技术、移动通信技术、云计算 技术为支撑,结合航运管理处专网与行业数据交换平台,构建航 运管理处与各部门之间智慧、畅通、安全、高效、绿色低碳的智 慧港口信息化平台。 系统充分考虑航运管理处安全法规及安全职责今后的变化 与发展趋势,应用目前主的、成熟的应用技术,内联外引,优势互补,使系统建设具备良好的开放性、扩展性、可维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值