我有一个小程序,它首先向用户输出一个字符串,然后接受输入。相反,我希望程序通过从端口发送和接收来工作。为了尝试实现这一点,我运行了命令socat TCP4-LISTEN:1337,reuseaddr,fork EXEC:./program
。通过这个命令,我希望能够运行nc 127.0.0.1 1337
并期望该程序:
- 接收程序发来的消息
- 能够提供输入
- 失去连接
但是,当使用它运行程序时,socat
它会像这样
- 提供意见
- 从程序收到的消息
- 提供另一个输入
- 失去了连接
我不明白为什么会发生这种情况。我使用该命令有什么问题吗socat
?如果是,请告诉我缺少/错误的内容。
这是程序。
#include <stdio.h>
void vuln(void) {
printf("Input\n");
char buffer[256];
gets(buffer); // potential buffer overflow
}
int main(void) {
vuln();
return 0;
}
答案1
在 C 中,printf
是缓冲 I/O 库的一部分 - 写入流的数据“缓冲”在内存中(即,不直接写入内核)。
默认情况下,stdout
(写入数据的流printf
)是行缓冲流,这意味着字节存储在内存缓冲区中,直到将换行符写入流(或者其他一些甚至触发刷新的情况)。
如果标准输出未与终端关联,stdout
则为块缓冲流,这意味着字节存储在内存缓冲区中,直到缓冲区满为止。
当您在终端运行程序时,'\n'
调用中的 会printf
触发刷新(因为它是行缓冲的):
$ ./a.out
Input
whatever-you-type
$
如果将输出重定向到文件,您将看到不同的行为:
Terminal 1 Terminal 2
-------------------- --------------------
$ ./a.out > /tmp/out
$ cat /tmp/out
$
type-input
$
$ cat /tmp/out
Input
$
socat
当您在没有 的情况下运行时pts
,输出流处于块缓冲模式;当您使用输出流运行它时,pts
它处于行缓冲模式。
如果您想覆盖该行为,您有几个选择。您可以调用一个函数来显式地刷新任何缓冲的数据:
printf("Input\n");
fflush(stdout);
或者,您可以在调用之前显式设置输出流的缓冲模式printf
:
setvbuf(stdout, NULL, _IONBF, 0); // _IONBF = Unbuffered
printf("Input\n");
请参阅罗伯特·洛夫 (Robert Love) 的书的第 3 章Linux系统编程以获得更详细的解释。