-1

Before marking this as a possible duplicate, please give it a read once as I couldn't find any answers. I am learning Socket programming and creating a very basic server-client file transfer program using sockets. Here's my server module:

import java.io.*;
import java.net.*;
import static java.lang.System.out;

public class ServerMain {
    public static void main(String[] args) {
    int PORT=13267;
    ServerSocket servSocket;
    Socket client;
    File source=new File("E:/Styles of Beyond.wav");;
    BufferedInputStream bis;
    BufferedReader br;
    String s="";
    InputStream is;
    byte[] byteBuffer=new byte[10240];
    OutputStream os;
    int count=0;

    try{

        servSocket=new  ServerSocket(PORT);
        out.println("Server running on port: "+PORT+", waiting for client...");
        client=servSocket.accept();
        client.setKeepAlive(true);
        out.println("Client: "+client.getRemoteSocketAddress()+" connected. Sending file...");
        bis=new BufferedInputStream(new FileInputStream(source));
        os=client.getOutputStream();
        is=client.getInputStream();

        while((count=bis.read(byteBuffer))>0){
            os.write(byteBuffer, 0, count);
        }

        out.println("Waiting for confirmation from client...");
        br=new BufferedReader(new InputStreamReader(is));
        if(br.readLine()=="Done")
            out.println("Done.");
        else
            out.println("Couldn't get confirmation from server.");
    }

    catch(Exception e)
    {
        e.printStackTrace();
    }
}
}

And the client module:

import static java.lang.System.out;
import java.io.*;
import java.net.*;

public class ClientMain {

    public static void main(String[] args) {
        int SERV_PORT=13267;
        Socket server;
        File destination=new File("E:/copy.wav");
        byte[] byteBuffer=new byte[10240];
        InputStream is;
        OutputStream os;
        BufferedWriter bw;
        int count=0;

        try{
            out.println("Tyring to connect to server...");
            server=new Socket("localhost",SERV_PORT);
            out.println("Connected to server: "+server.getRemoteSocketAddress());
            is=server.getInputStream();
            os=server.getOutputStream();
            out.println("Receiving data...");
            BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(destination));

            while((count=is.read(byteBuffer))>0){
                bos.write(byteBuffer, 0, count);
            }
            bw=new BufferedWriter(new OutputStreamWriter(os));
            bw.write("Done");
            bw.newLine();
            out.println("Data successfully written to copy.wav");
        }
        catch(Exception e){
            e.printStackTrace();
        }
}
}

The code works and even the file copy.wav is created properly, but then after writing on os of client, server gets stuck on "Waiting for confirmation from client...", and client is stuck on "Receiving data...". I tried using PrintWriter on client side instead of BufferedWriter, and also simply tried to write a single byte on os, and checking that byte for file received-confirmation on server side, but nothing seems to work, the client is not sending anything. One thing I noticed is that calling is.ready() on server returns false always.

If I remove all the code related to sending and receiving confirmation of file on both client & server, the client program produces SocketException: connection reset, as now the server doesn't wait for the client to complete its is.read() task and closes connection as soon as it completes writing using os.write(), which creates copy.wav with partial data of lesser size than original. How do I do this thing properly?

P.S: I know I haven't closed streams and handled exceptions properly but that doesn't seem to be a problem here.

aayushARM
  • 41
  • 1
  • 10

1 Answers1

2

Flush it!

https://docs.oracle.com/javase/7/docs/api/java/io/OutputStream.html#flush()

after you call:

        bw.write("Done");
        bw.newLine();

The OS is waiting for more so it can efficiently send the data in a big chunk.

Also, you can set TCP_NODELAY on it, but you should generally let the OS do it's thing because it knows better than you the best way to send the data in most circumstances.

How does TCP_NODELAY affect consecutive write() calls?

Community
  • 1
  • 1
xaxxon
  • 19,189
  • 5
  • 50
  • 80
  • Flushing is required so the OS even gets to know about the data in the first place. Not because of the Nagke algorithm. – user207421 Oct 12 '16 at 09:15
  • It still doesn't work, I tried flushing outputstream on both: on server after while loop, and on client after bw.newLine() as you said, still getting stuck on same state :( – aayushARM Oct 12 '16 at 09:18
  • @xaxxon Any help ? – aayushARM Oct 12 '16 at 10:00
  • set the nodelay as well, based on what @EJP said. – xaxxon Oct 12 '16 at 10:02
  • @xaxxon That doesn't work either! One more thing i have figured out by writing out.println() at different points is that although the while loop reaches end, its stuck on last is.read(), control never reaches to very next statement after loop, even though I'm flushing outputstream on server! wtf? – aayushARM Oct 12 '16 at 10:21
  • I really think there's no problem in my code here, its maybe Eclipse or other system related problem... – aayushARM Oct 12 '16 at 10:26
  • why would you expect that while((count=is.read(byteBuffer))>0){ would ever return <= 0? read will block until data is received or the socket is closed. Maybe youw ant to send the expected file size ahead of time so you know when you're done reading the actual file from the socket? – xaxxon Oct 12 '16 at 10:49
  • Here's what official doc says for return type of InputStream.read(byte[] b): – aayushARM Oct 12 '16 at 10:59
  • "Returns: the total number of bytes read into the buffer, or -1 if there is no more data because the end of the stream has been reached." So when stream has no more data, its should return -1 and come out of the loop... – aayushARM Oct 12 '16 at 11:00
  • @xaxxon Neither flushing nor disabling the Nagle algorithm will solve this problem, and I haven't said anything that would support such a conclusion. The problem is at the receiving end. – user207421 Oct 16 '16 at 01:29
  • @aayushARM 'End of stream' on a socket occurs when the peer closes the connection. Not just when 'the stream has no more data'. In that condition he read will just block. – user207421 Oct 16 '16 at 01:30