10

私は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);
    }

結局、多くの行が一緒に詰まってしまい、それが発生したときに行を適切に処理できなくなります。

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");
    }
}
4

2 に答える 2

11

これに対処する通常の方法はrecv、アプリケーションの永続バッファーに入れてから、1行を引き出して処理することです。後で、再度呼び出す前に、バッファ内の残りの行を処理できますrecv。バッファの最後の行は部分的にしか受信できないことに注意してください。recv行を終了するには、再入力してこのケースに対処する必要があります。

次に例を示します(完全にテストされていません!また、\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);
}
于 2011-05-22T20:32:57.633 に答える
7

TCPそのようなシーケンスは提供していません。@bdonlanがすでに言ったように、次のようなものを実装する必要があります。

  • recvソケットからバッファに継続的に
  • それぞれrecvで、受信したバイトにが含まれているかどうかを確認します\n
  • その\n時点までのすべてをバッファから使用する(そしてそれをクリアする)場合

これについてはよくわかりませんが(低レベルのI/OとstdioI/Oを混在させてはいけないことをどこかで読んだことがあります)、使用できる可能性がありますfdopen

あなたがする必要があるのは

  • fdopen(3)ソケットをに関連付けるために使用しますFILE *
  • デフォルトのブロックバッファリングではなく、setvbufラインバッファリング()することをstdioに通知するために使用します。_IOLBF

この時点で、作業を手からに効果的に移動する必要がありますstdiofgetsその後、で使用などを続けることができますFILE *

于 2011-05-22T20:42:55.147 に答える