3

How can I stop the SocketException from occurring?

I am trying to do a simple transfer of a serialized object from a client to a server on my local machine.

I have been able to use a slight variation of the following code to send strings, but when I try to send an object

Customer customerToReceive = (Customer) input.readObject();// EXCEPTION OCCURS RIGHT HERE

I get a SocketException that I don't understand how to interpret.

java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(Unknown Source)
at java.io.ObjectInputStream$PeekInputStream.read(Unknown Source)
at java.io.ObjectInputStream$BlockDataInputStream.read(Unknown Source)
at java.io.ObjectInputStream$BlockDataInputStream.readFully(Unknown Source)
at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
at java.io.ObjectInputStream.readSerialData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at MattServer.runCustomerServer(MattServer.java:44)
at MattServer.<init>(MattServer.java:14)
at MattServerTest.main(MattServerTest.java:10)

Here is the Client code, which doesn't seem to complain at all: public class MattClient { Socket client; ObjectOutputStream output; ObjectInputStream input; String message;

public MattClient()
{
    runCustomerClient();
}

public void runCustomerClient()
{
    try
    {
        //Connection:
        System.out.println("Attempting connection...");
        client = new Socket("localhost",12345);
        System.out.println("Connected to server...");

        //Connect Streams:

        //output.flush();
        System.out.println("Got IO Streams...");

        //SEND MESSAGES:
        try
        {
            for(int i = 1;i<=10;i++)
            {
                output = new ObjectOutputStream(client.getOutputStream());
                Customer customerToSend = new Customer("Matt", "1234 fake street", i);
                System.out.println("Created customer:");
                System.out.println(customerToSend.toString());
                output.writeObject(customerToSend);
                output.flush();
            };
            message = "TERMINATE";
            System.out.println(message);
            output.writeObject(message);
            output.reset();
            output.flush();
        }
        catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch(Exception e2)
        {
            e2.printStackTrace();
        }
        finally
        {

        }
    }
    catch (IOException e1)
    {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
    catch(Exception e3)
    {
        e3.printStackTrace();
    }
    finally
    {

    }
}

And the server which revolts :

public class MattServer
{
ServerSocket server;
Socket socket;
ObjectInputStream input;
ObjectOutputStream output;
String message;

public MattServer()
{
    runCustomerServer();
}

public void runCustomerServer()
{
    try
    {
        server = new ServerSocket(12345,100000);
        while(true)
        {
            //CONNECTION:
            System.out.println("Waiting for connection");
            socket = server.accept();
            System.out.println("Connection received...");

            //CONNECT STREAMS:
            //output = new ObjectOutputStream(socket.getOutputStream());
            //output.flush();
            input = new ObjectInputStream(socket.getInputStream());
            System.out.println("Got IO Streams...");

            //PROCESS STREAMS:
            System.out.println("Connection successful!");
            do
            {
                System.out.println("Started loop");
                try
                {
                    System.out.println("in try...");
                    System.out.println(socket.getInetAddress().getHostName());
                    Customer customerToReceive = (Customer) input.readObject();// EXCEPTION OCCURS RIGHT HERE
                    Object o = input.readObject();
                    System.out.println("Object of class " + o.getClass().getName() + " is " + o);
                    System.out.println("Got customer object");
                    System.out.println(customerToReceive.toString());
                }
                catch(ClassNotFoundException cnfE)
                {
                    System.out.println("Can't convert input to string");
                }
            } while(!message.equals("TERMINATE"));

            System.out.println("Finished.");

        }
    }
    catch(IOException ioE)
    {
        ioE.printStackTrace();
    }
    finally
    {
        try
        {
            input.close();
            socket.close();
            server.close();
        }
        catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
Jean-François Corbett
  • 37,420
  • 30
  • 139
  • 188
zolointo
  • 123
  • 1
  • 2
  • 7
  • 1
    This question is similar to http://stackoverflow.com/q/62929/130224 and http://stackoverflow.com/q/585599/130224. – reprogrammer Dec 11 '12 at 20:06
  • possible duplicate of [StreamCorruptedException: invalid type code: AC](http://stackoverflow.com/questions/2393179/streamcorruptedexception-invalid-type-code-ac) – user207421 Dec 11 '12 at 21:27

3 Answers3

2

This exception is the result of closing the connection too early, that is, the client sends its data and immediatly closes the connection. In this case, the server usually has not been able to read all data sent by the client and the termination of the connection comes in the middle of the transfer as seen by the server side.

Note that flush()-ing a network OutputStream does not mean or guarantee that any/all data has actually been transmitted. In the best scenario this only makes sure that all data is passed on to the network stack on the local machine, which will decide at its discretion when to actually transfer the data.

One solution to this is to let the server close the connection when it is ready to. The client should then wait, e.g. in a blocking read() operation, and will then be notified by the named exception when the server signals the end of the transmission.

Another way is to implement your own acknowledgement so that the client will wait for the server to send an acknowledgement message back, after which the connection can be safely closed by both sides.

For reference, there are socket options which influence the behavior of the connection in cases like yours, namely:

SO_LINGER and TCP_NODELAY

(These socket options are not a solution to your problem but may change the observed behavior in some cases.)

EDIT:

It appears the relevance of the SO_LINGER option for the observed behavior is not as obvious as I thought it was from the referenced documentation. Therefore, I will try to make the point clearer:

In Java there are basically two ways to terminate a TCP connection via close():

  1. Without enabling the SO_LINGER option. In this case, a TCP connection reset (RST) is immediatly sent and the call to close() returns. The peer side of the connection will receive an exception stating that the 'connection has been reset' when trying to use the connection (read or write) after the RST has been received.

  2. With enabled SO_LINGER option. In this case, a TCP FIN is generated to orderly shut down the connection. Then, if the peer does not acknowlege the data sent within the given time frame, a timeout occurs and the local party issuing the close() continues as in case #1, sending an RST and then declaring the connection 'dead'.

So, usually one wants to enable SO_LINGER to allow the peer to process the data and then tear down the connection cleanly. If SO_LINGER is not enabled and close() is called before all data has been processed by the peer (i.e. 'too early') the named exception re the reset connection occurs at the peer.

As said above, the TCP_NODELAY option may change the observed behavior in a non-deterministic way, because the data written is more likely to already be transferred across the network before the call to close() causes the reset of the connection.

JimmyB
  • 12,101
  • 2
  • 28
  • 44
  • So a quick solution is to play ping pong? Client sends an object and then receives a confirmation that it's been received before attempting to send another object? I'll give it a shot. – zolointo Dec 11 '12 at 20:38
  • Getting closer. I was able to send one object over using the read() write() block statements. Now the client is waiting indefinitely at input.read(), even though I had the server do output.write(1). Hmmm.. researching, but please chime in @Hanno if you have a suggestion. – zolointo Dec 11 '12 at 20:52
  • Never mind! I got it... mostly! I used a readObject/writeObject instead of a regular read/write. 10 Objects passed correctly! – zolointo Dec 11 '12 at 20:54
  • This answer is incorrect in its entirety. The receiver does not get the FIN until it has read all the pending data. TCP does not lose data or give errors because the sender closes 'too early'. Messing around with `SO_LINGER` and `TCP_NODELAY` does not fix this non-existent problem, and neither does sending redundant application acknowledgements. Consider FTP. – user207421 Dec 11 '12 at 22:21
  • @EJP: So, you are positive that the given Java code causes a `FIN` to be sent and not an `RST`? - Please share your source with us. – JimmyB Dec 12 '12 at 10:09
  • @EJP: Don't bother looking up a source. Just have a look at the Java docs for `SO_LINGER` I quoted above. The behavior is explained there. – JimmyB Dec 12 '12 at 10:17
  • @HannoBinder This is all irrelevant. You said 'the exception is the result of closing the connection too early', while data is still being sent. It isn't. Period. The code posted clearly receives an RST, *not* a FIN, and I haven't suggested otherwise. TCP_NODELAY just prevents packet merging, and SO_LINGER is a way to *cause* this problem, not to solve it. You don't seem to know much about any of this. – user207421 Dec 12 '12 at 12:19
  • @EJP If you don't know much about Java network programming, why do you refuse to read the documentation I pointed you to? It states quite clearly how `SO_LINGER` relates to `FIN` and `RST` respectively. Remember, this is a *Java* question. - Since most of your statements are exactly the opposite of correct, I recommend you edit/delete those comments. If needed, I may provide 'real' example code which demonstrates the behavior as it is *observed* and *documented*. – JimmyB Dec 12 '12 at 13:27
  • @user1895785 The 'ping-pong' scheme may be used to synchronize the transfer. However, by now I seem to believe that using `SO_LINGER` with a reasonably over-estimated timeout value (maybe even 10s of seconds) is the best way to go. This will allow a clean shutdown of the connection and implicitly includes timeout handling for broken connection cases or the like, so you won't have to. – JimmyB Dec 12 '12 at 13:53
  • @HannoBinder Your answer remains incorrect and the documentation to which you refer remains irrelevant for the reasons I have stated, which you have not addressed. Instead you have now fabricated the claim that I haven't read it, which is false; fabricated the baseless claim that indeed I have refused to read it; and questioned the extent of my knowledge, which is amply demonstrated in ISBN 1849965455. Look it up. I wrote it. Stick to the point. There is no such thing as closing 'too early' in TCP. Have a look at RFC 793 and see for yourself. – user207421 Dec 12 '12 at 18:20
  • @EJP If you literally 'wrote the book', I cannot seem to understand why you keep mixing up pure TCP functionality ("RFC 793") with the documented behavior of the higher layer (Java-)API, stating what *TCP* 'does' or 'does not', ignoring the relevant API docs, and throwing in another unrelated OSI layer ("FTP"). - Excuse me for not being impressed by the fact that someone had a book published, but I prefer to form my opinion based on what people actually do when challenged; and I esteem *constructive* efforts for that matter, which I haven't yet seen from your side. "Period." [delete me] – JimmyB Dec 12 '12 at 23:23
  • @HannoBinder (a) The 'higher layer' Java API just calls the same Sockets API that everyone else does. It doesn't add any behaviour of its own. SO_LINGER is available at the Sockets API level. (b) Please see my edited question for a fuller discussion of the fallacy you are advancing here. (c) Unlike posts here, technical books are rigorously reviewed by experts prior to publication, so they carry somewhat more weight than random posts on the Internet. – user207421 Dec 13 '12 at 01:31
-1

Don't call output.reset() from the client; seems like that could be causing the server to think the connection was reset. Irrelevant, but you probably don't need the flush() either.

I also don't think you should create a new ObjectOutputStream in each loop of the for-loop. Initialize that outside (above) the loop, and reuse it.

dashrb
  • 952
  • 4
  • 10
  • 1
    On an `ObjectOutputStream` the `reset()` just causes the stream to purge its cache of transmitted objects, that is, to 'forget' all objects already transmitted. Thus, calling `reset()` right before discarding the stream is kind of useless but does not interfere with the transmission of objects to the unterlying stream. - Flushing the `ObjectOutputStream` is, however, a good idea to make sure that all data was passed from the `ObjectOutputStream` to the underlying stream; `close()`-ing the OOStream should implicitly flush it, too. – JimmyB Dec 11 '12 at 20:32
-1

This exception has several causes, but the most usual one is that you wrote to a connection that had already been closed by the other end. In other words an application protocol problem: you are writing more data than you are reading.

In this case it is because you are using one ObjectInputStream for the life of the socket, but a new ObjectOutputStream for every message, which cannot possibly work. Use one of each for the life of the connection, at both ends.

You must also have got an 'invalid type code AC' message in an IOException at the receiver. Were you aware of that?

EDIT: For adherents to the 'early-closing' fallacy being adumbrated elsewhere in this thread:

  1. RFC 793 #3.5 states (a) 'CLOSE is an operation meaning "I have no more data to send"', (b) 'A TCP will reliably deliver all buffers SENT before the connection was CLOSED', and (c) 'Local user initiates the close: in this case a FIN segment can be constructed and placed on the outgoing segment queue' [my emphasis]. The FIN does not arrive before any pending data, and the close does not abort transmission of pending data.

  2. Setting a positive SO_LINGER timeout causes close() to block until all pending data is sent or the timeout expires. Without setting SO_LINGER at all, pending data is sent anyway, but asynchronously to the close unless a +ve linger timeout is set. There is a third possibility which deliberately discards pending data and sends an RST instead of a FIN. The OP isn't doing that here and it isn't any kind of a solution to a problem that already involves an RST.

  3. I did a throughput experiment over the Internet a few years ago in which I varied every possible TCP parameter except SO_LINGER, which I left in its default state. Every test consisted of a timed one-way transmission of several megabytes followed by a normal close without any positive 'linger' timeout or any waiting for the peer. In 1700 connections 'connection reset' was encountered zero times. The onus is on adherents to explain these results, also to explain why the overwhelming majority of TCP problems don't mess with SO_LINGER. The onus is also indeed on them to produce a test case that does exhibit the claimed behaviour. Conditions: a client which connects, sends a large amount of data, and closes, without messing with SO_LINGER or indeed any socket option whatsoever, and a server that listens, accepts, reads to EOS, and closes. Over to you.

There is no such thing as closing a connection 'too early' in TCP.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • "you are writing more data than you are reading" - No, the server is *reading* more data than is 'available'. – JimmyB Dec 12 '12 at 23:26
  • "one ObjectInputStream" vs. "a new ObjectOutputStream for every message" - Another bug, true; but unrelated to the observed `SocketException`. – JimmyB Dec 12 '12 at 23:35
  • @HannoBinder (a) The server cannot possibly read more data than was available. If it tried that it would block or get an EOS condition. (b) This answer is not 'unrelated'. It cause the said `IOException`, which should cause the receiver to close the socket, which causes the sender to get 'connection reset' if he keeps sending. – user207421 Dec 13 '12 at 01:29
  • "There is a third possibility which deliberately discards pending data and sends an RST instead of a FIN. The OP isn't doing that here and it isn't any kind of a solution to a problem that already involves an RST." - Wrong. This is *exactly* what the OP does (inadvertently) by *not* enabling lingering. This, *and only this*, is what causes the `RST` to be sent instead of a `FIN`, which in turn causes the named `IOException`; *not* related to the higher layer OOStream in any way. – JimmyB Dec 13 '12 at 08:03
  • "The server cannot possibly read more data than was available." - No, of course it cannot. But it is still trying to at the instant when the `RST` occurs, which, again, causes the exact `IOException` quoted. - Note that in the origninal code the client only *writes* whereas the server only *reads*. The server then still expects to receive data when the client issues the `RST`. - Apart from that, I appreciate your extended explanations, which I don't doubt w.r.t. the general networking details; only the relation to the OP's original problem seems to be disputable. – JimmyB Dec 13 '12 at 08:16
  • @JimmyB If y9ou don't fiddle with the linger option, it is off by default, which means: (a) `close()` is asynchronous, (b) all pending data is sent, (c) the connection is terminated by a FIN. There is no need for anyone to enable linger at any time. I have never seen production code that used a positve linger timeout, and only once have I seen a zero linger timeout, which was an incorrect reaction to an invalid benchmark. – user207421 Nov 04 '16 at 00:15