c recv()读取直到换行发生

Fur*_*ead 10 c sockets recv

我正在用C编写一个IRC机器人,并遇到了麻烦.

在我的主要功能中,我创建了我的套接字并连接,所有这些快乐的东西.然后我有一个(几乎)无限循环来读取从服务器发回的内容.然后我将读取的内容传递给辅助函数,processLine(char *line)- 问题是,以下代码读取直到我的缓冲区已满 - 我希望它只读取文本,直到换行符(\n)或回车符(\ r)发生(从而结束那条线)

   while (buffer[0] && buffer[1]) {
        for (i=0;i<BUFSIZE;i++) buffer[i]='\0';
        if (recv(sock, buffer, BUFSIZE, 0) == SOCKET_ERROR)
            processError();

        processLine(buffer);
    }
Run Code Online (Sandbox Code Playgroud)

最终发生的事情是,许多线路都被卡在一起,当发生这种情况时我无法正确处理线路.

如果您不熟悉IRC协议,可以简要总结一下,当发送消息时,它通常如下所示::YourNickName!YourIdent@YourHostName PRIVMSG #someChannel :The rest on from here is the message sent... 例如,登录通知是这样的::the.hostname.of.the.server ### bla some text bla###是一个代码(? )用于处理 - 即372是以下文本是当天消息的一部分的指示符.

当它全部卡在一起时,我无法读取什么行的数字,因为我找不到行开始或结束的位置!

我非常感谢你的帮助!

PS:这是在Linux上编译/运行,但我最终想把它移植到Windows,所以我尽可能多地制作多平台.

PSS:这是我的processLine()代码:

void processLine(const char *line) {
    char *buffer, *words[MAX_WORDS], *aPtr;
    char response[100];
    int count = 0, i;
    buffer = strdup(line);

    printf("BLA %s", line);

    while((aPtr = strsep(&buffer, " ")) && count < MAX_WORDS)
        words[count++] = aPtr;
        printf("DEBUG %s\n", words[1]);
    if (strcmp(words[0], "PING") == 0) {
        strcpy(response, "PONG ");
        strcat(response, words[1]);
        sendLine(NULL, response); /* This is a custom function, basically it's a send ALL function */
    } else if (strcmp(words[1], "376") == 0) { /* We got logged in, send login responses (i.e. channel joins) */
        sendLine(NULL, "JOIN #cbot");
    }
}
Run Code Online (Sandbox Code Playgroud)

bdo*_*lan 11

解决这个问题的常用方法是recv在应用程序中使用持久缓冲区,然后拉出一行并处理它.稍后您可以在recv再次调用之前处理缓冲区中的剩余行.请记住,缓冲区中的最后一行只能部分接收; 你必须通过重新进入recv完成该线来处理这种情况.

这是一个例子(完全未经测试!也寻找a \n,而不是\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);
}
Run Code Online (Sandbox Code Playgroud)

  • 哇哇 我很久以前就放弃了这个项目,感觉大部分时间都在我的头上(这是它).现在我终于回到一个非常相似的项目(irc bot再次,但有点不同),我读完了这一点,甚至没有意识到这是我的线程.在过去的两天里,我一直在敲桌子,试图实现这个(几乎就是你正在写的)但是,奇怪的是,我最终只有一个角色从线上的随机位置被移除.奇.无论如何,只是想再次感谢你!这帮了很多忙! (2认同)

cni*_*tar 7

TCP没有提供任何类型的排序.正如@bdonlan已经说过你应该实现类似的东西:

  • recv从插座连续进入缓冲区
  • 在每个上recv,检查收到的字节是否包含\n
  • 如果\n从缓冲区中使用所有内容(并清除它)

我对此没有很好的感觉(我在某处读到你不应该将低级I/O与stdioI/O混合),但你可以使用它fdopen.

你需要做的就是

  • 用于fdopen(3)将套接字与a关联FILE *
  • setvbuf告诉标准输入输出,你希望它行缓冲(_IOLBF),而不是默认的块缓冲.

此时你应该有效地将工作从你的手中移到了stdio.然后你可以继续使用fgetsFILE *.