【发布时间】:2020-02-23 22:58:42
【问题描述】:
我在接收大小超过 8000+(可能是 8192)字节的 TCP 消息时遇到问题。
有人知道我做错了什么或错过了什么吗?可能是缓冲区或时间问题?
代码(如下)非常适用于低于此“限制”的消息。
消息的前 64 个字节是包含总消息长度的标头。只有头的前 16 个字节是直接可读的。 字节 0 是 0x46 ('F') 此时不处理字节 1 字节 2 和 3 是标头之后的消息长度。 此时不处理字节 4-7 字节 8-15 全为零。 16-63字节和header后的数据加密。
接收者使用序列号确认每条消息。 当任一部分发送新消息时,它会增加它的序列号。 任何一方只有一条消息同时处于活动状态,在发送下一条消息之前,必须确认前一条消息。
如果序列号不相加,则重置通信。
消息大小不同是正常的,从 64 字节到 10k+。
接收大消息时,第一次消息检查(以“F”开头,长度在范围内)失败并导致返回。
bytes 的内容在检查时是垃圾(return 语句处的断点)。
检查IOHandler->InputBuffer 后发现它包含有效数据(不过这是中断后的一段时间)。
使用Wireshark(过滤器:主机,无arp),我看到对方发送的消息是理智的。它被分成 1460 字节的块和一个更小的块,全部是确认,没有其他消息发生。
下面的代码缺少消息解析和加密/解密,但在其他方面有些完整。 为确保正确维护/处理序列号,并优先考虑对远程部分的确认消息,所有接收和发送均通过此方法完成。
void __fastcall TForm1::TCPServerExecute(TIdContext *AContext)
{
try
{
if(AContext->Connection->IOHandler->InputBufferIsEmpty())
AContext->Connection->IOHandler->CheckForDataOnSource(100);
if(!AContext->Connection->IOHandler->InputBufferIsEmpty())
{
try
{
unsigned short msglen;
unsigned char* puch = (unsigned char*)&msglen;
TIdBytes bytes;
TIdBytes Data;
int DataLength;
unsigned char digest[20];
bytes.Length = 64;
AContext->Connection->IOHandler->ReadBytes(bytes,64,false); // read header
puch[0]= bytes[3]; // Endianess
puch[1]= bytes[2];
msglen = msglen & 0x1FF; // block count of data section each block 16 bytes maximum 640
if(bytes[0] != 0x46 || msglen > 640)
return;
Data.Length = msglen * 16;
if(msglen)
AContext->Connection->IOHandler->ReadBytes(Data,DataLength,false); // read data section
TMessage Msg = ValidMessage(bytes, Data);
if(Msg->IsAckMsg)
{
TList* list = txList->LockList();
if(list->Count)
{
for(int i=0; i < list->Count; i++)
{
TMessage* msg = (TMessage*)list->Items[i];
if(Msg->RSeqNo == msg->RSeqNo)
{
list->Delete(i);
delete msg;
break;
}
}
}
txList->UnlockList();
}
else
{
TMessage ACKMsg = Msg->MakeACK();
TIDBytes senbytes;
int Msglen = ACKMsg->Prepare(sendbytes);
AContext->Connection->IOHandler->Write(sendbytes, MsgLen);
rxList->Add(Msg);
LogMsg(ACKMsg);
PostMessage(this->Handle, MSG_UPDATE, NULL, 1);
}
}
catch(Exception& E)
{
}
}
else
{
TMessage* Msg = NULL;
TList* list = txList->LockList();
if(list->Count)
Msg = (TMessage*)list->Items[0];
txList->UnlockList();
if(Msg)
{
if(Msg->fMsgSent)
return;
TIDBytes bytes;
int Msglen = Msg->Prepare(bytes);
AContext->Connection->IOHandler->Write(bytes, MsgLen);
Msg->fMsgSent = true;
}
}
}
catch (Exception& E)
{
}
Wireshark 总结
Message received correct
1 0.000000 192.168.1.202 192.168.1.72 TCP 342 52000 ? 55496 [PSH, ACK] Seq=1 Ack=1 Win=63080 Len=288 // Command request to remote part
2 0.030413 192.168.1.72 192.168.1.202 TCP 117 55496 ? 52000 [PSH, ACK] Seq=1 Ack=289 Win=11680 Len=63 // Command acknowledged by remote part
3 0.031360 192.168.1.72 192.168.1.202 TCP 60 55496 ? 52000 [PSH, ACK] Seq=64 Ack=289 Win=11680 Len=1
4 0.031444 192.168.1.202 192.168.1.72 TCP 54 52000 ? 55496 [ACK] Seq=289 Ack=65 Win=63016 Len=0
5 0.050135 192.168.1.72 192.168.1.202 TCP 1514 55496 ? 52000 [PSH, ACK] Seq=65 Ack=289 Win=11680 Len=1460 // Respons from remote part beginning with 0x46 0x01 0x01 0xee
// Gives total message length 64 + 494*16 = 7968
6 0.051363 192.168.1.72 192.168.1.202 TCP 1514 55496 ? 52000 [PSH, ACK] Seq=1525 Ack=289 Win=11680 Len=1460
7 0.051424 192.168.1.202 192.168.1.72 TCP 54 52000 ? 55496 [ACK] Seq=289 Ack=2985 Win=64240 Len=0
8 0.053059 192.168.1.72 192.168.1.202 TCP 1514 55496 ? 52000 [PSH, ACK] Seq=2985 Ack=289 Win=11680 Len=1460
9 0.053860 192.168.1.72 192.168.1.202 TCP 1514 55496 ? 52000 [PSH, ACK] Seq=4445 Ack=289 Win=11680 Len=1460
10 0.053910 192.168.1.202 192.168.1.72 TCP 54 52000 ? 55496 [ACK] Seq=289 Ack=5905 Win=64240 Len=0
11 0.055217 192.168.1.72 192.168.1.202 TCP 1514 55496 ? 52000 [PSH, ACK] Seq=5905 Ack=289 Win=11680 Len=1460
12 0.056023 192.168.1.72 192.168.1.202 TCP 722 55496 ? 52000 [PSH, ACK] Seq=7365 Ack=289 Win=11680 Len=668 // Last response chunk
13 0.056091 192.168.1.202 192.168.1.72 TCP 54 52000 ? 55496 [ACK] Seq=289 Ack=8033 Win=64240 Len=0
14 0.056428 192.168.1.202 192.168.1.72 TCP 118 52000 ? 55496 [PSH, ACK] Seq=289 Ack=8033 Win=64240 Len=64 // Respons acknowledged
15 0.057674 192.168.1.72 192.168.1.202 TCP 60 55496 ? 52000 [ACK] Seq=8033 Ack=353 Win=11680 Len=0
Message not received correct
1 0.000000 192.168.1.202 192.168.1.72 TCP 342 52000 ? 55501 [PSH, ACK] Seq=1 Ack=1 Win=62952 Len=288 // Command request to remote part
2 0.034937 192.168.1.72 192.168.1.202 TCP 117 55501 ? 52000 [PSH, ACK] Seq=1 Ack=289 Win=11680 Len=63 // Command acknowledged by remote part
3 0.035910 192.168.1.72 192.168.1.202 TCP 60 55501 ? 52000 [PSH, ACK] Seq=64 Ack=289 Win=11680 Len=1
4 0.035961 192.168.1.202 192.168.1.72 TCP 54 52000 ? 55501 [ACK] Seq=289 Ack=65 Win=62888 Len=0
5 0.056781 192.168.1.72 192.168.1.202 TCP 1514 55501 ? 52000 [PSH, ACK] Seq=65 Ack=289 Win=11680 Len=1460 // Respons from remote part beginning with 0x46 0x01 0x02 0x33
// Gives total message length 64 + 563*16 = 9072
6 0.058048 192.168.1.72 192.168.1.202 TCP 1514 55501 ? 52000 [PSH, ACK] Seq=1525 Ack=289 Win=11680 Len=1460
7 0.058083 192.168.1.202 192.168.1.72 TCP 54 52000 ? 55501 [ACK] Seq=289 Ack=2985 Win=64240 Len=0
8 0.059735 192.168.1.72 192.168.1.202 TCP 1514 55501 ? 52000 [PSH, ACK] Seq=2985 Ack=289 Win=11680 Len=1460
9 0.060575 192.168.1.72 192.168.1.202 TCP 1514 55501 ? 52000 [PSH, ACK] Seq=4445 Ack=289 Win=11680 Len=1460
10 0.060604 192.168.1.202 192.168.1.72 TCP 54 52000 ? 55501 [ACK] Seq=289 Ack=5905 Win=64240 Len=0
11 0.062111 192.168.1.72 192.168.1.202 TCP 1514 55501 ? 52000 [PSH, ACK] Seq=5905 Ack=289 Win=11680 Len=1460
12 0.063037 192.168.1.72 192.168.1.202 TCP 1514 55501 ? 52000 [PSH, ACK] Seq=7365 Ack=289 Win=11680 Len=1460
13 0.063061 192.168.1.202 192.168.1.72 TCP 54 52000 ? 55501 [ACK] Seq=289 Ack=8825 Win=64240 Len=0
14 0.063804 192.168.1.72 192.168.1.202 TCP 365 55501 ? 52000 [PSH, ACK] Seq=8825 Ack=289 Win=11680 Len=311
15 0.063805 192.168.1.72 192.168.1.202 TCP 60 55501 ? 52000 [PSH, ACK] Seq=9136 Ack=289 Win=11680 Len=1 // Last response chunk
16 0.063839 192.168.1.202 192.168.1.72 TCP 54 52000 ? 55501 [ACK] Seq=289 Ack=9137 Win=63928 Len=0
No acknowledge as meesage not received correct.
【问题讨论】:
-
在不知道您尝试实现的实际协议的情况下很难诊断您的问题。消息的实际格式是什么,尤其是 64 字节的标头?而不是使用
ReadBytes()并手动移动字节,您应该使用更高级别的方法,例如TIdIOHandler.ReadInt16(),它会为您处理字节序。而且您绝对不应该像现在这样使用InputBufferIsEmpty()和CheckForDataOnSource()。这不是需要这些电话的那种情况。让ReadBytes()(或其他)阻塞,直到收到完整的请求数据 -
您也没有包含试图在 64 字节标头之后读取消息剩余数据的代码。那么我们怎么知道代码没有留下破坏后续消息读取的字节呢?您需要提供minimal reproducible example,以便我们查看所有内容。
-
我已经编辑了帖子并添加了更多代码,并且可能 CheckForData... 等更有意义。
-
您没有显示无法读取的实际数据。如果没有数据包数据,您的 Wireshark 摘要将毫无用处。但我觉得奇怪的一件事是你使用了
msglen & 0x1FF,这意味着msglen > 640将永远为真,因为msglen被限制为最大511。您将msglen描述为消息长度,但实际上您将其视为块计数器,表示消息中的多个 16 字节块,这似乎与 Wireshark 摘要中的数据包长度不符。您是否有任何关于此协议真正如何工作的文档? -
我在这段代码中看到了很多逻辑错误,其中最少的是不要捕获和丢弃 Indy 异常,但您还需要使用
try..finally或 RAII 来锁定/解锁您的列表安全,并且您有一些潜在的内存泄漏。
标签: delphi c++builder indy