vsasv
vsasv

Reputation: 870

TCP not writing full file?

I am trying to write a text file to multiple sockets, using one program. The code used to write the text file over is as follows:

import java.io.*;
import java.net.*;

public class Server {
    public static void main(String args[]) throws Exception{
        String servers[] = {"127.0.0.1","127.0.0.1","127.0.0.1"};
        int[] ports = {4525, 4003,3621};
        String fileName = "example";

        for(int i = 0; i < servers.length; i++){
            getFile(fileName, InetAddress.getByName(servers[i]), ports[i], "1");
        }
    }
    public static void getFile(String fileName, InetAddress ia, int port, String vers) throws Exception{
        Socket myServers = new Socket(ia, port);
        PrintWriter pwSoc = new PrintWriter(myServers.getOutputStream(), true);
        pwSoc.println(fileName + " " + "write " + vers);
        InputStream is = null;
        int i =0;
        while(i < 20000){
            i++;
        }
        int c;
        try{
            is = new FileInputStream(fileName + ".txt");
        }
        catch(Exception e){
            System.out.println("Error. This file is not found"); 
            return;
        }
        while ((c = is.read()) != -1) {
            pwSoc.println((char)c); 
            pwSoc.flush();
        }
        pwSoc.close();
        is.close();
        pwSoc.close();
        return;
    }
}

Then, on the server side (for all three servers), I use the following thread, which evaluates to the else if(req.equals("write")){ code block:

import java.io.*;
import java.net.*;
import java.util.StringTokenizer;
public class ServerRequestThread implements Runnable{ //Server 1
    Socket client;
    File[] files;
    VersionInfo vi;
    public ServerRequestThread(Socket s, VersionInfo vi) throws Exception{
        this.client = s;
        String filePath = new File(".").getCanonicalPath();
        this.files = new File(filePath).listFiles();
        this.vi = vi;
    }

    public void run() {
        String req = "", reversedString = "";
        try {
            while(true){
                InputStream in = client.getInputStream();
                BufferedReader bin = new BufferedReader(new InputStreamReader(in));
                String ex = bin.readLine();
                StringTokenizer st = new StringTokenizer(ex, " ");
                String fileName = st.nextToken();
                if(fileName.equals("done")){
                    return;
                }
                else if(fileName.equals("fileList")){
                    String returnList = showFiles(files).trim();
                    PrintWriter pout = new PrintWriter(client.getOutputStream(), true);
                    pout.println(returnList);
                    pout.close();
                }
                else if(fileName.equals("filecatchup")){
                    PrintWriter pout = new PrintWriter(client.getOutputStream(), true);
                    pout.println(vi.printHash());
                    pout.close();
                }
                else{
                    req = st.nextToken();
                }
                if(req.equals("read")){
                    PrintWriter pout = new PrintWriter(client.getOutputStream(), true);
                    int c;
                    InputStream is = null;
                    try{
                        is = new FileInputStream(fileName + ".txt");
                    }
                    catch(Exception e){
                        pout.println("File could not be found");
                        return;
                    }
                    while ((c = is.read()) != -1) {
                        pout.println((char)c); 
                    }
                    pout.close();
                    return;
                }
                else if(req.equals("write")){
                    if(st.hasMoreTokens()){
                        String vers = st.nextToken();
                        vi.updateHash(fileName, Integer.parseInt(vers));
                        System.out.println("The file " + fileName + " was updated  (via remote update) to v." + vers);
                    }
                    else{
                        vi.updateFile(fileName);
                        System.out.println("The file " + fileName + " was updated to v." + vi.getFile(fileName));
                    }

                    InputStream in2 = client.getInputStream();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in2));
                    reversedString = reader.readLine();


                    PrintWriter out = new PrintWriter(fileName + ".txt");
                    while(reversedString != null){
                        //cont+=reversedString;
                        out.print(reversedString);
                        reversedString = reader.readLine();
                    }
                    reader.close();
                    out.close();
                    return;
                }
            }
        } catch (Exception e) {}
    }
    public String showFiles(File[] files) throws Exception {
        String ret = "";
        //System.out.println("Path: " + new File(".").getCanonicalPath());
        for (File file : files) {
            if (file.isDirectory()) {
                //System.out.println("Directory: " + file.getName());
                showFiles(file.listFiles()); // Calls same method again.
            } else { 
                if(file.getName().toLowerCase().endsWith(".txt")){
                     ret+= " "  + file.getName();
                }
            } 
        } 
        return ret;
    } 

}

For some reason, when I write, the entire file does not go through to the other side. Rather, I miss somewhere around the first 100 bytes of the file. For example, if I write the Lorem Ipsum text, I will be missing somewhere around the first sentence.

Does anyone know why this is happening? I tried nearly everything, but I cannot seem to fix the issue

Upvotes: 0

Views: 61

Answers (2)

Steffen Ullrich
Steffen Ullrich

Reputation: 123280

You first create a BufferedReader bin and use it to read a single line for the command. But, this does not mean that only this line is read from the input stream. Instead more bytes are read but the BufferedReader bin only returns the first line of these bytes, the rest is kept inside the buffer of the BufferedReader (that's why the name!).

Later you try to read the payload and instead of using the reader bin you already have you create a new one reader and read from this. This way you ignore any bytes already buffered in bin - which means that you are missing bytes.

Upvotes: 2

Sergei Tachenov
Sergei Tachenov

Reputation: 24869

Your code has very many problems. It's of very poor quality, and you shouldn't be surprised that it doesn't work correctly. I would recommend to read a good beginner's book about Java and something about OOP in general. To name just a few problems:

  • You don't use try/finally/try-with-resources, which means that your streams may remain open forever if an exception occurs.
  • You don't seem to distinguish between characters and bytes, which is a major source of neverending troubles. In your case, you read bytes and then write them as characters. Take a UTF-8 file with non-ASCII characters and you'll break them instantly (literally, into bytes).
  • You abuse PrintWriter which is a very inefficient way of outputting data, and worse, you use println to print each byte on a single line for whatever reason.
  • You use magic code that has no well-defined purpose, like while (i < 20000) ++i;.

Now to your question. The effect you see is because you don't read your data in a consistent way on the server side. Instead of encapsulating your protocol into a class you randomly create various helper objects to read your data. Look at this, for example:

            InputStream in = client.getInputStream();
            BufferedReader bin = new BufferedReader(new InputStreamReader(in));
            String ex = bin.readLine();

What did you just do here? You created a buffered reader, which then (surprisingly) buffered some data to read a line. Of course it buffered more than just one line, that's how buffering works. At this point you should have forgotten about the in object and only used bin to read. That would be consistent. In fact, I'd write these lines as

            BufferedReader bin = new BufferedReader(new InputStreamReader(
                                                        client.getInputStream()));
            String ex = bin.readLine();

Just to be safe. But then you do

                InputStream in2 = client.getInputStream();
                BufferedReader reader = new BufferedReader(new InputStreamReader(in2));

That is, you access the same stream directly, bypassing the previously created buffered reader. Of course the new reader picks up where the last reader stopped, which can be anywhere depending on the buffer size, buffering strategy and how many data was read by the previous reader.

To fix this:

  • Encapsulate your protocol in a separate class or maybe classes.
  • Read your data consistently, using one buffered reader.
  • Do not use PrintWriter unless you're actually printing something for someone to read through a pipe or tail.
  • Specify encoding explicitly to the reader. Pick one and stick to it. UTF-8 is a good choice for network protocols.
  • Put close() calls in finally blocks or use try-with-resources.

Upvotes: 1

Related Questions