c recv() read until newline occurs

后端 未结 2 799
太阳男子
太阳男子 2021-01-11 18:03

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

相关标签:
2条回答
  • 2021-01-11 18:57

    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);
    }
    
    0 讨论(0)
  • 2021-01-11 18:59

    TCP doesn't offer any sequencing of that sort. As @bdonlan already said you should implement something like:

    • Continuously recv from the socket into a buffer
    • On each recv, check if the bytes received contain an \n
    • If an \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

    • use fdopen(3) to associate your socket with a FILE *
    • use 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 *.

    0 讨论(0)
提交回复
热议问题