TCP传输文件时附加文件名的问题

suneony 发布于 2017/02/16 22:13
阅读 301
收藏 0

最近在学习Socket相关内容,遇到一个问题:采用TCP同时传输文件内容与文件名时,客户端接收到的文件与服务器端发送的文件不一致。

1.当不传输文件名直接传输文件时,传输正常。服务器端和客户端的代码如下:

服务器端:

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket server=new ServerSocket(5050);
        OutputStream socketOutputStream=null;
        FileInputStream fileInputStream=null;

        while(true){
            Socket client=server.accept();
            //socket 输出流
            socketOutputStream=client.getOutputStream();
            //文件流
            fileInputStream=new FileInputStream("file.in");
            //缓冲区
            byte[] buff=new byte[1024];
            int len=0;
            while((len=fileInputStream.read(buff))!=-1){
                socketOutputStream.write(buff,0,len);
                socketOutputStream.flush();
            }

            socketOutputStream.close();
        }
    }
}

客户端:

public class Client {
    public static void main(String[] args) throws IOException {
        Socket client=new Socket("127.0.0.1",5050);
        InputStream socketInputStream=null;
        FileOutputStream fileOutputStream=null;
        //socket输入流
        socketInputStream=client.getInputStream();
        //文件输出流
        fileOutputStream=new FileOutputStream("file.out");

        byte[] buff=new byte[1024];
        int buffLen=0;
        while((buffLen=socketInputStream.read(buff))!=-1){
            fileOutputStream.write(buff,0,buffLen);
            fileOutputStream.flush();
        }
        fileOutputStream.close();
    }
}

此时,服务器端发送的文件和客户端接收到的文件一致(均为:16712789byte,且内容相同)。

2.先传输文件名再传输文件内容时,客户端接收到的文件与服务器端发送的文件不一致。程序如下:

服务器端:

/**
 * Created by suneony on 2/15/17.
 */
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket server=new ServerSocket(5050);
        OutputStream socketOutputStream=null;
        BufferedWriter socketBufferedWriter=null;
        FileInputStream fileInputStream=null;

        while(true){
            Socket client=server.accept();
            //socket 输出流
            socketOutputStream=client.getOutputStream();
            

            //首先传输文件名
            socketBufferedWriter=new BufferedWriter(new OutputStreamWriter(socketOutputStream,"UTF-8"));
            socketBufferedWriter.write("this is file name");
            socketBufferedWriter.newLine();
            socketBufferedWriter.flush();
            

            //开始传输文件内容
            //文件流
            fileInputStream=new FileInputStream("file.in");    
             //缓冲区
            byte[] buff=new byte[1024];
            int len=0;
            while((len=fileInputStream.read(buff))!=-1){
                socketOutputStream.write(buff,0,len);
                socketOutputStream.flush();
            }
            socketOutputStream.close();
        }
    }
}

客户端:

/**
 * Created by suneony on 2/15/17.
 */
public class Client {
    public static void main(String[] args) throws IOException {
        Socket client=new Socket("127.0.0.1",5050);
        InputStream socketInputStream=null;
        FileOutputStream fileOutputStream=null;
        //用于读取文件名
        BufferedReader socketBufferedReader=null;
        //socket输入流
        socketInputStream=client.getInputStream();
        //用于读取文件名
        socketBufferedReader=new BufferedReader(new InputStreamReader(socketInputStream,"UTF-8"));
        String receivedFileName=socketBufferedReader.readLine();
        System.out.println(receivedFileName);
        //文件输出流
        fileOutputStream=new FileOutputStream("file.out");
        //缓冲区
        byte[] buff=new byte[1024];
        int buffLen=0;
        while((buffLen=socketInputStream.read(buff))!=-1){
            fileOutputStream.write(buff,0,buffLen);
            fileOutputStream.flush();
        }
        fileOutputStream.close();
    }
}

此时,客户端能正确接收到文件名,但后续接收到的文件内容与服务器端不一致,文件损坏(客户端仅接收到16696405byte)。

一直没想明白哪里导致的文件内容丢失。

加载中
0
pj220
pj220

你的socketBufferedWriter以socketOutputStream作为底层,那么所有的write操作都要以socketBufferedWriter的write方法进行操作,不要再直接使用底层socketOutputStream的write方法。你使用底层socketOutputStream的write方法,会影响socketBufferedWriter对底层的socketOutputStream管理。
同样socketBufferedReader以socketInputStream作为底层,那么所有的read操作都要以socketBufferedReader的read方法进行操作,不要再直接使用底层socketInputStream的read方法。你使用底层socketInputStream的read方法,会影响socketBufferedReader对底层的socketInputStream管理。
你在服务器端混用这个问题还不突出,但在客户端这么使用问题就很大。
假设开始时socketInputStream里面总共1000字节,你用socketBufferedReader的read方法去读取200字节,有可能socketBufferedReader从socketInputStream读取了255个字节,
它把其中的200个字节返回给你,55个字节缓冲了起来,放到了缓冲区里。
如果你下次再用socketBufferedReader读取120个字节的时候,他把缓冲的55个字节先返回给你,然后再从socketInputStream读取65个字节。
可是这个时候你没有用socketBufferedReader读取120个字节,而是用socketInputStream读取120个字节,那么这个时候就是从偏移量255开始读取120字节。缓存里的55个字节根本就读不到。

结果就是你读取的总字节数比正常的小。

suneony
suneony
哦,豁然开朗。
0
mickelfeng
mickelfeng

你这应该是粘包导致的吧

suneony
suneony
是BufferedReader与InputStream混用导致的。BufferedReader的readline()函数会将换行符后面的部分字节也进行了缓存,再调用InputStream的read()函数时就没法读取到被BufferedReader缓存的部分字节。
返回顶部
顶部