简体   繁体   中英

tcp server blocking read in C

I want to implement a simple TCP server with blocking read, that receives messages sent from a client character by character until a separator. Once a message is received, it has to wait until the next message appears. Here is my pseudocode:

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

And the result is the following:

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

I don't know why recv instead of waiting for the next message character (blocking read), it continues reading blank spaces. My questions are the following:

  • Is recv really a socket blocking read function?
  • Is there something wrong or missing in the code?
  • Any other suggestions for implementing this?

Is recv really a socket blocking read function?

Yes, unless you made the handle non-blocking.

Is there something wrong or missing in the code? ,

  • You're not checking what recv returns. 0 indicates EOF, and -1 indicates an error.

  • You don't check how full your buffer is, so you risk buffer overflows.

  • You're not terminating the string in completeMessage with a NUL as required by printf %s .

Any other suggestions for implementing this?

You shouldn't read a character at a time!


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

There are quite a number of problems with your code, namely that you are ignoring the return value of recv() , you are not null-terminating your buffer before printing it, and you are not protecting yourself from a buffer overflow.

Try something more like this instead:

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

Alternatively, don't read char-by-char. Read as much data as you can from the socket on any given read and store it all in a growing buffer, and then scan that buffer for complete messages, eg:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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