I\'m working on writing a IRC bot in C, and have ran into a snag.
In my main function, I create my socket and connect, all that happy stuff. Then I have a (almost) i
The usual way to deal with this is to recv
into a persistent buffer in your application, then pull a single line out and process it. Later you can process the remaining lines in the buffer before calling recv
again. Keep in mind that the last line in the buffer may only be partially received; you have to deal with this case by re-entering recv
to finish the line.
Here's an example (totally untested! also looks for a \n
, not \r\n
):
#define BUFFER_SIZE 1024
char inbuf[BUFFER_SIZE];
size_t inbuf_used = 0;
/* Final \n is replaced with \0 before calling process_line */
void process_line(char *lineptr);
void input_pump(int fd) {
size_t inbuf_remain = sizeof(inbuf) - inbuf_used;
if (inbuf_remain == 0) {
fprintf(stderr, "Line exceeded buffer length!\n");
abort();
}
ssize_t rv = recv(fd, (void*)&inbuf[inbuf_used], inbuf_remain, MSG_DONTWAIT);
if (rv == 0) {
fprintf(stderr, "Connection closed.\n");
abort();
}
if (rv < 0 && errno == EAGAIN) {
/* no data for now, call back when the socket is readable */
return;
}
if (rv < 0) {
perror("Connection error");
abort();
}
inbuf_used += rv;
/* Scan for newlines in the line buffer; we're careful here to deal with embedded \0s
* an evil server may send, as well as only processing lines that are complete.
*/
char *line_start = inbuf;
char *line_end;
while ( (line_end = (char*)memchr((void*)line_start, '\n', inbuf_used - (line_start - inbuf))))
{
*line_end = 0;
process_line(line_start);
line_start = line_end + 1;
}
/* Shift buffer down so the unprocessed data is at the start */
inbuf_used -= (line_start - inbuf);
memmove(innbuf, line_start, inbuf_used);
}
TCP
doesn't offer any sequencing of that sort. As @bdonlan already said you should implement something like:
recv
from the socket into a bufferrecv
, check if the bytes received contain an \n
\n
use everything up to that point from the buffer (and clear it)I don't have a good feeling about this (I read somewhere that you shouldn't mix low-level I/O with stdio
I/O) but you might be able to use fdopen
.
All you would need to do is
fdopen(3)
to associate your socket with a FILE *
setvbuf
to tell stdio that you want it line-buffered (_IOLBF
) as opposed to the default block-buffered.At this point you should have effectively moved the work from your hands to stdio
. Then you could go on using fgets
and the like on the FILE *
.