客户端发送500字节,但服务器接收244字节 - 套接字编程?

时间:2013-10-16 17:30:49

标签: c sockets

我正在尝试为Stop-and-Wait ARQ实施UDP。根据停止等待约定,我在ACK0之间切换1

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

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

客户端

// 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的消息,因此ACK需要0 500。但是,服务器会收到长度为244的邮件,并发送回ACK 0 244。由于它们不匹配,当前的实现只是停在那里。

为什么会出现这种长度差异?

2 个答案:

答案 0 :(得分:3)

在您的客户端,您正在做

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

你想要的是

sendto(sockfd, &sends, slen, 0, addr, addrlen)
由于两个原因,strlen存在问题: (1)你没有空终止缓冲区,更重要的是, (2)您发送的二进制数据可能有一个空字节作为其245字节。

经验法则:从不在二进制数据上使用字符串函数。

答案 1 :(得分: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