views:

65

answers:

2

I'm writing myself a small server daemon in C, and the basic parts like processing connects, disconnects and receives are already in, but a problem in receiving still persists.

I use "recv" to read 256 bytes at once into a char array, and because it can contain multiple lines of data as one big chunk, I need to be able to split each line separatly to process it. That alone wouldn't be the problem, but because of the possibility that a line could be cut off because it didn't fit into the buffer anymore, I also need to be able to see if a line has been cut off. Not that bad, too, just check the last char for \r or \n, but what if the line was cut off? My code does not allow for easy "just keep reading more data" because I'm using select() to handle multiple requests.

Basically, this is my situation:

//This is the chunk of code ran after select(), when a socket
//has readable data
char buf[256] = { 0 };
int nbytes;

if ((nbytes = recv(i, buf, sizeof(buf) - 1, 0)) <= 0)
{   
   if (nbytes == 0)
   {   
      struct remote_address addr;

      get_peername(i, &addr);
      do_log("[Socket #%d] %s:%d disconnected", i, addr.ip, addr.port);
   }   
   else
      do_log("recv(): %s", strerror(errno));

   close(i);
   FD_CLR(i, &clients);
}   
else
{   
   buf[sizeof(buf) - 1] = 0;
   struct remote_address addr;

   get_peername(i, &addr);
   do_log("[Socket #%d] %s:%d (%d bytes): %s", i, addr.ip, addr.port, nbytes, buf);

   // split "buf" here, and process each line
   // but how to be able to get the rest of a possibly cut off line
   // in case it did not fit into the 256 byte buffer?
}

I was thinking about having a higher scoped temporary buffer variable (possibly malloc()'d) to save the current buffer in, if it was too long to fit in at once, but I always feel bad about introducing unnecessarily high scoped variables if there's a better solution :/

I appreciate any pointers (except for the XKCD ones :))!

+3  A: 

I guess you need to add another per-stream buffer that holds the incomplete line until the line feed that comes after is received.

Tronic
indeed, if you add backspace handling, you could make ur app work with hyperterminal too since user are allowed to undo their typo
YeenFei
This is the standard way of doing things (you can do the `read` straight into this per-stream buffer).
caf
A: 

I'd use some kind of dynamically expanding buffer like GString to accumulate data.

The other thing that might help would be putting the socket into nonblocking mode using fcntl(). Then you can recv() in a loop until you get a -1. Check errno, it will be either EAGAIN or EWOULDBLOCK (and those aren't required to have the same value: check for both).

Final remark: I found that using libev (google it; I can't post multiple links) was more fun than using select().

Jack