【问题标题】:Java Socket.read(ByteBuffer dst) not getting any byteJava Socket.read(ByteBuffer dst) 没有得到任何字节
【发布时间】:2014-07-12 19:25:49
【问题描述】:

我对最近三天的 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 实现了由其他类 ServerMain 调用的可运行对象,该类仅将 serversocket.accept() 传递给 ServersThread(Socket 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);
    }

}

【问题讨论】:

  • 我已经使用 tcpdump 和 wireshark 验证了客户端发送数据并且服务器按计划接收到字节。我研究了wireshark和pcap文件的ACK、PSH ACK、SYN、FIN ACK。然而,在 java 的级别, sc.read(dst) != 1 仍在发生!非常令人费解!有人提出建议吗?我现在无计可施。

标签: java sockets socketchannel


【解决方案1】:

在解决这些问题大约 12 天之后,我终于以某种方式解决了问题,尽管我无法解释为什么会首先出现问题。这个问题很棘手,因为它几乎一直在发生。文件传递不一致。

我更改了应用程序的协议。

客户端的原始协议 1.发送字符串订单 2.发送实际文件 3. 接收服务器的确认

客户端的新协议 1.发送字符串订单 2.接收服务器的确认 3.发送实际文件 4. 接收服务器的确认

由于新协议,发送文件变得一致。

还是谢谢。

【讨论】:

    【解决方案2】:

    读取第一个上传文件时,您正在读取直到流结束,包括所有第二个文件。因此,当您阅读第二个文件时,什么都没有了。您需要在文件之前向自己发送文件长度,并且只读取那么多字节。

    【讨论】:

    • 如果你看到客户端的 main 方法,我发送一个文件并结束(关闭所有套接字)。我通过在客户端和服务器端重新启动该过程来发送另一个文件。第二次运行服务器仍然可以读取和回复命令和响应;但是,它总是以空 sc.read(dst) 出现,因此没有写入文件。
    • 您仍在读取套接字通道,直到文件结束。只有当对等方关闭连接时才会发生这种情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-15
    • 2013-01-13
    相关资源
    最近更新 更多