AdrianES
AdrianES

Reputation: 670

how to correctly close SocketChannel java in JAVA?

I am developing multithreading non-blocking tcp server and client in Java. I have a problem with the server realizing that the client socket is closed. For him it's allways open. Here is the code and the output of my code:

Server:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package androidnonblockingnewesttest;

import globalvariables.*;
import interface_package.*;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.nio.channels.Channels;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

/**
 *
 * @author wsserver
 */
public class AndroidNonBlockingNewestTest {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        //Prevzemi staticen interface
        GlobalVariables.sinterface = new ServerInterface();
        GlobalVariables.sinterface.show();

        //POSTAVI SERVER
        //INFINITE LOOP
        //NE MOZAM POSLE OVA DA POVIKUVAM NISTO BIDEJKI NEMA DA STIGNE DO NEGO PORADI while(true)
        int port = GlobalVariables.portNo;

        ServerSocketChannel serverSocket = null;
        try {
            serverSocket = ServerSocketChannel.open();
            serverSocket.socket().bind(new InetSocketAddress(port));
            serverSocket.configureBlocking(false);
            System.out.println("Server has started listening on port " + port);
            GlobalVariables.sinterface.setServerStatus("Server has started listening on port " + port);
            GlobalVariables.sinterface.setReceivedText("Server has started listening on port " + port);
        } catch (IOException e) {
            System.out.println("Error: Cannot listen on port " + port + " : " + e);
            GlobalVariables.sinterface.setServerStatus("Error: Cannot listen on port " + port + " : " + e);
            GlobalVariables.sinterface.setReceivedText("Error: Cannot listen on port " + port + " : " + e);
            System.exit(1);
        }
        while (true) // infinite loop - loops once for each client
        {
            SocketChannel clientSocket = null;
            try {
                clientSocket = serverSocket.accept(); //waits here (forever) until a client connects

                if (clientSocket == null) {
                    // No connections came .
                } else {
                    clientSocket.configureBlocking(false);
                    // You got a connection. Do something
                    System.out.println("Server has just accepted socket connection from a client");
                    GlobalVariables.sinterface.setServerStatus("Server has just accepted socket connection from a client");
                    GlobalVariables.sinterface.setReceivedText("Server has just accepted socket connection from a client");

                    // Create the Handle Connection object - our new thread object - only create it
                    ThreadedHandleConnection con = new ThreadedHandleConnection(clientSocket);

                    if (con == null) //If it failed send and error message
                    {
                        try {
                            ObjectOutputStream os = new ObjectOutputStream(Channels.newOutputStream(clientSocket));
                            os.writeObject("error: Cannot open socket thread");
                            GlobalVariables.sinterface.setReceivedText("error: Cannot open socket thread");
                            os.flush();
                            os.close();
                        } catch (Exception ex) //failed to even send an error message
                        {
                            System.out.println("Cannot send error back to client: " + ex);
                            GlobalVariables.sinterface.setServerStatus("Cannot send error back to client: " + ex);
                            GlobalVariables.sinterface.setReceivedText("Cannot send error back to client: " + ex);
                        }
                    } else {
                        con.start();
                    } // otherwise we have not failed to create the HandleConnection object
                    // start this thread now
                }
            } catch (IOException e) {
                System.out.println("Accept failed: " + e);
                GlobalVariables.sinterface.setServerStatus("Accept failed: " + e);
                GlobalVariables.sinterface.setReceivedText("Accept failed: " + e);
                break;
            }
        }

        try // do not get here at the moment 
        {
            System.out.println("Closing server socket.");
            GlobalVariables.sinterface.setServerStatus("Closing server socket.");
            GlobalVariables.sinterface.setReceivedText("Closing server socket.");
            serverSocket.close();
        } catch (IOException e) {
            System.err.println("Could not close server socket. " + e.getMessage());
            GlobalVariables.sinterface.setServerStatus("Could not close server socket. " + e.getMessage());
            GlobalVariables.sinterface.setReceivedText("Could not close server socket. " + e.getMessage());
        }
    }
}

Connection Handler:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package androidnonblockingnewesttest;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;

/**
 *
 * @author wsserver
 */
public class ThreadedHandleConnection extends Thread {

    private SocketChannel clientSocket;
    Charset charset = Charset.forName("ISO-8859-1");
    CharsetEncoder encoder = charset.newEncoder();
    CharsetDecoder decoder = charset.newDecoder();
    ByteBuffer buffer = ByteBuffer.allocate(1024);

    // The constructor for the connecton handler
    public ThreadedHandleConnection(SocketChannel clientSocket) {
        try {
            this.clientSocket = clientSocket;
            clientSocket.configureBlocking(false);
        } catch (IOException ex) {
            System.out.println("SocketChannel blocking exception: " + ex.getMessage());
        }
    }

    // The main thread execution method 
    @Override
    public void run() {
        while (!clientSocket.socket().isClosed()) {

            try {

                System.out.println("Socket status:clientSocket.isConnected():" + clientSocket.isConnected());
                System.out.println("Socket status:clientSocket.finishConnect():" + clientSocket.finishConnect());
                System.out.println("Socket status:clientSocket.isOpen():" + clientSocket.isOpen());
                System.out.println("Socket status:clientSocket.socket().isClosed():" + clientSocket.socket().isClosed());
                System.out.println("Socket status:clientSocket.socket().isClosed():" + clientSocket.socket().isConnected());


                int bytesread = clientSocket.read(buffer);
                if (!(bytesread > 0)) {
                    System.out.println("Nothing to read");
                } else {
                    buffer.flip();

                    String request = decoder.decode(buffer).toString();

                    System.out.println("Request:" + request);
                    buffer.clear();
                }
                Thread.sleep(3000);
            } catch (IOException ex) {
                System.out.println("Socket run exception " + ex.getMessage());
            } catch (Exception ex) {
                System.out.println("Exception " + ex.getMessage());
            }
        }
    }
}

Client:

package androidnonblockingnewesttest;

import globalvariables.GlobalVariables;
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author wsserver
 */
public class ThreadedTCPClient {

    private SocketChannel socket = null;
    String serverIP = GlobalVariables.serverIP;
    int port = GlobalVariables.portNo;

    Charset charset = Charset.forName("ISO-8859-1");
    CharsetEncoder encoder = charset.newEncoder();
    CharsetDecoder decoder = charset.newDecoder();
    ByteBuffer buffer = ByteBuffer.allocate(1024);

    // the constructor expects the IP address of the server
    public ThreadedTCPClient() {
        if (!connectToServer()) {
            System.out.println("Cannot open socket connection...");
        }
    }

    private boolean connectToServer() {
        try // open a new socket to port:  and create streams
        {
            this.socket = SocketChannel.open();
            this.socket.configureBlocking(false);
            this.socket.connect(new InetSocketAddress("localhost", port));
            while(!this.socket.finishConnect()){

            }
            System.out.print("Connected to Server\n");

        } catch (Exception ex) {
            System.out.print("Failed to Connect to Server\n" + ex.toString());
            System.out.println(ex.toString());
            return false;
        }
        return true;
    }

    public void sendTest() {
        try {
            buffer = encoder.encode(CharBuffer.wrap("Hellow from client"));
            socket.write(buffer);
            buffer.clear();
        } catch (IOException ex) {
            System.out.println("Write exception: "+ex.getMessage());
        }
    }

    public void closeConnection(){
        try{
            this.socket.socket().close();
            this.socket.close();
            System.out.println("Close");
        }catch(Exception ex){
            System.out.print("Failed to close connection to Server\n" + ex.toString());
            System.out.println(ex.toString());
        }
    }

    public static void main(String args[]) {

        ThreadedTCPClient theApp = new ThreadedTCPClient();
        try {
            Thread.sleep(10000);
            theApp.sendTest();
            theApp.closeConnection();
        } catch (Exception ex) {
            System.out.println(ex.toString());
        }

    }
}

And here is my outpusts. Output from the client:

Connected to Server
Close

Output from the server:

Server has started listening on port 10001
Server has just accepted socket connection from a client
Socket status:clientSocket.isConnected():true
Socket status:clientSocket.finishConnect():true
Socket status:clientSocket.isOpen():true
Socket status:clientSocket.socket().isClosed():false
Socket status:clientSocket.socket().isClosed():true
Nothing to read
Socket status:clientSocket.isConnected():true
Socket status:clientSocket.finishConnect():true
Socket status:clientSocket.isOpen():true
Socket status:clientSocket.socket().isClosed():false
Socket status:clientSocket.socket().isClosed():true
Nothing to read
...
Socket status:clientSocket.isConnected():true
Socket status:clientSocket.finishConnect():true
Socket status:clientSocket.isOpen():true
Socket status:clientSocket.socket().isClosed():false
Socket status:clientSocket.socket().isClosed():true
Request:Hellow from client
Socket status:clientSocket.isConnected():true
Socket status:clientSocket.finishConnect():true
Socket status:clientSocket.isOpen():true
Socket status:clientSocket.socket().isClosed():false
Socket status:clientSocket.socket().isClosed():true
Nothing to read
...

After closing the connection from the client all the SocketChannel and SocketChannel.socket() statuses aren't changing. Please help.

Upvotes: 0

Views: 6293

Answers (1)

user207421
user207421

Reputation: 311052

Your code is wrong in numerous respects.

  1. If you're using non-blocking I/O you should certainly also be using a Selector rather than spinning madly in accept(), finishConnect(), etc.

  2. isClosed() doesn't tell you whether the peer has closed. It tells you whether you have closed. If the peer has closed, read() will return -1. Similary, isConnected() only tells you whether you ever connected the socket. You did, so calling it repeatedly is pointless.

  3. The correct way to connect in non-blocking mode is to (a) call connect(), (b) select on OP_CONNECT, (c) when it fires call finishConnect(), and (d) if and only if that returns true, cancel the interest in OP_CONNECT.

  4. If you get any IOException whatsoever in non-blocking mode you must close the channel.

  5. 'Multithreading non-blocking server' verges on being a contradiction in terms. If you have threads, block. If you have non-blocking mode, select.

Upvotes: 3

Related Questions