一、背景
写了一个使用 InputStream 接收 Socket 字节流的 demo ,发现只要 socket 不关闭的情况下读这个InputStream 会一直 block。如果是读的是 FileInputStream,读完文件却会立刻返回。
二、原理分析
行为的差异主要来自 InputStream 。Socket 的 InputStream 在 tcp 链接断开后(收到 tcp Fin 请求)会给 InputStream 一个信号代表所有数据已经读完了,连接已经断开不可能再拿到任何数据了。这时通过 InputStream 进行读取会直接返回 -1 或 null,不会继续 block 了。如果没有收到 Fin 请求,读取 InputStream 会一直 block 。这点上和 FileInputStream 就不同了,FileInputStream 如果读到了文件结尾得到 EOF 就会返回,即便后续再有数据写入文件也拿不到了,只能重新读一次。
三、代码验证
同时读写文件:两个线程分别读写一个文件,虽然写线程一直在写,但是读线程很快就返回了,不会等读线程写完。
package com.zhuo.io;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedInputStreamTest {
private static final String fileName = "/Users/lc/mine/b.txt";
public static class WriteThread implements Runnable {
public void run() {
try {
FileOutputStream fileOutputStream = new FileOutputStream(fileName);
int i = 0;
while (i++ < 10) {
fileOutputStream.write(("hello" + i).getBytes());
Thread.sleep(1);
}
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("write thread ends...");
}
}
public static class ReadThread implements Runnable {
public void run() {
try {
BufferedInputStream bufferedInputStream =
new BufferedInputStream(new FileInputStream(fileName));
int c;
while ((c = bufferedInputStream.read()) != -1) {
System.out.print((char) c);
}
System.out.println();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("read thread ends...");
}
}
public static void main(String[] args) throws Exception {
new Thread(new WriteThread()).start();
new Thread(new ReadThread()).start();
}
}
读socket:在socket不关闭的情况下,即便client不发送数据,server侧socket的inputStream读取始终是block的。
package com.lc;
import static com.lc.Constants.PORT;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class SocketClient {
public static class SocketThread implements Runnable {
public SocketThread(Inet4Address address, int port) {
try {
this.socket = new Socket(address, port);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private Socket socket;
public void run() {
try {
int i = 0;
while (i++ < 100) {
socket.getOutputStream().write(("hello" + i + "\n").getBytes());
Thread.sleep(1000);
}
} catch (Exception e) {
throw new IllegalStateException();
}
}
}
public static void main(String[] args) {
byte[] ip = {127, 0, 0, 1};
try {
SocketThread socketThread = new SocketThread((Inet4Address) InetAddress.getByAddress("local", ip), 34456);
new Thread(socketThread).start();
} catch (UnknownHostException unknownHostException) {
throw new RuntimeException(unknownHostException);
}
}
}
package com.lc;
import static com.lc.Constants.PORT;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketServer {
public static class SocketThread implements Runnable {
public SocketThread(int port) {
try {
this.serverSocket = new ServerSocket(port);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private ServerSocket serverSocket;
public void run() {
try {
Socket socket = serverSocket.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
throw new IllegalStateException();
}
}
public static void main(String[] args) {
new Thread(new SocketThread(34456)).start();
}
}
}
网友评论