SocketChannel:读取的字节数不足

SocketChannel: not enough bytes read

我正在通过 socketChannel 实现客户端服务器通信。在对较大对象进行负载测试期间,我 运行 遇到了客户端问题。 所以我已经实施了一个测试程序来验证我的问题并在此处进行说明。

首先是一个小的解释:我不知道在客户端将通过套接字发送的对象的大小。 所以我把我的发送分成两部分: 1.:在服务器端将对象序列化为字节缓冲区(在用字节数组说明的示例中)。 2.:通过socket发送对象的soze 3.: 发送对象

在客户端,我首先读取一个 4 字节的 ByteBuffer 中的对象大小。其次,我创建了一个具有读取大小的新 ByteBuffer,然后将数据从 socketchannel 读取到缓冲区中。

如果你看一下代码,你会看到(在客户端 class),我的期望是,socketChannel.read 方法将 return 相同字节数为之前读取的对象大小。

但是增加sendet byte array后,会出现大量的不匹配和零大小。为什么会这样??套接字通道是非阻塞的。所以它应该能够读取配置的字节缓冲区中的所有内容。字节缓冲区的大小足够大。那么为什么有时会丢失字节?

非常感谢! 这是示例代码:

主要Class

package socketChannelMaxTest;

import java.io.IOException;

public class MaxTest {

    public static void main(String[] args) throws IOException {
        Sender.startServer();
        new Client();
    }

}

服务器发件人

package socketChannelMaxTest;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class Sender {

    private SocketChannel sc;

    public Sender(SocketChannel sc) {
        this.sc = sc;
        startThreads();
    }

    public static void startServer() throws IOException {
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ServerSocketChannel socket = ssc.bind(new InetSocketAddress(9999));
        new Thread(() -> {
            System.out.println("Server: Listening");
            SocketChannel sc;
            try {
                sc = socket.accept();
                sc.configureBlocking(true);
                new Sender(sc);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }).start();
    }

    private void startThreads() {
        new Thread(() -> {
            System.out.println("Sender: start sending");
            ByteBuffer headerBuffer = ByteBuffer.allocateDirect(4);

            int maxBufferSize = 10*1024;
            for (int i = 1; i < maxBufferSize; i++) {
                byte[] randomByteArray = new byte[i];
                ByteBuffer dataBuffer = ByteBuffer.wrap(randomByteArray);
                int objectSize = randomByteArray.length;
                headerBuffer.putInt(objectSize);
                headerBuffer.flip();
                try {
                    sc.write(headerBuffer);
                    System.out.println("Sender: " + objectSize + " " + sc.write(dataBuffer));
                } catch (IOException e) {
                    e.printStackTrace();
                }
                headerBuffer.compact();

            }
            System.out.println("Sender: finished");
        }, "Receiver Thread").start();
    }
}

客户端接收方

package socketChannelMaxTest;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class Client {
    public Client() throws IOException {
        startThreads();
    }

    private void startThreads() throws IOException {
        System.out.println("Client: start client");
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 9999));
        socketChannel.configureBlocking(true);
        System.out.println("Client: connected");

        new Thread(() -> {
            System.out.println("Client: start listening");
            ByteBuffer headerBuffer = ByteBuffer.allocate(4);
            int readedObjectSize = 0;
            while (socketChannel.isConnected()) {
                try {
                    int read = socketChannel.read(headerBuffer);
                    headerBuffer.flip();
                    readedObjectSize = headerBuffer.getInt();
                    headerBuffer.compact();

                    ByteBuffer dataBuffer = ByteBuffer.allocateDirect(readedObjectSize);
                    int readedDataBufferSize = socketChannel.read(dataBuffer);
                    // should be 0
                    int remainginBytes = dataBuffer.remaining();
                    dataBuffer.flip();
                    System.out.println("Client:" + readedObjectSize + " " + readedDataBufferSize + " " + remainginBytes);

                    if (readedObjectSize != readedDataBufferSize)
                        System.out.println("Missmatch");

                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        }, "Receiver Thread").start();
    }

}

您假设剩余数据在任何时候都始终为 0 的事实存在问题

// should be 0
int remainginBytes = dataBuffer.remaining();

您无法确定任何时候都会是这种情况。如果数据太大,您将需要从 SocketChannel 中再次读取以检索剩余信息。

你应该有一个从 socketChannel 读取直到没有剩余字节的循环

int read = socketChannel.read(headerBuffer);
headerBuffer.flip();
readedObjectSize = headerBuffer.getInt();
headerBuffer.compact();

int remainginBytes = -1;
int readedDataBufferSize = 0;
ByteBuffer dataBuffer = ByteBuffer.allocateDirect(readedObjectSize);

while(remainginBytes != 0){

    readedDataBufferSize = socketChannel.read(dataBuffer);
    remainginBytes = dataBuffer.remaining();
}

dataBuffer.flip();

System.out.println("Client:" + readedObjectSize + " " + readedDataBufferSize + " " + remainginBytes);
if (readedObjectSize != readedDataBufferSize)
   System.out.println("Missmatch");

请记住,将套接字配置为 non-blocking 模式并不能确保您在检索所有数据之前不会 return

如果这仍然不适合您,请随时发表评论!