使用fread通过UDP发送数据的问题

时间:2019-02-19 18:57:24

标签: c sockets fread

我正在努力使用UDP实现“停走”协议。实际上,我基本上使用char缓冲区来序列化我发送的任何数据,在需要时使用ntoh和hton,并在数据到达另一端时反序列化。一切正常,但是当我尝试发送文件时,我在新文件中得到了随机的伪造字节。我将尝试总结该问题的必要信息:

char缓冲区中的第一个字符表示要发送的数据包的类型。我实现了一个小的3向握手来建立连接。服务器仅将特定端口作为arg,客户端将获取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");

很抱歉,如果我提供的信息有点模棱两可,但我可以肯定的是我的问题在于我对文件的读/写。例如,我运行了该程序,尝试在600行中的每一行上传输带有字母字符(a-z)的文本文件。原始文件为16200字节,而发送数据创建的新文件仅为3069字节。新文件只有113行,整个过程中会出现随机字节,如下所示:

 36 abcdefghijklmnopqrstuvwxyz
 37 abcdefghijklmnopqrstuvwxyz
 38 abcdefghijklmnopqr8ze$ü^?abcdefghijklmnopqrstuvwxyz
 39 abcdefghijklmnopqrstuvwxyz

尤其是在文件末尾:

111 abcdefghijklmnopqrstuvwxyz
112 abcdefghijklmnopqrstuvwxyz
113 abcdefghi8ze$ü^?

关于如何判断何时结束文件传输,我认为我不了解fread()的工作原理。如果有人对如何更好地实现这一点有想法,我会很高兴。我要做的就是一次将数据发送到一个kb数据包中,直到到达文件末尾为止,此时,我将发送此最终的部分数据包,然后发送信号以关闭客户端。

1 个答案:

答案 0 :(得分:0)

两件事立即发生。

  • fread返回读取的项目数量fread(datapkt.data, 1017, 1, filereq)尝试读取大小为1017的一个项。此调用不可能返回1017。它只能返回0或1。测试

        if (fread(....) != 1017)
    

    总是失败。但是,已读取1017个字节,并且由于测试失败而被丢弃。这说明了巨大的数据丢失。

    交换参数顺序,检查如何读取1字节项目:

        size_t bytes = fread(datapkt.data, 1, 1017, filereq);
    

    ,并使用此bytes作为有效负载的大小。 bytes < 1017可能表示文件结束。可以肯定的是,请致电feofferror

  • 服务器流:

        if (dacpkt.seqNum == datapkt.seqNum) {
            ....
            fread(...);
            ....
        }
        if (fread(....) != 1017) {
            ....
        }
    

    表示一旦fread匹配,将有{em>两次调用seqNum,如果没有则一次调用。我不能真正跟踪数据流,但是确实感到很奇怪。在收到确认之前,服务器不得继续发送新的seqNum

    在任何情况下,都不应盲目发送MAX字节。前一个fread还必须检查它获得了多少个字节,并相应地固定memcpysendto中的大小。


要_detect EOF并同时读取数据,请尝试类似的操作

    size_t bytes = fread(datapkt.data, 1, 1017, filereq);
    if (bytes == 0 && feof(filereq)) {
        handle_end_of_file();
    } else {
        memcpy(memcpy(buffer + off, &datapkt.data, bytes);
        send_the_packet();
    }