odnoklassniki / one-nio

Unconventional I/O library for Java
Apache License 2.0
655 stars 97 forks source link

Can't acquire a socket at the first time after the peer restarted or the remote socket closed #33

Closed forchid closed 5 years ago

forchid commented 5 years ago

The RpcClient can't acquire a socket at the first time after the peer restarted, because the IOException is thrown when the client invokes the remote method, and the RpcClient only catch SocketException instead of IOException in one.nio.rpc.RpcClient:

    private byte[] invokeRaw(Object request, int timeout) throws Exception {
        byte[] buffer = serialize(request);

        Socket socket = borrowObject();
        try {
            try {
                // Issue: stale connection(eg. the remote has been restored after the peer closed)
                //and IOException occurs but can't be catched
                sendRequest(socket, buffer, timeout);
            } catch (SocketException e) {
                // Stale connection? Retry on a fresh socket
                destroyObject(socket);
                socket = createObject();
                sendRequest(socket, buffer, timeout);
            }

But the sendRequest() method can throw IOException after the peer socket closed:

   private void sendRequest(Socket socket, byte[] buffer, int timeout) throws IOException {
        if (timeout != 0) socket.setTimeout(timeout);
        socket.writeFully(buffer, 0, buffer.length);
        socket.readFully(buffer, 0, 4);
    }

This issue also exists in one.nio.http.HttpClient.invoke() method:

    public Response invoke(Request request) throws InterruptedException, PoolException, IOException, HttpException {
        int method = request.getMethod();
        byte[] rawRequest = request.toBytes();
        ResponseReader responseReader;

        Socket socket = borrowObject();
        boolean keepAlive = false;
        try {
            try {
                // Issue: stale connection(eg. the remote has been restored after the peer closed) 
                //and IOException occurs but can't be catched
                socket.writeFully(rawRequest, 0, rawRequest.length);
                responseReader = new ResponseReader(socket, BUFFER_SIZE);
            } catch (SocketException e) {
                // Stale connection? Retry on a fresh socket
                destroyObject(socket);
                socket = createObject();
                socket.writeFully(rawRequest, 0, rawRequest.length);
                responseReader = new ResponseReader(socket, BUFFER_SIZE);
            }
apangin commented 5 years ago

We have never seen such problem. A stale socket usually results in a "Connection reset" which is a SocketException. Can you provide a test scenario which demonstrates the issue? What OS do you run? Does one-nio use native NativeSockets or JavaSockets?

forchid commented 5 years ago

Using JavaSocket and Windows 8.1 x64.

apangin commented 5 years ago

Ah, that's Windows. Understood. Modified RpcClient/HttpClient to handle IOException instead of SocketException.

But please be aware that Windows is not a supported one-nio platform. JavaSockets are for development purpose only. Production systems should use NativeSockets.