我正在用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表示以下文本是当天消息的一部分。
当它全部卡在一起时,我无法读取什么行的数字,因为我无法找到一条线的开始或结束位置!
我非常感谢你的帮助!
P.S。:这是在Linux上编译/运行的,但我最终想把它移植到Windows上,所以我尽可能多地制作它。我们可以多平台。
P.S.S。:这是我的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");
}
}
答案 0 :(得分:11)
处理此问题的常用方法是将recv
放入应用程序中的持久缓冲区,然后拉出一行并处理它。稍后您可以在再次调用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);
}
答案 1 :(得分:7)
TCP
不提供任何此类排序。正如@bdonlan已经说过你应该实现类似的东西:
recv
到缓冲区recv
上,检查收到的字节是否包含\n
\n
使用缓冲区中的所有内容(并清除它)我对此没有很好的感觉(我在某处读过你不应该将低级I / O与stdio
I / O混合),但你可以使用{{1} }。
您需要做的只是
fdopen
将您的套接字与fdopen(3)
FILE *
告诉stdio您希望它行缓冲(setvbuf
),而不是默认的块缓冲。此时你应该有效地将工作从你的手移到_IOLBF
。然后,您可以继续使用stdio
上的fgets
等。