Java Socket.read (ByteBuffer dst) не получает ни одного байта

Я озадачен своим последним затруднительным положением с программированием Java Socket в течение трех дней подряд. Я использую NIO ByteBuffer и Channel для передачи байтов от клиента к серверу. Я тестирую свой код, заставляя клиента отправлять 2 файла последовательно - первые файлы всегда приходят, а второй всегда теряется, так что где. Я сделал tcpdump и увидел трафик по определенному адресу и порту, но не смог расшифровать файл pcap (понять, как читать весь синтаксис в файлах pcap).

каким-то образом socketchannel.read(bytebuffer) не читает ни одного байта для второго файла. первый файл будет в порядке. Он проходит через команду чтения, получает файл и отвечает. второй файл будет в порядке для команды чтения и ответа. он не получает никакого файла для socketchannel.read получает -1 --- ЭТО проблема.

Пожалуйста, помогите. Это действительно сумасшедшая проблема.

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)
            { }
        }
    }
}

Этот ServerThread реализует runnable, который вызывается другим классом ServerMain, который передает только serversocket.accept() в ServersThread (сокет Socket)

Вот класс ServerMain:

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);
            }
        }
    }
}

Вот клиентский класс

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);
    }

}

person Spring    schedule 23.05.2014    source источник
comment
С помощью tcpdump и wireshark я проверил, что клиент отправил данные, а сервер получил байт в соответствии с планом. Я изучил ACK, PSH ACK, SYN, FIN ACK - файла wireshark и pcap. Однако на уровне java все еще происходит sc.read(dst) != 1! очень загадочно! Кто-нибудь готов предложить? Я сейчас в остроумии.   -  person Spring    schedule 24.05.2014


Ответы (2)


При чтении первой загрузки вы читаете до конца потока, включая весь второй файл. Поэтому, когда вы идете читать второй файл, ничего не остается. Вам нужно отправить себе длину файла перед файлом и прочитать только это количество байтов.

person user207421    schedule 23.05.2014
comment
Если вы видите основной метод клиента, я отправляю один файл, и он заканчивается (закрывает все сокеты). Я отправляю другой файл, снова запуская процесс на стороне клиента и на стороне сервера. Серверы второго запуска все еще могут читать и отвечать на команду и ответ; однако он всегда выходит с пустым файлом sc.read(dst), поэтому файл не записывается. - person Spring; 24.05.2014
comment
Вы все еще читаете канал сокета до конца файла. Это происходит только тогда, когда одноранговый узел закрывает соединение. - person user207421; 25.05.2014

Проведя около 12 дней с этой проблемой, я, наконец, каким-то образом решил проблему, хотя я не могу объяснить, почему проблема возникает в первую очередь. Проблема сложная, потому что это происходит почти все время. Несогласованная доставка файлов.

Я меняю протокол приложения.

Исходный протокол для клиента 1. Отправить String order 2. Отправить фактический файл 3. Получить подтверждение сервера

Новый протокол для клиента 1. Отправить String order 2. Получить подтверждение сервера 3. Отправить фактический файл 4. Получить подтверждение сервера

Благодаря новому протоколу отправляемый файл становится согласованным.

Спасибо, в любом случае.

person Spring    schedule 25.05.2014