接收多个TCP段

时间:2017-04-30 14:17:46

标签: c sockets tcp

我有一个分配,其中TCP客户端以以下形式向TCP服务器发送数据:

IP_地址\ 0port \ 0message \ n

现在,当我通过这样的终端发送一些数据时,服务器(IP地址10.0.2.15)正好收到数据包:

printf "127.0.0.1\0004444\000Some message\n" | nc -N 10.0.2.15 3333

但是,分配的第二部分是读取多个段中的数据包:

(printf "127.0.0.1"; sleep 0.3; printf "\0004444\000"; sleep 0.3; \
printf "It works"; sleep 0.5; printf "\n") | nc -N 10.0.2.15 3333

如何在服务器上实现读取功能,以便在可能的情况下将所有段存储到缓冲区中?

1 个答案:

答案 0 :(得分:1)

字节recv()返回的字节数可以少至1个字节,直到所请求的字节数。 TCP是一个字节流,它没有消息的概念,而是必须在应用程序代码中处理。

接收器必须知道预期的字节数,然后继续读取循环,直到它读取了很多字节,无论多少次读取。

然而,在这种情况下,接收方不知道消息的确切长度,因为发送方在发送消息本身之前没有发送消息长度,因此唯一可用的选项是接收方从套接字读取逐个字节,直到遇到终止\n

例如:

int readLine(int socket, char **line)
{
    int r, len = 0, cap = 256;
    char b;

    *line = NULL;

    char *outline = (char*) malloc(cap);
    if (!outline) return -2;

    do
    {
        r = recv(socket, &b, 1, 0);
        if (r <= 0)
        {
            free(outline);
            return r;
        }

        if (b == '\n')
            break;

        if (len == cap)
        {
            cap += 256;
            char *newline = (char*) realloc(outline, cap);
            if (!newline)
            {
                free(outline);
                return -2;
            }
            outline = newline;
        }

        outline[len] = b;
        ++len;
    }
    while (true);

    if ((len > 0) && (line[len-1] == '\r'))
        --len;

    if (len == cap)
    {
        char *newline = (char*) realloc(outline, cap + 1);
        if (!newline)
        {
            free(outline);
            return -2;
        }
        outline = newline;
    }

    outline[len] = '\0';

    *line = outline;

    return 1;
}

char *line;
int r;

do
{
    r = readLine(cliSock, &line);
    if (r <= 0)
    {
        if (r == 0)
            printf("client disconnected\n");
        else if (r == -2)
            printf("memory error\n");
        else
            printf("read error\n");
        break;
    }

    // process line as needed...

    free(line);
}
while (true);

或者,您可以使用中间缓冲区来帮助您在读取之间缓存数据并更有效地从套接字中获取数据:

char *buffer;
int buflen, bufcap;

int readLine(int socket, char **line)
{
    char *ptr;
    int r, idx = 0;

    *line = NULL;

    do
    {
        ptr = memchr(buffer + idx, '\n', buflen - idx);
        if (ptr)
        {
            int total = ((ptr + 1) - buffer);

            int len = (total - 1);
            if ((len > 0) && (buffer[len-1] == '\r'))
                --len;

            *line = (char*) malloc(len + 1);
            if (*line == NULL)
                return -2;

            memcpy(*line, buffer, len);
            (*line)[len] = '\0';

            if (total < buflen)
                memmove(buffer, buffer + total, buflen - total);
            buflen -= total;

            break;
        }

        if (buflen == bufcap)
        {
            int newcap = bufcap + 256;
            char *newbuffer = (char*) realloc(buffer, newcap);
            if (!newbuffer)
                return -2;
            buffer = newbuffer;
            bufcap = newcap;
        }

        r = recv(socket, buffer + buflen, bufcap - buflen, 0);
        if (r <= 0)
            return r;

        buflen += r;
    }
    while (true);

    return 1;
}

buflen = 0;
bufcap = 256;
buffer = (char*) malloc(bufcap);
if (buffer)
{
    char *line;
    int r;

    do
    {
        r = readLine(cliSock, &line);
        if (r <= 0)
        {
            if (r == 0)
                printf("client disconnected\n");
            else if (r == -2)
                printf("memory error\n");
            else
                printf("read error\n");
            break;
        }

        // process line as needed...

        free(line);
    }
    while (true);

    free(buffer);
}