简体   繁体   English

套接字TCP通信数据包已拆分

[英]Socket TCP Communication packet splitted

I develop a Peer-to-Peer Project, i try to communicate two machines ( A my laptop connected by WiFi and B my NAS connected by Ethernet) on the same network. 我开发了一个对等项目,我尝试在同一网络上通信两台计算机( A我的笔记本电脑通过WiFi连接, B我的NAS通过以太网连接)。 The communication work well but i have an issue when i send a packet of size 1696bytes. 通信工作正常,但是当我发送大小为1696bytes的数据包时出现问题。

So in my sender A i write the block of 1696bytes to B , when i look on Wireshark i see two packets are send one of size 1448bytes and another of size 248bytes (1448+248=1696). 因此,在我的发送方A中,我将1696bytes的块写入B ,当我查看Wireshark时,我看到发送了两个大小为1448bytes的数据包,另一个发送了大小为248bytes的数据包(1448 + 248 = 1696)。

So i think the packet are cut by one protocol or something between the network and physical layer because 1448 can correspond to a MTU. 因此,我认为该数据包被网络和物理层之间的一种协议或某些内容削减,因为1448可以对应于MTU。 And so in my receiver B something weird happen, if i send two times the block of 1696, the first time i will read one block of 1696 even if in Wireshark i see the two splitted blocks, and the second time i read two times for the two splitted blocks... 因此,在我的接收器B中,发生了一些奇怪的事情,如果我发送两次1696的块,即使我在Wireshark中看到了两个拆分的块,我第一次也会读取一个1696的块,而第二次我读了两次两个分开的方块...

For me it's a problem because i need the two blocks together to decrypt them. 对我来说,这是一个问题,因为我需要两个块一起解密。

And when i send from B to A in A and i receive always one block of size 1696bytes, and in Wireshark i see also one block of 1696bytes. 当我从B发送到AA时 ,我总是收到一个大小为1696bytes的块,而在Wireshark中,我也看到一个大小为1696bytes的块。 So maybe the issue come from Ethernet... 所以也许问题出在以太网...

So i don't know which solution is the best, i could read bytes one to one until a size defined like 2048 and so apply a padding to my packet in the sender to have a 2048 packet size. 所以我不知道哪种解决方案是最好的,我可以一对一读取字节,直到定义为2048大小,然后在发送方中对我的数据包应用填充以使其具有2048数据包大小。 Or maybe you have other fix to give me. 或者,也许您还有其他解决方法可以给我。

I hope i was clear, 我希望我很清楚

Thank you, 谢谢,

The Receiver class : 接收器类:

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

The sender class : 发件人类别:

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

TCP is a stream protocol. TCP是流协议。

Writing to a TCP socket is like writing to a file. 写入TCP套接字就像写入文件一样。

Reading from a TCP socket is like reading from a file. 从TCP套接字读取就像从文件读取一样。

A single read() call doesn't correspond to a single send() . 单个read()调用不对应于单个send() Instead, it just reads whatever is available in the TCP buffer at the moment. 相反,它只读取当前TCP缓冲区中可用的任何内容。 Whether it's half a message, a whole message or a hundred messages. 无论是半条消息,整条消息还是一百条消息。

If you want individual messages written to a file to be separate from one another, you'd need some way of telling when a message starts, and when it ends. 如果希望将写入文件的各个消息彼此分开,则需要某种方式来告知消息何时开始以及何时结束。 There are plenty of ways of doing that, here are a couple: 有很多方法可以做到,这里有几个:

  • In text files, one can use a delimiter such as "\\n to separate parts of the file. In a binary file, this is trickier unless you can guarantee that the delimiter won't show up in the middle of a message. The same is true in TCP. You could delimit messages using some special value (eg \\0 ). Then, all you'd need to do on the other side is read from the socket until you see the delimiter. Don't do this if you can't guarantee that your message body doesn't contain the delimiter for obvious reasons. 在文本文件中,可以使用诸如"\\n这样的分隔符来分隔文件的各个部分。在二进制文件中,除非您可以保证该分隔符不会出现在消息中间,否则这将非常棘手。在TCP中为true。您可以使用一些特殊值(例如\\0 )对消息进行定界。然后,从套接字读取另一边需要做的所有事情,直到看到定界符为止。由于明显的原因,不能保证您的邮件正文不包含定界符。

  • Count. 计数。 Prefix each message with, say, an integer (4 bytes) that indicates the length of the message. 给每个消息加上前缀(例如,整数(4个字节)),该整数指示消息的长度。 So if you want to send 01 02 03 aa bb cc dd (hex), send 00 00 00 07 01 02 03 aa bb cc dd . 因此,如果您要发送01 02 03 aa bb cc dd (十六进制),请发送00 00 00 07 01 02 03 aa bb cc dd The receiver would read the first 4 bytes of every message, and figure out the number of bytes it has to read to get the entire message. 接收器将读取每个消息的前4个字节,并弄清为获得整个消息必须读取的字节数。 This does however require the sender to know the length of its message in advance. 但是,这确实需要发送方事先知道其消息的长度。 It's not a problem in most cases, though. 不过,在大多数情况下这不是问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM