本周受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。