tcp服务器阻塞读取C

时间:2017-05-11 18:51:14

标签: c sockets tcp

我想实现一个带有阻塞读取的简单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真的是套接字阻塞读取功能吗?
  • 代码中是否有错误或遗漏?
  • 有任何其他建议吗?

2 个答案:

答案 0 :(得分:2)

  

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

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

  

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

  • 您没有检查recv返回的内容。 0表示EOF,-1表示错误。

  • 您不会检查缓冲区的容量,因此存在缓冲区溢出的风险。

  • 您没有按照completeMessage的要求使用NUL终止printf %s中的字符串。

  

有任何其他建议吗?

你不应该一次读一个角色!

#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;
    }
}

答案 1 :(得分:2)

您的代码存在很多问题,即您忽略了recv()的返回值,在打印之前,您不是在终止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-by-char。在任何给定的读取中从套接字读取尽可能多的数据并将其全部存储在增长的缓冲区中,然后扫描该缓冲区以获取完整的消息,例如:

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);
}