2

我正在尝试实现Stop-and-Wait ARQfor UDP。根据 Stop-and-Wait 约定,我ACK0和之间切换1

正确ACK定义为正确的序列号(01正确的消息长度。

以下片段是我的相关代码部分。

客户端

// transmission function
void str_cli(FILE *fp, int sockfd, long *len, struct sockaddr *addr, int addrlen)
{
    char *buf;
    long lsize, ci;
    char sends[DATALEN];
    struct ack_so ack;
    int n, slen;
    float time_inv = 0.0;
    struct timeval sendt, recvt;
    ci = 0;

    int prev_msg_acked = TRUE;
    int ack_number = 1;

    fseek(fp, 0, SEEK_END);
    lsize = ftell (fp);
    rewind(fp);
    printf("The file length is %d bytes\n", (int)lsize);
    printf("the packet length is %d bytes\n", DATALEN);

    // allocate memory to contain the whole file.
    buf = (char *) malloc(lsize);
    if (buf == NULL)
       exit (2);

    // copy the file into the buffer.
    fread(buf, 1, lsize, fp);

    // the whole file is loaded in the buffer
    buf[lsize] ='\0'; // append the end byte
    gettimeofday(&sendt, NULL); // get the current time
    while(ci <= lsize)
    {
        if (prev_msg_acked) // only transmits when previous message has been acknowledged
        {
            if ((lsize+1-ci) <= DATALEN) // final string
                slen = lsize+1-ci;
            else // send message of length DATALEN
                slen = DATALEN;
            memcpy(sends, (buf+ci), slen);

            /*************** SEND MESSAGE ***************/
            if((n = sendto(sockfd, &sends, strlen(sends), 0, addr, addrlen)) == -1) {
                printf("send error!\n"); // send the data
                exit(1);
            }

            // update the expected ACK number
            if (ack_number == 1)
                ack_number = 0;
            else
                ack_number = 1;

            ci += slen;
        }

        /*************** RECEIVE ACK ***************/
        if ((n = recvfrom(sockfd, &ack, 2, 0, addr, &addrlen)) == -1)
        {
            printf("error when receiving\n");
            exit(1);
        }

        if (ack.num != ack_number || ack.len != slen) // ACK wrong
        {
            printf("%i %i expected\n", ack_number, strlen(sends));
            printf("%i %i received\n", ack.num, ack.len);
            printf("ACK check fails, retransmission...\n");
            prev_msg_acked = FALSE;
        }
        else
            prev_msg_acked = TRUE;
    }
}

服务器端

// transmitting and receiving function
void str_ser(int sockfd, int *ack_number)
{   
    FILE *fp;
    char buf[BUFSIZE];
    char recvs[DATALEN];
    int end = 0, n = 0;
    long lseek = 0;
    struct ack_so ack;

    struct sockaddr_in addr;
    socklen_t len = sizeof(struct sockaddr_in);

    printf("receiving data!\n");

    while(!end)
    {
        // receive the packet
        if ((n = recvfrom(sockfd, &recvs, DATALEN, 0, (struct sockaddr *)&addr, &len)) == -1)
        {
            printf("error when receiving\n");
            exit(1);
        }

        // toggle the ack_number
        if (*ack_number == 1)
            *ack_number = 0;
        else
            *ack_number = 1;

        // if the last bit of the received string is the EoF
        if (recvs[n-1] == '\0')
        {
            end = 1;
            n--;
        }

        memcpy((buf+lseek), recvs, n);
        lseek += n;

        // up to here, successfully received a packet
        // send ACK back
        ack.num = *ack_number;
        ack.len = strlen(recvs);
        printf("%i %i as ACK sent\n", ack.num, ack.len);
        if ((n = sendto(sockfd, &ack, 2, 0, (struct sockaddr *)&addr, len)) == -1)
        {
            printf("ACK send error!\n");
            exit(1);
        }
    }

    if ((fp = fopen ("myUDPreceive.txt", "wt")) == NULL)
    {
        printf("File doesn't exit\n");
        exit(0);
    }

    fwrite (buf, 1, lseek, fp); //write data into file
    fclose(fp);
    printf("A file has been successfully received!\nThe total data received is %d bytes\n", (int)lseek);
}

通过这个实现,我最终得到了以下结果:

客户端结果

$ ./cli localhost
The file length is 59792 bytes
the packet length is 500 bytes
0 500 expected
0 244 received
ACK check fails, retransmission...

服务器端结果

$ ./ser
receiving data!
0 244 as ACK sent

可以看出,客户端发送了一条长度为 的消息500,因此期望ACK0 500。但是,服务器收到一个长度为 的消息244,并返回一个ACK 0 244. 由于它们不匹配,因此当前的实现只是停在那里。

为什么会发生这种长度差异?

4

2 回答 2

3

在您的客户中,您正在做

sendto(sockfd, &sends, strlen(sends), 0, addr, addrlen)

你想要的是

sendto(sockfd, &sends, slen, 0, addr, addrlen)

strlen 存在问题的原因有两个:(1)您没有以空值终止缓冲区,更重要的是,(2)您发送的二进制数据可能有一个空字节作为其 245 字节。

经验法则:永远不要对二进制数据使用字符串函数。

于 2013-10-16T17:46:48.037 回答
2

您可能不应该使用strlen来确定两端数据的长度,因为这将阻止您发送任何二进制数据。此外,在服务器上,如果它是文件的末尾,您似乎希望数据末尾只有一个 0 字节,因此strlen在所有其他情况下,您的调用也会给您带来不可靠的结果。使用返回值recvfrom来确定您收到了多少数据。

您的代码的另一个问题是:

buf = (char *) malloc(lsize);
...
buf[lsize] ='\0'; // append the end byte

您正在分配一个与文件大小相等的内存缓冲区,然后在该内存的末尾设置一个 0 字节,从长远来看,这将导致您出现问题。你应该为此分配一个额外的字节,所以让它:

buf = (char *) malloc(lsize+1);
...
buf[lsize] ='\0'; // append the end byte

更新:

查看完整代码后,我发现了真正的问题:

struct ack_so
{
    uint8_t num; // the sequence number
    uint8_t len; // the packet length
};

ack_so::len是一个 8 位无符号整数,其值范围为 0-255,因此 500 会溢出该值,得到 244。

你应该做len一个uint32_t

于 2013-10-16T17:48:59.777 回答