我目前正在编写一个关于Linux x86_64
使用<sys/socket.h>
. 通过 接受连接后accept()
,我使用fdopen()
将检索到的套接字包装到FILE*
流中。
写入和读取该FILE*
流通常工作得很好,但是一旦我写入它,套接字就会变得不可用,而它有一个非空的读取缓冲区。
出于演示目的,我编写了一些代码来侦听连接,然后使用fgetc()
. 如果该行太长而无法放入缓冲区,则不会完全读取,而是在下一次迭代期间读取。
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
FILE* listen_on_port(unsigned short port) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in name;
name.sin_family = AF_INET;
name.sin_port = htons(port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sock, (struct sockaddr*) &name, sizeof(name)) < 0)
perror("bind failed");
listen(sock, 5);
int newsock = accept(sock, 0, 0);
return fdopen(newsock, "r+");
}
int main(int argc, char** argv) {
int bufsize = 8;
char buf[9];
buf[8] = 0; //ensure null termination
int data;
int size;
//listen on the port specified in argv[1]
FILE* sock = listen_on_port(atoi(argv[1]));
puts("New connection incoming");
while(1) {
//read a single line
for(size = 0; size < bufsize; size++) {
data = fgetc(sock);
if(data == EOF)
break;
if(data == '\n') {
buf[size] = 0;
break;
}
buf[size] = (char) data;
}
//check if the read failed due to an EOF
if(data == EOF) {
perror("EOF: Connection reset by peer");
break;
} else {
printf("Input line: '%s'\n", buf);
}
//try to write ack
if(fputs("ack\n", sock) == EOF)
perror("sending 'ack' failed");
//try to flush
if(fflush(sock) == EOF)
perror("fflush failed");
}
puts("Connection closed");
}
代码应在 gcc 中编译,无需任何特殊参数。使用端口号作为参数运行它,并使用 netcat 在本地连接到它。
现在,如果您尝试发送少于 8 个字符的字符串,它将完美运行。但是如果你发送一个包含超过 10 个字符的字符串,程序就会失败。此示例输入:
ab
cd
abcdefghij
将创建此输出:
New connection incoming
Input line: 'ab'
Input line: 'cd'
Input line: 'abcdefgh'
fflush failed: Illegal seek
EOF: Connection reset by peer: Illegal seek
Connection closed
如您所见,(正确地)只读取了 abcdefgh 的前 8 个字符,但是当程序尝试发送 'ack' 字符串(客户端从未收到),然后刷新输出缓冲区时,我们收到一个Illegal seek
错误,并且下一次调用fgetc()
返回 EOF。
如果该fflush()
部分被注释掉,仍然会出现同样的错误,但是
fflush failed: Illegal seek
服务器输出中缺少行。
如果该fputs(ack)
部分被注释掉,一切似乎都按预期工作,但从 gdb 手动调用的 perror() 仍然报告“非法搜索”错误。
如果两者都fputs(ack)
被fflush()
注释掉,那么一切都会按预期工作。
不幸的是,我没有找到任何好的文档,也没有关于这个问题的任何 Internet 讨论,因此非常感谢您的帮助。
编辑
我最终解决的解决方案是不使用fdopen()
and FILE*
,因为似乎没有干净的方法可以将套接字 fd 转换为FILE*
可以在r+
模式下可靠使用的 a 。fputs
相反,我直接在套接字 fd 上工作,为and编写自己的替换代码fprintf
。
如果有人需要,这里是代码。