InputStream
和InputStreamReader
是Java I/O类库中的两个关键类,用于处理字节流。它们的主要区别在于它们处理数据的方式。
InputStream
是用于读取字节流的抽象类。它是所有字节输入流类的父类。InputStream
的子类可以从不同的数据源读取字节,例如文件、网络连接、内存等。read()
,用于读取单个字节,以及read(byte[] b)
,用于读取一组字节。InputStream inputStream = new FileInputStream("example.txt");
int data = inputStream.read(); // 读取单个字节
byte[] buffer = new byte[1024];
int bytesRead = inputStream.read(buffer); // 读取一组字节
FileInputStream
是 InputStream
类的一种具体实现,它专门用于从文件中读取字节。下面是 FileInputStream
与 InputStream
的主要区别:
特定用途:
InputStream
是一个抽象类,用于表示字节输入流的基本接口,但它本身不能直接实例化。它有多个具体的实现类,包括 FileInputStream
。(所以InputStream inputStream = new FileInputStream("example.txt");是多态的体现)FileInputStream
是 InputStream
的一个具体实现,它通过文件系统读取字节流。用法:
InputStream
可以用于从各种来源读取字节,例如文件、网络连接、字节数组等。FileInputStream
专门用于从文件中读取字节。你需要提供文件路径或文件对象作为构造函数的参数。构造方法:
FileInputStream
的构造方法需要一个表示文件路径的字符串或者一个 File
对象。例如:new FileInputStream("example.txt")
或 new FileInputStream(new File("example.txt"))
。InputStream
本身不能直接实例化,因为它是一个抽象类。你需要使用其具体实现类之一来创建对象。异常处理:
FileInputStream
的构造方法和读取方法都可能抛出 IOException
,因此在使用时需要进行异常处理。InputStream
的一些方法也可能抛出 IOException
。InputStreamReader
是Reader
类的子类,它是用于读取字符流的桥梁,将字节流转换为字符流。InputStream
作为参数,将字节流转换为字符流,并提供了字符读取方法,如read()
和read(char[] cbuf)
。InputStreamReader
处理字符的方式是根据指定的字符编码将字节转换为字符。InputStream inputStream = new FileInputStream("example.txt");
Reader reader = new InputStreamReader(inputStream, "UTF-8");
int charData = reader.read(); // 读取单个字符
char[] charBuffer = new char[1024];
int charsRead = reader.read(charBuffer); // 读取一组字符
BufferedReader
是Reader
类的装饰器,用于缓冲字符输入。它提供了缓冲功能,可以一次读取多个字符,以提高读取性能。BufferedReader
通常用于包装其他Reader
,例如FileReader
或InputStreamReader
,以提供缓冲的字符读取。InputStream inputStream = new FileInputStream("example.txt");
Reader reader = new InputStreamReader(inputStream, "UTF-8");
BufferedReader bufferedReader = new BufferedReader(reader);
OutputStreamWriter
是Java I/O类库中的一个类,用于将字符流转换为字节流。它是Writer
类的子类,允许你按字符而不是字节写入数据到输出流中,并且可以指定字符编码。
字符到字节的转换: OutputStreamWriter
将字符数据转换为字节数据,然后将字节写入底层的输出流。
字符编码: 你可以在创建OutputStreamWriter
时指定字符编码。这是非常重要的,特别是在处理文本数据时,因为它影响了字符到字节的映射。
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
public class OutputStreamWriterExample {
public static void main(String[] args) {
try {
// 创建一个字节输出流
FileOutputStream fileOutputStream = new FileOutputStream("output.txt");
// 创建OutputStreamWriter并指定字符编码
Writer writer = new OutputStreamWriter(fileOutputStream, "UTF-8");
// 写入字符数据
writer.write("Hello, OutputStreamWriter!");
// 关闭流
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
之前我们都是通过自己写的client类连接server的,现在我们通过浏览器来连接server。
连接时,在浏览器里输入http://localhost:8080。
当我们从浏览器访问自己写的服务器时,浏览器(也就是客户端)会向服务器发送请求,我们可以通过socket的输入流来接受并打印这些请求的内容:
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
String line;
//如果读到的字符串不为空,就打印。为空,则跳出循环
while(!((line=bufferedReader.readLine()).isEmpty())){
System.out.println(line);
}
line里的内容就是请求的内容:
GET / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,zh-TW;q=0.5
然后我们还可以向客户端返回数据:
OutputStreamWriter writer=new OutputStreamWriter(socket1.getOutputStream());
writer.write("HTTP/1.1 200 Accepted\r\n");
//在响应头写完后一定要再换行才能写我们的响应体(在浏览器上展示的部分)
writer.write("\r\n");
//响应的内容
writer.write("lyjnb");
writer.flush();
socket1.close();
其中,
"HTTP/1.1 200 Accepted\r\n" 是 HTTP 协议中的响应头。 "HTTP/1.1" 表示使用的是 HTTP 1.1 版本。 "200" 是响应状态码,表示请求被成功处理。 "Accepted" 是状态码的描述,表示请求已被接受。 最后的 "\r\n" 是回车和换行符,表示行结束符,HTTP 协议要求在头部信息的每一行末尾使用这个组合。
最后我们得到的效果:
package socket3_browser_to_socket;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String args[]){
try(ServerSocket socket=new ServerSocket(8080)){
System.out.println("waiting for client...");
Socket socket1=socket.accept();
System.out.println("already connected, ip address:"+socket1.getInetAddress().getHostAddress());
InputStream inputStream=socket1.getInputStream();
System.out.println("data received:");
//BufferedReader是Reader类的装饰器,用于缓冲字符输入。它提供了缓冲功能,可以一次读取多个字符,以提高读取性能。
//InputStreamReader是Reader类的子类,它是用于读取字符流的桥梁,将字节流转换为字符流。
//InputStream是用于读取字节流的抽象类。它是所有字节输入流类的父类。
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
String line;
//如果读到的字符串不为空,就打印。为空,则跳出循环
while(!((line=bufferedReader.readLine()).isEmpty())){
System.out.println(line);
}
OutputStreamWriter writer=new OutputStreamWriter(socket1.getOutputStream());
//"HTTP/1.1 200 Accepted\r\n" 是 HTTP 协议中的响应头。
//"HTTP/1.1" 表示使用的是 HTTP 1.1 版本。
//"200" 是响应状态码,表示请求被成功处理。
//"Accepted" 是状态码的描述,表示请求已被接受。
//最后的 "\r\n" 是回车和换行符,表示行结束符,HTTP 协议要求在头部信息的每一行末尾使用这个组合。
writer.write("HTTP/1.1 200 Accepted\r\n");
//在响应头写完后一定要再换行才能写我们的响应体(在浏览器上展示的部分)
writer.write("\r\n");
//响应的内容
writer.write("lyjnb");
writer.flush();
socket1.close();
}catch (Exception e){
e.printStackTrace();
}
}
}