本周受Z老师之托,写了一个Java接收文件(本例为图片)的TCP服务端。转载请注明出处。
总共就一个类:Main.java, 几个注意点在代码之后,代码分享如下:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.text.SimpleDateFormat;
import java.util.Date;
/** TCP接收文件例子 */
public class Main {
public static ServerSocket server; //用于接收
public static Socket socket;
public static int port = 9999; //要监听的本地端口
public static void main(String[] args) {
try {
server = new ServerSocket(port);
System.out.println("开始监听");
while(true){
Socket s = server.accept(); //阻塞
new ReceiveThread(s).start();
}
}catch(Exception e) {
e.printStackTrace();
}
}
/** 接收的线程 */
private static class ReceiveThread extends Thread {
private Socket s;
private InputStream socketIs;
private OutputStream fos;
/** 决定本线程是否要继续的标志 */
private boolean isConnected;
public ReceiveThread(Socket s) {
this.s = s;
isConnected = true;
}
@Override
public void run() {
while(isConnected) {
String ip = s.getInetAddress().getHostAddress();
System.out.println("对方是" + ip + " 等待读取");
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmsssss");
String filePath = "E:\\" + simpleDateFormat.format(new Date()) + ".jpg";
try {
socketIs = s.getInputStream();
byte buf[] = new byte[1024];
int len = 0;
while((len = socketIs.read(buf)) != -1){ //阻塞
if(fos == null) { fos = new FileOutputStream(filePath); }
fos.write(buf, 0, len);
//怎样判断文件接收完成?
//1. 如果对方断开连接,本循环自动退出
//2. 如果对方保持连接,用超时进行判断
s.setSoTimeout(500); //设置500ms超时
}
System.out.println("【情况 1】连接断开,传输结束");
isConnected = false; //此处是正常断开,要结束本线程
if(fos != null) { fos.close(); }
socketIs.close();
s.close();
} catch (SocketTimeoutException eTimeout) {
try {
/*向对方返回消息*/
OutputStream out = s.getOutputStream();
out.write( "接收完成".getBytes() );
s.setSoTimeout(0); //重新设回不超时
fos.close(); //关闭文件输出流,生成正常大小的文件
fos = null; //重新置null,防止目录中生成一个大小为0KB的文件
System.out.println("【情况2】超时,本次传输结束");
}catch(IOException eIO) { eIO.printStackTrace(); }
}catch(IOException eIO) { eIO.printStackTrace(); }
}
}
}
}
几个注意点:
1. ServerSocket的accept()方法是阻塞的,直到有新的连接,代码才往后执行。
2. InputStream的read()方法是阻塞的,直到有数据可读,代码才往后执行,读到-1停止。那什么时候才会读到-1呢?只有在对方断开连接(对方主动断开或对方程序出错等情况)的时候才会读到-1。也就是说,如果对方发了文件,但保持连接,这时服务端的read()方法就会一直阻塞,导致不知道文件是否已经接收完成,这种情况下,就只能用sokcet的超时属性进行判断,超过一定时限之后捕获TimoutException从而判定文件传输结束,开始下一次read()阻塞。
3. 只有在输出流关闭之后(调用close()方法)之后文件才会被完整写入磁盘,否则文件为0KB。