0

我目前正在用 C 语言编写一个服务器,该服务器使用 TCP 通过套接字进行通信。客户端应该发送 {filename\n} + {file contents\n},服务器将存储该信息,然后发送成功响应。但是,我们不能确定客户端实际上会在结构化协议中发送正确的信息。

通常在更简单的情况下,我们知道之前要发送的指定字节数,并且可以等到达到指定的字节数。在这种情况下,我们不这样做,目前代码如下所示:

//buffer is a resize-able array, 
//assume it's correct + don't worry about memory leakage for now
resizeablearray_t *buffer;

char data[255];
char retry = 1; 
while (retry) {
   int bytes = read(socketfd, data, 255);
   if (bytes <= 0) {
      fprintf(stderr, "Error %s\n", strerror(errno));
      return;
   }
   push(buffer, data, bytes);
}

因此,我们遇到了一个巨大的问题:我们如何向 c 中的 read() 函数表明我们已经读取了之前调用的所有信息并且我们不应该再次调用它?

read() 函数会阻塞,直到有字节要通过服务器读取。但是,在我们的模型中,如果由于计数不足而继续尝试读取并且缓冲区中没有任何内容,我们将永远等待。

关于如何在另一个 read() 调用之前指示,没有任何关于传入消息及其结构的先前信息,我们可以从 while 循环中中断并停止尝试从套接字读取的任何想法?

4

2 回答 2

2

在现代文件传输协议中,有一个文件大小的指示,或者更直接地,事务有效负载中有多少字节。这允许对方知道将传递多少字节。如果没有传递足够的字节,阅读器需要继续阅读。当所有字节都被传递时,就您所询问的意义而言,没有“额外”的读取调用。

即使发送方动态创建数据,因此在事务开始时不知道总共要发送多少字节,它至少可以报告当前发送中发送了多少字节。当它完成时,它可以发送一个表明它已经完成发送的最终报告。接收器会一直读取,直到它看到这个最终报告。

如果您使用recvcall 而不是read,您将能够传入标志以指示您不希望永远阻止等待,而是希望立即获得是否有任何内容要读取的状态。但是,这是确定传输是否完成的不可靠方法。发件人可能很慢,文件的其余部分很可能会在一段时间后出现。

于 2018-11-29T00:58:17.857 回答
1

如何在两者之间创建一个 [简单/粗略的] 数据包协议:

byte_length(4)|data(byte_length)

完成后,发送方发送一个byte_length设置为 0 的最终数据包(即 EOF 标记)以指示所有数据已发送。

这是一些粗略的代码:

//buffer is a resize-able array,
//assume it's correct + don't worry about memory leakage for now
resizeablearray_t *buffer;

char data[255];
char retry = 1;

void
receive(void)
{
    uint32_t size;
    int bytes;

    while (retry) {

        bytes = read(socketfd,&size,sizeof(size));
        if (bytes <= 0) {
            fprintf(stderr, "Error %s\n", strerror(errno));
            return;
        }

        // got EOF marker
        if (size == 0)
            break;

        size = ntohl(size);

        bytes = read(socketfd, data, size);
        if (bytes <= 0) {
            fprintf(stderr, "Error %s\n", strerror(errno));
            return;
        }
        push(buffer, data, bytes);
    }
}

void
sender(void)
{
    uint32_t size;
    int bytes;

    while (retry) {

        bytes = read(filefd, data, sizeof(data));
        if (bytes == 0)
            break;

        if (bytes < 0) {
            fprintf(stderr, "Error %s\n", strerror(errno));
            return;
        }

        size = htonl(bytes);
        bytes = write(socketfd, &size, sizeof(size));

        bytes = write(socketfd, data, bytes);
        if (bytes <= 0) {
            fprintf(stderr, "Error %s\n", strerror(errno));
            return;
        }
    }

    // send EOF marker
    size = 0;
    write(socketfd,&size,sizeof(size));
}
于 2018-11-29T01:18:31.940 回答