Java OutputStream only flushes data on close
Asked Answered
F

5

8
Socket socket = new Socket("192.168.178.47", 82);

OutputStream out = socket.getOutputStream();
out.write("{ \"phone\": \"23456789\" }".getBytes());
out.flush();

//Server

InputStream in = client.getInputStream();
ByteArrayOutputStream bOut = new ByteArrayOutputStream();

int i = 0;
while((i = in.read()) >= 0) {
    bOut.write(i);
}

String complete = new String(bOut.toByteArray(), "UTF-8");

I had tried to send data via OutputStream to a socket but the data is not flushing. If I add an out.close(); to the end then it works perfectly, but the socket is closed and I cannot accept the response. Does anybody know why? The server is not giving any type of error. I had used Java 1.7!

Fathead answered 20/4, 2015 at 14:29 Comment(4)
What does the server code look like?Excelsior
How did you confirm that the output is not sent?Vaules
Well, you know, an OutputStream has .flush(). Also, the way your write your JSON is broken: it uses the JRE's default encoding. Why don't you use a JSON library instead?Cordiality
A socket OutputStream doesn't flush at all. flush() does nothing, whenever you call it. The problem is that you are reading until EOS but the sender is never closing the stream.Illyricum
P
7

It is possible that the server is waiting for the end of line. If this is the case add "\n" to the text

Preventer answered 20/4, 2015 at 14:32 Comment(1)
This was the exact thing my program was doing. Thanks for the tip!Nieman
O
4

I'm not sure of the labelling "//Server" in your question, but I'm assuming the following code is the server code:

InputStream in = client.getInputStream();
ByteArrayOutputStream bOut = new ByteArrayOutputStream();

int i = 0;
while((i = in.read()) >= 0) {
    bOut.write(i);
}

String complete = new String(bOut.toByteArray(), "UTF-8");

This will continue to read, blocking each time, until it gets a value from read() less than zero. That only happens if the stream is closed.

It really looks like you need to establish your own protocol. So instead of looking for "<=0" look for some constant value that signals the end of the message.

Here's a quick demonstration of what I mean (I didn't have time yesterday). I have 3 classes, Message,MyClient (which also is the main class), and MyServer. Notice there isn't anything about sending or receiving a newline. Nothing is setting tcpNoDelay. But it works fine. Some other notes:

  • This code only sends and receives a single request and response.
  • It doesn't support sending multiple Message instances. That would require checking for the start of a Message as well as the end.

Message class:

public class Message {
    public static final String MSG_START = "<message>";
    public static final String MSG_END = "</message>";

    private final String content;

    public Message(String string){
        content = string;
    }

    @Override
    public String toString(){
        return MSG_START + content + MSG_END;
    }
}

MyServer class

public class MyServer implements Runnable{
    public static final int PORT = 55555;

    @Override
    public void run(){
        try {
            ServerSocket serverSocket = new ServerSocket(PORT);
            Socket socket = serverSocket.accept();
            String message = getMessage(socket);
            System.out.println("Server got the message:  " + message);
            sendResponse(socket);
        }catch (IOException e){
            throw new IllegalStateException(e);
        }
    }

    private void sendResponse(Socket socket) throws IOException{
        Message message = new Message("Ack");
        System.out.println("Server now sending a response to the client: " + message);
        OutputStream out = socket.getOutputStream();
        out.write(message.toString().getBytes("UTF-8"));
    }

    private String getMessage(Socket socket) throws IOException{

        BufferedInputStream in = new BufferedInputStream(socket.getInputStream());
        StringBuilder sb = new StringBuilder(100);
        byte[] bytes = new byte[1024<<8];
        while(sb.lastIndexOf(Message.MSG_END) == -1){
            int bytesRead = in.read(bytes);
            sb.append(new String(bytes,0,bytesRead,"UTF-8"));
        }

        return sb.toString();
    }
}

MyClient class

public class MyClient {

    public static void main(String[] args){
        MyClient client = new MyClient();

        Thread server = new Thread(new MyServer());
        server.start();

        client.performCall();
    }

    public void performCall(){
        try {
            Socket socket = new Socket("127.0.0.1",MyServer.PORT);
            sendMessage(socket, "Why hello there!");
            System.out.println("Client got a response from the server: " + getResponse(socket));
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }

    public String getResponse(Socket socket) throws IOException{
        String response;

        StringBuilder sb = new StringBuilder(100);
        InputStream in = socket.getInputStream();
        byte[] bytes = new byte[1024];
        while(sb.lastIndexOf(Message.MSG_END) == -1){
            int bytesRead = in.read(bytes);
            sb.append(new String(bytes,0,bytesRead,"UTF-8"));
        }
        response = sb.toString();

        return response;
    }

    public void sendMessage(Socket socket, String message) throws IOException{
        Message msg = new Message(message);
        System.out.println("Client now sending message to server: " + msg);
        OutputStream out = socket.getOutputStream();
        out.write(msg.toString().getBytes("UTF-8"));
    }
}

The output

Client now sending message to server: Why hello there!
Server got the message:  Why hello there!
Server now sending a response to the client: Ack
Client got a response from the server: Ack

Process finished with exit code 0
Outhaul answered 20/4, 2015 at 14:49 Comment(2)
It's not possible to send -1 from the client code. He is reading in single bytes. As long as the connection hasn't been closed he can only receive values between 0 and 255. If the client called out.write(-1) the server would receive 255.Endurance
@SpiderPig To be honest, I wasn't really sure. I have vague recollections of doing that in the past, but I'm probably remembering it wrong. What you said makes sense. I'm updating my answer to reflect it.Outhaul
B
1

The problem is not that you are not flushing properly, but that the reading code waits for the socket to disconnect before handling the data:

while((i = in.read()) >= 0)

Will loop as long as something can be read from in (the socket's InputStream). The condition will not fail until the other peer disconnects.

Bethanie answered 20/4, 2015 at 14:50 Comment(0)
I
0

Try using

socket.setTcpNoDelay(true);

There is buffering that occurs for performance reasons (read up on Nagle's algorithm).

Irrawaddy answered 20/4, 2015 at 14:39 Comment(2)
Great minds :) I tried to post before you but this site started going really slow (other sites were fine).Excelsior
This will have exactly no effect on this problem.Illyricum
E
-1

Looking at your code it seems ok. However you are sending less than the MTU Nagle's algothrim could be holding it back until enough data is present for a full packet or you close the socket.

So - try this:

socket.setTCPNoDelay(true);

http://en.wikipedia.org/wiki/Nagle%27s_algorithm https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html#setTcpNoDelay-boolean-

Excelsior answered 20/4, 2015 at 14:44 Comment(2)
Also try padding out your string to > 1500 bytes. That would prove it.Excelsior
This will have exactly no effect on this problem.Illyricum

© 2022 - 2024 — McMap. All rights reserved.