前言:再写一个关于客户端上传图片到服务端的案例时遇到的问题
当客户端发完数据,如果不用socket.shutdownOutput(),或者不使用人为标记,去结束循序
那么服务端可能还会是默认等待接收客户端数据的状态,会发生阻塞状态。
案例:
-
编写一个服务器端和客户端
-
服务器端在10087端口监听
-
客户端连接到服务器端,发送要上传的图片 (字节)
-
服务器端接收到客户端发送的数据,上传到本地文件 ,然后向客户端发送上传完毕,最后退出
-
客户端接收到服务端发送的信息,输出信息 ,然后退出
代码一:不使用socket.shutdownOutput()的情况
服务端:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class UpLoadServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10087);
Socket s = ss.accept();
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\upload\\2.jpg"));
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1){
bos.write(bys,0,len);
}
System.out.println(len);
//当Client停止发送时,Server还在接收Client的状态
BufferedOutputStream bosServer = new BufferedOutputStream(s.getOutputStream());
bosServer.write("上传完毕!".getBytes());
bosServer.close();
bos.close();
bis.close();
s.close();
}
}
客户端:
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class UpLoadClient {
public static void main(String[] args) throws IOException {
Socket s = new Socket(InetAddress.getLocalHost(),10087);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src\\upload\\1.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1){
bos.write(bys,0,len);
}
BufferedInputStream bisClient = new BufferedInputStream(s.getInputStream());//阻塞Server发送
byte[] bytes = new byte[1024];
int read = bisClient.read(bytes);
System.out.println(new String(bytes,0,read));
bis.close();
s.close();
}
}
运行结果:图片上传了,但是
客户端和服务端处于阻塞状态
问题分析
由于结束循环的条件时 len==-1 时,这里我们把len打印出来,判断具体是那一步阻塞了
服务端
int len = 0;
while ((len = bis.read(bys)) != -1){
bos.write(bys,0,len);
System.out.println(len);
}
System.out.println(len);
客户端:客户端传输完文件,len一定会等于1
int len = 0;
while ((len = bis.read(bys)) != -1){
bos.write(bys,0,len);
}
System.out.println(len);
运行结果:
这里可以发现:服务端并没有结束循序,客户端结束时,len没有等于-1
因为客户端要接收服务端发来的信息,而服务端还处于接收客户端消息的状态,
两者在互相等待
代码二:使用socket.shutdownOutput()的情况,并继续打印len
服务端:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class UpLoadServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10087);
Socket s = ss.accept();
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\upload\\2.jpg"));
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1){
bos.write(bys,0,len);
System.out.println(len);
}
System.out.println(len);
//当Client停止发送时,Server还在接收Client的状态
BufferedOutputStream bosServer = new BufferedOutputStream(s.getOutputStream());
bosServer.write("上传完毕!".getBytes());
bosServer.close();
bos.close();
bis.close();
s.close();
}
}
客户端:
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class UpLoadClient {
public static void main(String[] args) throws IOException {
Socket s = new Socket(InetAddress.getLocalHost(),10087);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src\\upload\\1.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1){
bos.write(bys,0,len);
}
s.shutdownOutput();// 返回给Server一个-1
System.out.println(len);
BufferedInputStream bisClient = new BufferedInputStream(s.getInputStream());//阻塞Server发送
byte[] bytes = new byte[1024];
int read = bisClient.read(bytes);
System.out.println(new String(bytes,0,read));
bis.close();
s.close();
}
}
运行结果:
从这里可以发现,使用socket.shutdownOutput()让结束了服务端一直处于循环的状态,结束了两者一直在阻塞的状态,其中可以看出关键就是这个-1,使用socket.shutdownOutput()后,服务端接收到了一个数据-1
注意点
并且这与socket.close()同理,如果客户端不接收服务端发来的信息,而是循环完后直接socket.close(),服务端也会结束并且最后打印出来的len值为-1
总结
调用Socket.shutdownOutput()后,禁用此套接字的输出流,对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列(即-1),之后,从另一端TCP套接字的输入流中读取数据时,如果到达输入流末尾而不再有数据可用,则返回 -1。
简单理解就是告诉服务端,我没有数据要传了