繁体   English   中英

tcp服务器阻止读取C

[英]tcp server blocking read in C

我想实现一个带有阻止读取的简单TCP服务器,该服务器接收从客户端一个字符接一个字符发送的消息,直到分隔符为止。 收到消息后,它必须等到下一条消息出现。 这是我的伪代码:

// Messages sent from the client
char *message1 = "mssg1\n"
char *message2 = "mssg2\n"

// On server side
char buffer;
char completeMessage[5]

while(1){

    while(buffer != '\n'){
        recv(sock, &buffer, 1, 0); // 1 is the read size

        if(buffer != '\n') {
            printf("buffer: %c\n", buffer);
            completeMessage[n] = buffer;
            count ++;  
        }
        else{
            printf("Complete message: %s\n", completeMessage);
            count = 0;
        }
    }
}

结果如下:

buffer: m
buffer: s
buffer: s
buffer: g
buffer: 1
Complete message: mssg1
buffer:
buffer:
buffer:
buffer:
buffer:
buffer:
// Error due to buffer overflow

我不知道为什么recv而不是等待下一个消息字符(阻止读取),而是继续读取空格。 我的问题如下:

  • recv真的是套接字阻止读取功能吗?
  • 代码中是否有错误或遗漏?
  • 还有其他建议可以实施吗?

recv真的是套接字阻止读取功能吗?

是的,除非您使句柄不阻塞。

代码中是否有错误或遗漏?

  • 您没有检查recv返回什么。 0表示EOF, -1表示错误。

  • 您无需检查缓冲区的容量,因此有可能缓冲区溢出。

  • 您不会像printf %s所要求的那样用NUL终止completeMessage的字符串。

还有其他建议可以实施吗?

您不应该一次读一个字符!


#define BUFFER_SIZE (64*1024)

char* extract_string(const char* start, const char* end) {
    size_t len = end - start;
    char* dst = malloc(len+1);
    if (dst == NULL)
        return NULL;

    memcpy(dst, src, len);
    dst[len] = '\0';
    return dst;
}

{
    char buf_start[BUFFER_SIZE];
    char* buf_end = buf_start + BUFFER_SIZE;
    char* window_start = buf_start;
    char* window_end = buf_start;
    while (1) {
        if (window_end == buf_end) { // No more space.
            fprintf(stderr, "Overly large message");
            return 0;
        }

        ssize_t rv = recv(sock, window_end, buf_end-window_end, 0);
        if (rv == -1) {  // Error.
            perror("recv");
            return 0;
        }

        if (rv == 0) {  // EOF.
            return 1;
        }

        while (rv--) {
            if (*(window_end++) == '\n') {
                char* msg = extract_string(window_start, window_end-1);  // Excl LF.
                if (msg == NULL) {
                    fprintf(stderr, "Out of memory");
                    return 0;
                }

                // Do something with msg
                printf("Complete message: %s\n", msg);
                free(msg);

                window_start = window_end;
            }
        }

        memmove(buf_start, window_start, window_end-window_start);
        window_end -= (window_start - buf_start);
        window_start = buf_start;
    }
}

您的代码有很多问题,即您忽略了recv()的返回值,在打印buffer之前没有将buffer终止为空,并且没有保护自己免受缓冲区溢出的影响。

尝试类似这样的方法:

char ch, *tmp, *message = NULL;
int ret, length = 0, allocated = 0;

while (1)
{
    ret = recv(sock, &ch, 1, 0);
    if (ret <= 0)
    {
        if (ret < 0)
            printf("Read error: %d\n", errno); // or WSAGetLastError() on Windows
        else
            printf("Client disconnected\n");
        break;
    }

    if (ch == '\n')
    {
        if ((length > 0) && (message[length-1] == '\r'))
          --length;

        printf("Complete message: '%.*s'\n", length, message);    

        length = 0;
    }
    else
    {
        printf("ch: %c\n", ch);

        if (length == allocated)
        {
            if (length >= 5000) // some max length of your choosing...
            {
                printf("Message length too large!\n");
                break;
            }

            // just for example. You should use a more robust growth algorithm in production code...
            tmp = (char*) realloc(message, allocated + 10);
            if (!tmp)
            {
                printf("Memory allocation failed\n");
                break;
            }

            message = tmp;
            allocated += 10;
        }

        message[length] = ch;
        ++length;
    }
}

free(message);

或者,不要按字符阅读。 从任何给定读取的套接字中读取尽可能多的数据,并将其全部存储在不断增长的缓冲区中,然后扫描该缓冲区以获取完整的消息,例如:

char *buffer = (char*) malloc(100);
if (!buffer)
{
    printf("Memory allocation failed\n");
}
else
{
    int ret, offset, remaining, inbuf = 0, allocated = 100;
    char *ptr;

    while (1)
    {
        if (inbuf == allocated)
        {
            if (inbuf >= 5000) // some max length of your choosing...
            {
                printf("Buffer length too large!\n");
                break;
            }

            // just for example. You should use a more robust growth algorithm in production code...
            tmp = (char*) realloc(buffer, allocated + 100);
            if (!tmp)
            {
                printf("Memory allocation failed\n");
                break;
            }

            buffer = tmp;
            allocated += 100;
        }

        ret = recv(sock, buffer+inbuf, allocated-inbuf, 0);
        if (ret <= 0)
        {
            if (ret < 0)
                printf("Read error: %d\n", errno); // or WSAGetLastError() on Windows
            else
                printf("Client disconnected\n");
            break;
        }

        printf("Received: %.*s\n", ret, buffer+inbuf);
        inbuf += ret;

        while (ptr = (char*)memchr(buffer, '\n', inbuf))
        {
            offset = (ptr-buffer);
            if ((offset > 0) && (buffer[offset-1] == '\r'))
                --offset;

            printf("Complete message: '%.s'\n", offset, buffer);    

            ++ptr;
            remaining = (inbuf - (ptr - buffer));
            if (remaining > 0)
                memmove(buffer, ptr, remaining);
            inbuf = remaining;
        }
    }

    free(buffer);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM