Thread: How to use recv() to get all data for non blocking sockets?

  1. #1
    Registered User
    Join Date
    Jul 2015
    Posts
    33

    How to use recv() to get all data for non blocking sockets?

    I'm trying to get messages of varying lengths, and I've already set the socket to non blocking and using select() to check if it's ready for reading. But I got ERROR recv(): Resource temporarily unavailable.

    Code:
    ...
    if (FD_ISSET(i, &read_fds_list)) {
        // retrieve message
        memset(&buffer, '\0', MAX_BUFFER);
        read_message(i, buffer);
                        
        printf("%s\n", buffer);
    }
    
    
    ----------
    
    void read_message(int socket_fd, char message[]) {
        char data[1];
        memset(&data, '\0', 1);
        
        data_read = recv(socket_fd, data, 1, 0);
        
        // read and store into message until new line
        // new line indicates end of message
        while ((data_read != -1) && (strcmp(data, "\n")) != 0) {
            strcat(message, data);
            memset(&data, '\0', 1);
            data_read = recv(socket_fd, data, 1, 0);
        }
        
        if (data_read == -1) {
            exit_err("CLIENT: ERROR recv()\n");
        }
    }
    I tried adding another select() call, but it just froze up and doesn't continue.
    Last edited by jjwooyoung; 02-12-2017 at 01:19 PM.

  2. #2
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    You should call perror to see what the error is:
    Code:
        void read_message(int socket_fd, char *message) {
            char data;
            ssize_t data_read;
    
            while ((data_read = recv(socket_fd, &data, 1, 0)) > 0 && data != '\n')
                *message++ = data;
             
            if (data_read == -1) {
                perror("CLIENT: ERROR recv()");
                exit(EXIT_FAILURE);
            }
    
            *message = '\0';
        }
    I've kept your single-character-at-a-time code (slightly re-written), even though it doesn't really make sense to read one character at a time.

  3. #3
    Registered User
    Join Date
    Jul 2015
    Posts
    33
    My exit_err() calls perror(), which printed out "Resource temporarily unavailable" as I mentioned above. I'm reading one character at a time because my messages are getting concatenated together.

    E.g
    Let's say two messages are sent like so
    > hello there
    > how are you

    But recv() reads them based on the buffer size, so because the messages all vary in length I end up getting messages like this printed out:
    > hello therehow are you

    Is there a better way to solve this?
    Last edited by jjwooyoung; 02-12-2017 at 02:27 PM.

  4. #4
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Quote Originally Posted by jjwooyoung View Post
    My exit_err() calls perror(), which printed out "Resource temporarily unavailable" as I mentioned above. I'm reading one character at a time because my messages are getting concatenated together.
    I see. Sorry for not paying closer attention.

    I guess it's a little involved to do what you want while reading more than one char at a time. I may give it a shot. Still, that's not your problem ....

  5. #5
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Here's a first attempt. I'm using read instead of recv (which you can do too since you're not using the flag parameter). It's reading from a file for testing. It's lacking a bound on the message array(!).
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    void exit_err(const char *msg) {
        perror(msg);
        exit(EXIT_FAILURE);
    }
    
    // Assumes message is large enough to hold the newline-delimited data.
    size_t read_message(int socket_fd, char *message) {
        static char data[10];  // small size for testing
        static char *newline = NULL;
        static ssize_t data_read;
        size_t i = 0;  // message index
    
        // if newline is non-NULL there is data in the buffer
        if (newline) {
            newline++; // won't be accessed if beyond data bounds
            char *p = memchr(newline, '\n', data + data_read - newline);
            i = (p ? p : data + data_read) - newline;
            memcpy(message, newline, i);
            newline = p;
        }
    
        while (!newline && (data_read = read(socket_fd, data, sizeof data)) > 0) {
            newline = memchr(data, '\n', sizeof data);
            size_t additional = newline ? newline - data : data_read;
            memcpy(message + i, data, additional);
            i += additional;
        }
        message[i] = '\0';
    
        if (data_read == -1) exit_err("read_message");
    
        return i;
    }
    
    int main() {
        char mess[100];
    
        int fd = open("data.txt", O_RDONLY);
        if (fd == -1) exit_err("open file");
    
        size_t sz;
        while ((sz = read_message(fd, mess)) != 0)
            printf("%3zu [%s]\n", sz, mess);
    
        close(fd);
        return 0;
    }

  6. #6
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    Code:
        char data[1];
        memset(&data, '\0', 1);
         
        data_read = recv(socket_fd, data, 1, 0);
         
        // read and store into message until new line
        // new line indicates end of message
        while ((data_read != -1) && (strcmp(data, "\n")) != 0) {
            strcat(message, data);
            memset(&data, '\0', 1);
    Well this is all broken because a single char can never be a valid C string that you can use with strcmp, strcat etc.
    You might have more success with
    Code:
    char data[2] = { 0 };
    Also, the memset calls add nothing.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  7. #7
    Registered User
    Join Date
    Jul 2015
    Posts
    33
    Quote Originally Posted by Salem View Post
    Well this is all broken because a single char can never be a valid C string that you can use with strcmp, strcat etc.
    Also, the memset calls add nothing.
    So what's a good way to get all data from varied length messages?

    Also, how do chat applications (e.g skype, slack, etc) implement the message relaying so that it's not blocking and also displays immediately? My program seems to have some sort of delay when displaying what the other user sent - meaning it only displays message of user A after user B sends x number of messages (hope that makes sense).

  8. #8
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    So you start with this.
    Code:
    if (FD_ISSET(i, &read_fds_list)) {
        // retrieve message
        buffused = 0;
        int valid = read_message(i, buffer,MAX_BUFFER-1,&buffused);
        if ( buffused > 0 ) {
          buffer[buffused] = '\0';
          printf("%s\n", buffer);
          // or just this by itself
          // printf("%.*s\n", buffused, buffer);
        }
        if ( valid < 0 ) {
          // error
        } else if ( valid == 0 ) {
          // remote disconnected
        }
    Not all socket streams are printable data, so a lower level routine doesn't care about adding \0 to the buffer.
    Consider it to be essentially recv() with a buffer and EAGAIN handling.
    Code:
    int read_message ( int sock, char *buff, size_t bufflen, size_t *buffused ) {
      int result = 0;
      *buffused = 0;
      
      // Loop until the buffer is full, or a recv result
      // indicates there is a problem of one sort or another
      while ( *buffused < bufflen ) {
        int n = recv(sock, &buff[*buffused], 1, 0);
        if ( n > 0 ) {
          // success
          ++(*buffused);
          result = 1;
        } else if ( n < 0 ) {
          if ( errno == EAGAIN || errno == EWOULDBLOCK ) {
            // This is expected on a non-blocking socket,
            // so just return with what's available at the
            // moment.
            result = 1;
          } else {
            // Everything else is a hard error.
            // Do something with it in the caller.
            result = -errno;
          }
          break;
        } else {  // n == 0
          // closed
          result = 0;
          break;
        }
      }
    
      return result;
    }
    If you want to add framing (eg \n) and \0 termination to it, then that's an easy exercise.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. recv data with sockets
    By r063r in forum C++ Programming
    Replies: 2
    Last Post: 10-05-2008, 09:42 AM
  2. Pros and Cons of blocking versus non-blocking sockets?
    By abachler in forum Networking/Device Communication
    Replies: 4
    Last Post: 05-08-2008, 06:52 AM
  3. non-blocking send/recv
    By antex in forum Networking/Device Communication
    Replies: 14
    Last Post: 05-31-2007, 01:10 PM
  4. blocking recv
    By rohit in forum C Programming
    Replies: 0
    Last Post: 03-06-2002, 02:47 AM
  5. non blocking recv
    By rohit in forum Linux Programming
    Replies: 4
    Last Post: 03-05-2002, 09:35 PM

Tags for this Thread