套接字TCP通信数据包分裂

时间:2017-02-26 15:14:51

标签: java sockets tcp network-programming

我开发了一个点对点项目,我尝试通信两台机器( A 我的笔记本电脑通过WiFi连接,而 B 我的NAS通过以太网连接)相同的网络。通信工作很好,但是当我发送一个1696字节的数据包时,我有一个问题。

因此,在我的发件人 A 中,我将1696字节的块写入 B ,当我查看Wireshark时,我看到两个数据包发送的大小为1448字节,另一个是大小248字节(1448 + 248 = 1696)。

所以我认为数据包是由网络和物理层之间的一种协议切断的,因为1448可以对应于MTU。所以在我的接收器中 B 发生了一些奇怪的事情,如果我发送两次1696块,第一次我将读取一个1696块,即使在Wireshark中我看到两个分块,并且第二次我为两个分裂的块读了两次......

对我来说这是一个问题因为我需要两个块来解密它们。

当我从 B 发送到 A 中的 A 时,我总是会收到一个大小为1696字节的块,并且在Wireshark中我也看到了一块1696字节。所以问题可能来自以太网......

所以我不知道哪种解决方案是最好的,我可以一对一地读取字节,直到大小定义为2048,因此在发送器中对我的数据包应用填充以具有2048数据包大小。或者也许你还有其他方法可以帮我。

我希望我很清楚,

谢谢,

接收者类:

public abstract class DataReceiver implements Runnable {
protected ConnectionType    type;
protected AsymmetricEncryption as;
protected Socket            socket;
protected FIFOQueue         buffer;
protected int               dataPacketSize;

protected abstract IDataStructure dataFormatter(byte[] data, int len) throws NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, InvalidAlgorithmParameterException, BadPaddingException;

public DataReceiver(ConnectionType type, Socket socket, FIFOQueue buffer, AsymmetricEncryption as) {
    this.type = type;
    this.socket = socket;
    this.buffer = buffer;
    this.dataPacketSize = 2048;
    this.as = as;
}

public int waitData() {
    try {
        System.out.println(socket.getPort() + " : I wait data from " + socket.getInetAddress().getHostAddress());
        byte[] data = new byte[dataPacketSize];
        int len;
        IDataStructure dataStructure;
        while ((len = socket.getInputStream().read(data, 0, data.length)) != -1) {
            try {
                if ((dataStructure = dataFormatter(data, len)) == null) {
                    System.err.println("Error: bad data format.");
                } else if (type == ConnectionType.EXTRA) {
                    ((ExtraDataStructure)dataStructure).getContent().setParam();
                    buffer.putData(dataStructure);
                } else if (type == ConnectionType.INTRA) {
                    ((IntraDataReceiver)this).getRqManager().doRequest((IntraDataStructure)dataStructure, buffer, socket);
                }
                Arrays.fill(data, (byte)0);
            } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | JsonSyntaxException | InvalidAlgorithmParameterException e) {
                e.printStackTrace();
            }
            System.out.println("New data received from " + socket.getInetAddress().getHostAddress());
            //dumpBuffer();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return (0);
}

    @Override
    public void run() {
        waitData();
    }
}

发件人类:

public class ExtraDataDispatcher extends DataDispatcher {

public ExtraDataDispatcher(AsymmetricEncryption as, NodeIdentifier nodeId) {
    super(ConnectionType.EXTRA, as, nodeId);
}

@Override
public boolean dispatchData(IDataStructure data, Socket destSocket) throws IOException {
    System.out.println("HERE");
    destSocket.getOutputStream().write(new Gson().toJson(((ExtraDataStructure)data).getContent()).getBytes());
    return false;
}
}

1 个答案:

答案 0 :(得分:3)

TCP是一种流协议。

写入TCP套接字就像写入文件一样。

从TCP套接字读取就像从文件中读取一样。

单个read()来电与单个send()不对应。相反,它只是读取TCP缓冲区中可用的任何内容。无论是半信息,整条信息还是一百条消息。

如果您希望写入文件的单个消息彼此分离,则需要某种方式来告知消息何时开始以及何时结束。有很多方法可以做到这一点,这里有几个:

  • 在文本文件中,可以使用"\n等分隔符来分隔文件的各个部分。在二进制文件中,这是比较棘手的,除非您可以保证分隔符不会显示在消息的中间。 TCP中也是如此。您可以使用某些特殊值(例如\0)来分隔邮件。然后,从插槽中读取另一侧需要做的所有操作,直到看到分隔符为止。如果由于显而易见的原因无法保证您的邮件正文不包含分隔符,请不要这样做。

  • 计数。例如,使用表示消息长度的整数(4个字节)作为前缀。因此,如果您想发送01 02 03 aa bb cc dd(十六进制),请发送00 00 00 07 01 02 03 aa bb cc dd。接收器将读取每个消息的前4个字节,并计算出为获取整个消息而必须读取的字节数。但是,这确实要求发送者事先知道其消息的长度。但在大多数情况下,这不是问题。

相关问题