数据在 (TCP) 套接字中消失
我已经得到了这个主要原型的 TCP 套接字服务器,它接受连接,然后运行用户指定的程序来与另一端通信。神秘的是 write() 被调用并返回,但没有输出到达客户端。
strace 输出(运行“cat”作为执行程序)看起来像这样:
[pid 8142] read(0, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14"..., 32768) = 292
[pid 8142] write(1, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14"..., 292) = 292
[pid 8142] read(0, "", 32768) = 0
[pid 8142] close(0) = 0
[pid 8142] close(1) = 0
[pid 8142] close(2) = 0
而在客户端什么也没有发生:
shell$ seq 100 | nc localhost 4445
shell$
我准备相信 execve 程序应该将套接字视为更像套接字,因为使用发送/接收/关闭而不是读/写/关闭 - 但我到目前为止看到的文档似乎表明 close() 应该按设计工作,并且关闭仅在半关闭连接时才需要。 Unix Sockets FAQ 提到未发送的数据应该在关闭时刷新而不设置任何 SO_LINGER 选项, Linux 手册页 socket(7) 声称“当套接字作为 exit(2) 的一部分关闭时,它总是会徘徊 在后台。”给它足够的输出会导致第一部分到达客户端。
为了完整起见,这里是程序......
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/net.h>
#include <netinet/in.h>
static int sock_fd_listen;
static struct sockaddr_in my_addr = {PF_INET, 0x5d11, 0x0100007f};
static struct
static int one=1;
static int sockargs[]={0, 0, 0};
extern char **environ;
void step1()
{
int retval;
sock_fd_listen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
retval=bind(sock_fd_listen, (struct sockaddr *)&my_addr,
sizeof(struct sockaddr_in));
if (retval==-1) {
perror("bind");
exit(1);
}
setsockopt(sock_fd_listen, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
listen(sock_fd_listen, 5);
}
void main(int argc, char *argv[])
{
static char buf[4096];
int nconn=0;
step1();
while (1) {
int conn_sock;
pid_t pid;
sockargs[0]=sock_fd_listen;
conn_sock=accept(sock_fd_listen,NULL,NULL);
pid=fork();
if (pid==0) {
dup2(conn_sock,0);
dup2(conn_sock,1);
close(conn_sock);
execve(argv[1],argv+1,environ);
fprintf(stderr, "execve failed: %s\n",strerror(errno));
exit(-1);
} else {
close(conn_sock);
}
}
}
I have got this mostly-prototypical TCP socket server that accepts a connection and then runs a user-specified program to talk to the other side. The mysterious thing is that write() is called, and returns, but no output comes through to the client.
The strace output (running "cat" as the executed program) looks like this:
[pid 8142] read(0, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14"..., 32768) = 292
[pid 8142] write(1, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14"..., 292) = 292
[pid 8142] read(0, "", 32768) = 0
[pid 8142] close(0) = 0
[pid 8142] close(1) = 0
[pid 8142] close(2) = 0
while on the client side nothing at all happens:
shell$ seq 100 | nc localhost 4445
shell$
I'd be ready to believe that the execve'd program should treat the socket more socket-like, as in using send/recv/shutdown instead of read/write/close - but the documentation I've seen up to now seems to suggest that close() should work as designed and shutdown would only be necessary for half-closing a connection. The Unix Sockets FAQ mentions that unsent data should be flushed on close without setting any SO_LINGER options, and the Linux man page socket(7) claims that "When the socket is closed as part of exit(2), it always lingers
in the background." Giving it enough output causes the first part to make it to the client.
For the sake of completeness, here is the program...
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <linux/net.h>
#include <netinet/in.h>
static int sock_fd_listen;
static struct sockaddr_in my_addr = {PF_INET, 0x5d11, 0x0100007f};
static struct
static int one=1;
static int sockargs[]={0, 0, 0};
extern char **environ;
void step1()
{
int retval;
sock_fd_listen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
retval=bind(sock_fd_listen, (struct sockaddr *)&my_addr,
sizeof(struct sockaddr_in));
if (retval==-1) {
perror("bind");
exit(1);
}
setsockopt(sock_fd_listen, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
listen(sock_fd_listen, 5);
}
void main(int argc, char *argv[])
{
static char buf[4096];
int nconn=0;
step1();
while (1) {
int conn_sock;
pid_t pid;
sockargs[0]=sock_fd_listen;
conn_sock=accept(sock_fd_listen,NULL,NULL);
pid=fork();
if (pid==0) {
dup2(conn_sock,0);
dup2(conn_sock,1);
close(conn_sock);
execve(argv[1],argv+1,environ);
fprintf(stderr, "execve failed: %s\n",strerror(errno));
exit(-1);
} else {
close(conn_sock);
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论