关于Socket流中read()的阻塞问题
在我学习Tcp的时候我遇到了一个问题就是关于socket的阻塞问题,具体代码是这样的
服务端:
public class MyServer {
public static void main(String[] args) throws IOException {
try (
ServerSocket serverSocket = new ServerSocket(8281);
Socket socket = serverSocket.accept();
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
FileOutputStream fos = new FileOutputStream("copy.png")
) {
int len;
byte[] bytes = new byte[1024];
//接收图片数据
while ((len = is.read(bytes)) != -1) {
System.out.println(len);
fos.write(bytes, 0, len);
}
//向客户端返回接收成功
System.out.println("接收成功");
os.write("传输成功".getBytes());
}
}
}
客户端:
public class MyClient {
public static void main(String[] args) throws IOException {
try(
Socket socket = new Socket("127.0.0.1",8281);
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
FileInputStream fs = new FileInputStream("D:\\WorkSpace\\core_java\\img.png")
){
//发送数据到服务端
int len;
byte[] bytes = new byte[102400];
while ((len = fs.read(bytes))!=-1){
os.write(bytes,0,len);
}
//接收服务端的返回信息
String line =br.readLine();
System.out.println(line);
}
}
}
当运行这个程序的时候,我们发现当接收完了图片之后,服务端并没有接着运行下面给客户端传递接收消息的代码:输出接收成功。
通过调试发现原来一直服务端代码一直阻塞在了 while ((len = is.read(bytes)) != -1)这个地方,“那为什么图片接收到了末尾,不应该返回-1然后退出循环吗,为什么会一直阻塞在这里”。
这里我通过查阅资料给出的解释是,socket里面的流(socket.getInputStream();socket.getOutputStream();)和普通的流(FileOutputStream fos = new FileOutputStream(“copy.png”))是不一样的,普通的流读到末尾时read会返回一个-1,但是socket流并不会,socket流没有结束符。所以read会阻塞等待客户端发送数据。
那么到这里我就有了2个疑问:
1.那怎么解决这个阻塞的问题
2.既然socket流没有结束符那(len = is.read(bytes)) != -1,这里的-1是否可以改为其他任意数字,因为这里的-1似乎没有任何作用。
怎么解决这个阻塞问题
方法1:
当客户端把图片传完后我们显示的调用socket.shutdownOutput();来关闭输出流以告知客户端我的图片资源传完了。
public class MyClient {
public static void main(String[] args) throws IOException {
try(
Socket socket = new Socket("127.0.0.1",8281);
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
FileInputStream fs = new FileInputStream("D:\\WorkSpace\\core_java\\img.png")
){
int len;
byte[] bytes = new byte[102400];
while ((len = fs.read(bytes))!=-1){
os.write(bytes,0,len);
}
socket.shutdownOutput();
String line =br.readLine();
System.out.println(line);
}
}
}
结果:
方法2:
在服务端的接收循环里面加一个判断条件,如果当前的len小于数组bytes的大小,那么则退出循环--------当读到图片的末尾部分时,最后一次读取时len的值要么等于0,要么小于数组bytes的长度。
public class MyServer {
public static void main(String[] args) throws IOException {
try (
ServerSocket serverSocket = new ServerSocket(8281);
Socket socket = serverSocket.accept();
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
FileOutputStream fos = new FileOutputStream("copy.png")
) {
int len;
byte[] bytes = new byte[1024];
while ((len = is.read(bytes)) != -1) {
System.out.println(len);
fos.write(bytes, 0, len);
//判断退出条件
if(len<bytes.length){
break;
}
}
System.out.println("接收成功");
os.write("传输成功".getBytes());
}
}
}
这也是行的通的。
注意事项:
当选用这个方法时,客户端的暂存数组bytes一定要大于等于服务端的暂存数组bytes,因为发送和读取的速度是很快的,如果客户端的数组小于服务端的数组,客户端一发出去,服务端会立马接收:比如客户端数组大小为1,服务端为1024.那么客户端发出一个1的大小数据,服务端就会用这个1024的数组接收,然后这里的len会等于1,然后继续下一个循环又从0开始接收,这样的话我们的len是永远小于1024的,所以行不通。
既然socket流没有结束符那(len = is.read(bytes)) != -1,这里的-1是否可以改为其他任意数字,因为这里的-1似乎没有任何作用:
经过我的测试,如果是用方法二来解决阻塞问题,那么这里的-1确实是可以改成任意数字,但是如果是用第一种方式通过socket.shutdownOutput();来解决阻塞问题时,这里的-1就不能改成其他的数字。他会报错。
由此我推断出当客户端关闭连接时,服务端的read就会读出一个-1,然后通过这个-1来退出服务端的读取循环。
还有就是当客户端代码为以下时,又不会发生阻塞问题:
public class MyClient {
public static void main(String[] args) throws IOException {
try(
Socket socket = new Socket("127.0.0.1",8281);
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
FileInputStream fs = new FileInputStream("D:\\WorkSpace\\core_java\\img.png")
){
int len;
byte[] bytes = new byte[102400];
while ((len = fs.read(bytes))!=-1){
os.write(bytes,0,len);
}
}
}
}
因为当图片数据传完后,这个try的代码块就执行完了,然后会自动关流。关了流,服务端就知道传完了,就不会发生阻塞问题。