C语言 c recv() 读取直到出现换行符

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6090594/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 08:43:36  来源:igfitidea点击:

c recv() read until newline occurs

csocketsrecv

提问by FurryHead

I'm working on writing a IRC bot in C, and have ran into a snag.

我正在用 C 编写一个 IRC 机器人,但遇到了一个障碍。

In my main function, I create my socket and connect, all that happy stuff. Then I have a (almost) infinite loop to read what's being sent back from the server. I then pass what's read off to a helper function, processLine(char *line)- the problem is, that the following code reads until my buffer is full - I want it to only read text until a newline (\n) or carriage return (\r) occurs (thus ending that line)

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

   while (buffer[0] && buffer[1]) {
        for (i=0;i<BUFSIZE;i++) buffer[i]='
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");
    }
}
'; if (recv(sock, buffer, BUFSIZE, 0) == SOCKET_ERROR) processError(); processLine(buffer); }

What ends up happening is that many lines get jammed all together, and I can't process the lines properly when that happens.

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

If you're not familiar with IRC protocols, a brief summary would be that when a message is sent, it often looks like this: :YourNickName!YourIdent@YourHostName PRIVMSG #someChannel :The rest on from here is the message sent...and a login notice, for instance, is something like this: :the.hostname.of.the.server ### bla some text blawith ### being a code(?) used for processing - i.e. 372 is an indicator that the following text is part of the Message Of The Day.

如果您不熟悉 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 是指示以下文本是当天消息的一部分。

When it's all jammed together, I can't read what number is for what line because I can't find where a line begins or ends!

当它们全部挤在一起时,我无法读取哪一行的编号,因为我找不到一行的开始或结束位置!

I'd appreciate help with this very much!

我非常感谢这方面的帮助!

P.S.: This is being compiled/ran on linux, but I eventually want to port it to windows, so I am making as much of it as I can multi-platform.

PS:这是在 linux 上编译/运行的,但我最终想将它移植到 Windows,所以我尽可能多地使用它。

P.S.S.: Here's my processLine() code:

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

#define BUFFER_SIZE 1024
char inbuf[BUFFER_SIZE];
size_t inbuf_used = 0;

/* Final \n is replaced with ##代码## 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 ##代码##s
   * 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);
}

回答by bdonlan

The usual way to deal with this is to recvinto 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 recvagain. 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 recvto finish the line.

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

Here's an example (totally untested! also looks for a \n, not \r\n):

这是一个示例(完全未经测试!还查找\n,而不是\r\n):

##代码##

回答by cnicutar

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

TCP不提供任何此类排序。正如@bdonlan 已经说过的那样,您应该实现以下内容:

  • Continuously recvfrom the socket into a buffer
  • On each recv, check if the bytes received contain an \n
  • If an \nuse everything up to that point from the buffer (and clear it)
  • 不断地recv从套接字进入缓冲区
  • 在每个上recv,检查收到的字节是否包含\n
  • 如果\n使用缓冲区中的所有内容(并清除它)

I don't have a good feeling about this (I read somewhere that you shouldn't mix low-level I/O with stdioI/O) but you might be able to use fdopen.

我对此感觉不太好(我在某处读到您不应该将低级 I/O 与stdioI/O 混合),但您可能可以使用fdopen.

All you would need to do is

您需要做的就是

  • use fdopen(3)to associate your socket with a FILE *
  • use setvbufto tell stdio that you want it line-buffered (_IOLBF) as opposed to the default block-buffered.
  • 用于fdopen(3)将您的套接字与FILE *
  • 用于setvbuf告诉 stdio 你想要它行缓冲 ( _IOLBF) 而不是默认的块缓冲。

At this point you should have effectively moved the work from your hands to stdio. Then you could go on using fgetsand the like on the FILE *.

在这一点上,您应该已经有效地将工作从您的手中转移到了stdio. 然后,你可以继续使用fgets并喜欢上了FILE *