【发布时间】:2019-07-13 08:47:06
【问题描述】:
我正在使用 UDP 实现“停止和运行”协议。我基本上使用 char 缓冲区来序列化我发送的任何数据,在需要的地方使用 ntoh 和 hton,并在我的数据到达另一端时反序列化。一切都很好,但是当我尝试发送文件时,我在新文件中得到随机的虚假字节。我将尝试总结这个问题的必要信息:
我的 char 缓冲区中的第一个字符表示正在发送的数据包的类型。我实现了一个小的 3 次握手来建立连接。服务器只需要一个特定的端口作为参数,客户端需要一个 ip、端口和文件名,然后从服务器请求这个文件。服务器响应文件的大小,然后客户端发送最终的 ACK 数据包,然后发送数据。我使用 fread() 一次发送数据 ~1kb,使用 1kb 缓冲区大小。一旦客户端获得这个数据包,它就会发送一个确认数据包,然后等待下一个数据包。以下是相关代码:
else if (b == 'a')
{
memcpy(&dacpkt.seqNum, buffer+sizeof(char), sizeof(int));
dacpkt.seqNum = ntohl(dacpkt.seqNum); // convert from network to host data
datapkt.pktLen = 1017;
if (dacpkt.seqNum == datapkt.seqNum)
{
++seq;
dacpkt.t = b;
datapkt.seqNum = seq;
fread(datapkt.data, datapkt.pktLen, 1, filereq);
memcpy(buffer, &datapkt.t, sizeof(char)); // data packet type
off = sizeof(char);
datapkt.seqNum = htonl(datapkt.seqNum);
memcpy(buffer + off, &datapkt.seqNum, sizeof(int)); // data packet sequence#
off += sizeof(int);
datapkt.pktLen = htons(datapkt.pktLen);
memcpy(buffer + off, &datapkt.pktLen, sizeof(short)); // data packet size
off += sizeof(short);
memcpy(buffer + off, &datapkt.data, 1017); // data packet payload
n = sendto(sock, buffer, MAX, 0, (struct sockaddr*) &from, fromlen);
if (n < 0)
error("sendto");
}
if(fread(datapkt.data, 1017, 1, filereq) != 1017) // fread hit the end of the file
{
++seq;
dacpkt.t = 'c';
dacpkt.seqNum = seq;
memcpy(buffer, &dacpkt.t, sizeof(char)); // close packet
off = sizeof(char);
dacpkt.seqNum = htonl(dacpkt.seqNum);
memcpy(buffer + off, &dacpkt.seqNum, sizeof(int)); // close packet sequence#
n = sendto(sock, buffer, 5, 0, (struct sockaddr*) &from, fromlen);
}
}
dacpkt 是我在从 recvfrom() 反序列化 char 缓冲区时填充的确认数据包,而 datapkt 是我构建的数据包,然后序列化并发送。我相信我的问题在于我对 fread 的使用。这是客户端的接收代码:
if (b == 'd')
{
//build the response packet and send
memset(buffer, 0, MAX);
//populate struct
dacpkt.t = 'a';
dacpkt.seqNum = htonl(sequence_num);
//copy to buffer
memcpy(buffer, &dacpkt.t, sizeof(char));
off = sizeof(char);
memcpy(buffer + off, &dacpkt.seqNum, sizeof(int));
//send the ACK
n = sendto(sock, buffer, MAX, 0, (struct sockaddr *) &server, length);
if (n < 0)
error("sendto");
//receive as well before exiting while loop
memset(buffer, 0, MAX);
n = recvfrom(sock, buffer, MAX, 0, (struct sockaddr *) &from, &length);
if (n < 0)
error("recvfrom");
datapkt.t = buffer[0];
off = sizeof(char);
memcpy(&datapkt.seqNum, buffer + off, sizeof(int));
datapkt.seqNum = ntohl(datapkt.seqNum);
off += sizeof(int);
memcpy(&datapkt.pktLen, buffer + off, sizeof(short));
datapkt.pktLen = ntohl(datapkt.pktLen);
off += sizeof(short);
strcat(datapkt.data, buffer + off);
//ensures we are receiving the next packet
if (datapkt.seqNum = (sequence_num + 1))
{
n = fputs(datapkt.data, newfile); //we need buffer to be the exact size
if (n < 0)
error("writing to file");
sequence_num = datapkt.seqNum;
}
}
fclose(newfile);
}
//post-receiving the file!
memset(buffer, 0, MAX);
dacpkt.t = 'c';
dacpkt.seqNum = htonl(sequence_num);
memcpy(buffer, &dacpkt.t, sizeof(char));
memcpy(buffer + 1, &dacpkt.seqNum, sizeof(int));
//send the ACK
n = sendto(sock, buffer, MAX, 0, (struct sockaddr *) &server, length);
if (n < 0)
error("sendto");
如果我提供的信息有点模棱两可,我很抱歉,但我相当肯定我的问题在于我对文件的读/写。例如,我运行程序尝试传输每行包含字母字符 (a-z) 的文本文件,共 600 行。原始文件为 16200 字节,而发送数据创建的新文件只有 3069 字节。新文件只有 113 行,随机字节出现如下:
36 abcdefghijklmnopqrstuvwxyz
37 abcdefghijklmnopqrstuvwxyz
38 abcdefghijklmnopqr8ze$ü^?abcdefghijklmnopqrstuvwxyz
39 abcdefghijklmnopqrstuvwxyz
值得注意的是,在文件末尾:
111 abcdefghijklmnopqrstuvwxyz
112 abcdefghijklmnopqrstuvwxyz
113 abcdefghi8ze$ü^?
关于如何判断何时结束文件传输,我想我不明白 fread() 的工作原理。如果有人对我如何更好地实现这一点有想法,我会全力以赴。我要做的就是一次发送一个 kb 数据包中的数据,直到到达文件末尾,此时我发送这个最终的部分数据包,然后发送一个信号以关闭客户端。
【问题讨论】: