Search code examples
javasocketssocketchannel

Java Socket.read(ByteBuffer dst) not getting any byte


I am puzzle with my latest predicament with Java Socket programming for three days straight now. I use NIO ByteBuffer and Channel to transfer byte from client to server. I test my code by having client to send 2 files in sequences - the first files always arrived but the second one is always lost so where. I did tcpdump and I saw the traffic in specific address and port but I could not decipher pcap's file (understand how to read all the syntax in the pcap files).

somehow socketchannel.read(bytebuffer) is not reading any byte for the 2nd file. the first file will be okay. It passes through the read command, receives file, and respond. the second file will be okay for read command and respond. it does not receive any file for socketchannel.read is getting -1 --- THAT is the problem.

Please do help. This is really crazy problem.

public class ServersThread implements Runnable 
{
    private Socket socket;

    public ServersThread(Socket socket)
    {
        this();
        this.socket = socket;
    }

    public void run()
    {
        try
        {
        InputStreamReader isr = new InputStreamReader(socket.getInputStream());
        BufferedReader br = new BufferedReader(isr);
        PrintStream ps = new PrintStream(socket.getOutputStream(), true);

        String orderFromClient = br.readLine(); // first read command from client

        String filename = orderFromClient.split(":")[0]
        long fileSize = Long.parseLong(orderFromClient.split(":")[1]);

        File destFile = new File(filename);         
        destFile.setReadable(true);
        destFile.setWritable(true);

        FileOutputStream inFile = new FileOutputStream(destFile);
        FileChannel inChannel = inFile.getChannel();


        SocketChannel sc = socket.getChannel();
        ByteBuffer dst = ByteBuffer.allocate(65536);
        dst.clear();

        // this receiving binary file part that is questionable.
        // it always run okay for the first file
        // the second file is created but always has size 0
                    // The second file will enter into while-loop start and end but it won't enter sc.read(dst)

        logger.debug(AUDIT,"while-loop start");
        start = System.currentTimeMillis();
        while (sc.read(dst) != -1)
        {
            dst.flip();
            logger.debug(AUDIT,"dst flip and ask remaining: {} at position {}",dst.hasRemaining(),dst.position());
            while (dst.hasRemaining())
            {
                temp = inChannel.write(dst);
                curnset += temp;
                logger.debug(AUDIT, "c {} | t {} | size {}", curnset, temp, fileSize);
            }
            dst.clear();
        }
        end = System.currentTimeMillis();
        logger.debug(AUDIT,"while-loop end");

        if (curnset == fileSize)
            ps.println("SUCCESS");
        else
            ps.println("FAIL");

        }
        catch(Exception e)
        {
            e.printStackTrace(System.err);
            logger.error("Exception ",e);
        }
        finally
        {
            try
            {
                inChannel.close();
                inFile.close();
                sc.close();
                ps.close();
                isr.close();
                br.close();
                socket.close();
            }
            catch(IOException e)
            { }
        }
    }
}

That ServerThread implements runnable that is being call by other class ServerMain that pass only serversocket.accept() into ServersThread(Socket socket)

Here is ServerMain class:

public class ServerMain
{

    public static void main(String[] args)
    {
        ServerSocketChannel listener = null;
        ServerSocket serverMain = null;

        try
        {
            listener = ServerSocketChannel.open();
            serverMain = listener.socket();
            serverMain.setReuseAddress(true);
            serverMain.bind(new InetSocketAddress("192.168.1.12",9999));

            while (true)
            {
                new ServersThread(serverMain.accept()).start();
            }

        }
        catch (Exception e)
        {
            logger.error("Exception ", e);
            e.printStackTrace(System.err);
        }
        finally
        {
            try
            {
                listener.close();
            }
            catch (IOException e)
            {
                logger.error("IOException ", e);
                e.printStackTrace(System.err);
            }
        }
    }
}

Here is the client class

public class ClientCallable
{

    public static void process(String serverAddr,int serverPort,File file,String command)
    {
        SocketChannel sc = null;
        PrintStream ps = null;
        BufferedReader br = null;
        int timeout = 10 * 1000;
        try
        {
            sc = SocketChannel.open();
            sc.configureBlocking(true);

            if (!sc.connect(new InetSocketAddress(serverAddr, serverPort)))
                return ClientMain.ERROR_UNABLE_TO_CONNECT;
            sc.socket().setSoTimeout(timeout);

        }
        catch (Exception e)
        {
            logger.error("Exception ", e);
            e.printStackTrace(System.err);
            return;
        }

        long maxCount = 8192 * 1024;
        long curnset = 0l;
        long temp = 0l;
        long filesize = 0l;
        long startTime = 0l;
        long endTime = 0l;
        String serverResp = null;
        FileInputStream fis = null;
        FileChannel fc = null;
        try
        {
            ps = new PrintStream(sc.socket().getOutputStream());
            br = new BufferedReader(new InputStreamReader(sc.socket()
                    .getInputStream()));
            fis = new FileInputStream(file);
            fc = fis.getChannel();
            filesize = fc.size();

            // send command to server
            ps.print(command);

            // send binary file
            ByteBuffer dst = ByteBuffer.allocate(65536);
            dst.clear();
            startTime = System.currentTimeMillis();
            while (fc.read(dst) != -1)
            {
                dst.flip();
                while (dst.hasRemaining())
                {
                    temp = sc.write(dst);
                    curnset += temp;
                    logger.debug(AUDIT, "c {} | t {} | size {}", curnset, temp,
                            filesize);
                }
                dst.clear();
            }
            sc.shutdownOutput();
            endTime = System.currentTimeMillis();

            // read server respond
            serverResp = br.readLine();
            logger.debug(AUDIT,"server responds {}",serverResp);
        }
        catch (Exception e)
        {
            logger.error("Exception ", e);
            e.printStackTrace(System.err);
        }

        try
        {
            if (fis != null)
                fis.close();
            if (fc != null)
                fc.close();
            if (ps != null)
                ps.close();
            if (br != null)
                br.close();
            if (sc != null)
                sc.close();
        }
        catch (Exception e)
        {
            logger.error("Exception ", e);
            e.printStackTrace(System.err);
        }

    }

    public static void main(String[] args)
    {
        String serverAddr = "192.168.1.12"
        int serverPort = 9999;
        File file1 = new File("file1.fpt");
        File file2 = new File("file2.fpt");
        String command = "somecommandtoserver";
        process(serverAddr,serverPort,file1,command);
        process(serverAddr,serverPort,file2,command);
    }

}

Solution

  • After spending about 12 days straight with these problem, I finally have somehow solve the problem even though I can't explain why the problem happens in the first place. The problem is tricky because it happens almost all the time. Inconsistent file delivery.

    I change the protocol of the application.

    Original protocol for client 1. Send String order 2. Send actual file 3. Receive server's acknowledgement

    New protocol for client 1. Send String order 2. Receive server's acknowledgement 3. Send actual file 4. Receive server's acknowledgement

    Due to new protocol, the sending file becomes consistent.

    Thanks anyway.