Java程序中截获控制台输出

  本文的目标是设计一个基于Swing的JTextArea显示控制台输出。此期间,我们还将讨论一些和Java管道流(PipedInputStream和PipedOutputStream)有关的注意事项。最后还要创建一个能够捕获和显示其他程序(可以是非Java的程序)控制台输出的简单程序。

 

一、Java管道流

 

     要在文本框中显示控制台输出,我们必须用某种方法“截取”控制台流。换句话说,我们要有一种高效地读取写入到System.out和System.err所有内容的方法。Java的管道流PipedInputStream和PipedOutputStream是一个非常有效的工具。

     写入到PipedOutputStream输出流的数据可以从对应的PipedInputStream输入流读取。Java的管道流极大地方便了我们截取控制台输出。Listing 1显示了一种非常简单的截取控制台输出方案。 

 

【Listing 1:用管道流截取控制台输出】
 

  1. PipedInputStream pipedIS = new PipedInputStream();     
  2. PipedOutputStream pipedOS = new PipedOutputStream();     
  3. try {     
  4.   pipedOS.connect(pipedIS);     
  5. }     
  6. catch(IOException e) {     
  7.   System.err.println("连接失败");     
  8.   System.exit(1);     
  9. }     
  10. PrintStream ps = new PrintStream(pipedOS);     
  11. System.setOut(ps);     
  12. System.setErr(ps);  
 

     这里我们只是建立了一个PipedInputStream,把它设置为所有写入控制台流的数据的最终目的地。所有写入到控制台流的数据都被转到PipedOutputStream,这样,从相应的PipedInputStream读取就可以迅速地截获所有写入控制台流的数据。接下来的事情似乎只剩下在Swing JTextArea中显示从pipedIS流读取的数据,得到一个能够在文本框中显示控制台输出的程序。遗憾的是,在使用Java管道流时有一些重要的注意事项。只有认真对待所有这些注意事项才能保证Listing 1的代码稳定地运行。下面我们来看第一个注意事项。

 

1.1 注意事项一

  PipedInputStream运用的是一个1024字节固定大小的循环缓冲区。写入PipedOutputStream的数据实际上保存到对应的PipedInputStream的内部缓冲区。从PipedInputStream执行读操作时,读取的数据实际上来自这个内部缓冲区。如果对应的PipedInputStream输入缓冲区已满,任何企图写入PipedOutputStream的线程都将被阻塞。而且这个写操作线程将一直阻塞,直至出现读取PipedInputStream的操作从缓冲区删除数据。

  这意味着,向PipedOutputStream写数据的线程不应该是负责从对应PipedInputStream读取数据的唯一线程。从图1可以清楚地看出这里的问题所在:假设线程t是负责从PipedInputStream读取数据的唯一线程;另外,假定t企图在一次对PipedOutputStream的write()方法的调用中向对应的PipedOutputStream写入2000字节的数据。在t线程阻塞之前,它最多能够写入1024字节的数据(PipedInputStream内部缓冲区的大小)。然而,一旦t被阻塞,读取PipedInputStream的操作就再也不会出现,因为t是唯一读取PipedInputStream的线程。这样,t线程已经完全被阻塞,同时,所有其他试图向PipedOutputStream写入数据的线程也将遇到同样的情形。

                                       

                                                               图1:管道流工作过程

 

  这并不意味着在一次write()调用中不能写入多于1024字节的数据。但应当保证,在写入数据的同时,有另一个线程从PipedInputStream读取数据。

  Listing 2示范了这个问题。这个程序用一个线程交替地读取PipedInputStream和写入PipedOutputStream。每次调用write()向PipedInputStream的缓冲区写入20字节,每次调用read()只从缓冲区读取并删除10个字节。内部缓冲区最终会被写满,导致写操作阻塞。由于我们用同一个线程执行读、写操作,一旦写操作被阻塞,就不能再从PipedInputStream读取数据。

 

【Listing 2:用同一个线程执行读/写操作导致线程阻塞】
 

  1. import java.io.*;  
  2.   
  3. //用同一个线程执行读/写操作导致线程阻塞】  
  4. public class PipedStreamWithOneThread {  
  5.     static PipedInputStream  pipedIS = new PipedInputStream();  
  6.     static PipedOutputStream pipedOS = new PipedOutputStream();  
  7.     public static void main(String[] a){  
  8.         try {  
  9.             pipedIS.connect(pipedOS);  
  10.         }catch(IOException e) {  
  11.             System.err.println("连接失败");  
  12.             System.exit(1);  
  13.         }  
  14.         byte[] inArray = new byte[10];  
  15.         byte[] outArray = new byte[20];  
  16.         int bytesRead = 0;  
  17.         try {  
  18.             pipedOS.write(outArray, 020);  // 向pipedOS发送20字节数据  
  19.             System.out.println("   已发送20字节...");  
  20. //           在每一次循环迭代中,读入10字节// 发送20字节  
  21.             bytesRead = pipedIS.read(inArray, 010);   
  22.             System.out.println("   已读取10字节...");  
  23.             System.out.println("   10");  
  24.             int i=0;  
  25.             while(bytesRead != -1) {  
  26.                 pipedOS.write(outArray, 020);  
  27.                 System.out.println("   已发送20字节..."+i);  
  28.                 i++;  
  29.                 bytesRead = pipedIS.read(inArray, 010);  
  30.                 System.out.println("   已读取10字节...");  
  31.                 System.out.println(10+10*i);  
  32.             }  
  33.         }catch(IOException e) {  
  34.             System.err.println("读取pipedIS时出现错误: " + e);  
  35.             System.exit(1);  
  36.             }  
  37.         }  
  38.     }  

     只要把读/写操作分开到不同的线程,Listing 2的问题就可以轻松地解决。

     Listing 3是Listing 2经过修改后的版本,它在一个单独的线程中执行写入PipedOutputStream的操作(和读取线程不同的线程)。为证明一次写入的数据可以超过1024字节,我们让写操作线程每次调用PipedOutputStream的write()方法时写入2000字节。那么,在startWriterThread()方法中创建的线程是否会阻塞呢?按照Java运行时线程调度机制,它当然会阻塞。写操作在阻塞之前实际上最多只能写入1024字节的有效载荷(即PipedInputStream缓冲区的大小)。但这并不会成为问题,因为主线程(main)很快就会从PipedInputStream的循环缓冲区读取数据,空出缓冲区空间。最终,写操作线程会从上一次中止的地方重新开始,写入2000字节有效载荷中的剩余部分。

 

【Listing 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux C语言和Java程序可以通过HTTP请求进行通信。而为了保证通信的安全和加密性,我们通常会使用HTTPS请求。那么这个过程该如何实现呢? 首先,我们需要理解HTTP请求和HTTPS请求的基本原理。HTTP协议是基于明文传输的,容易被恶意攻击者截获数据包,窃取敏感信息。为了避免这种情况的发生,HTTPS协议在HTTP上添加了SSL/TLS协议,将数据进行加密传输,保障了数据的安全性。因此,使用HTTPS请求进行通信是非常重要的。 在Linux C语言,我们可以使用第三方库openssl来实现HTTPS请求的功能。具体实现方式为: 1. 导入openssl库,并在代码初始化相关的参数,如证书路径、私钥等。 2. 创建一个HTTPS连接,并指定服务器地址和端口号。 3. 设置连接的参数和加密类型,以确保连接的安全性。 4. 发送请求以及相关的参数。 5. 接收响应,并对响应进行处理和解析。 在Java,则可以使用HttpURLConnection或HttpClient等内置的库进行HTTP/HTTPS请求的实现。具体实现方式为: 1. 创建URL对象,并设置请求的目标地址。 2. 打开连接,并设置请求的相关参数,如请求方式、请求参数、请求头等。 3. 发送请求,并接收响应。对响应进行解析和处理,可采用JSON、XML等格式。 总之,无论是Linux C语言还是Java程序,都可以通过HTTPS请求进行通信,并保障数据的安全性和加密性。相比起HTTP,HTTPS是一种更为安全和可靠的通信协议,可以更好地满足商业和技术的需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值