UNIX 域套接字导致输出延迟
Output is delayed with UNIX domain socket
我正在尝试用 C 编写一个简单的服务器和客户端,它们将充当 shell。
服务器以系统命令的形式从客户端接收数据,执行它们并将输出重定向到套接字。然后客户端读取套接字,并将其打印到标准输出。当我输入 ls
或 df
等现有系统命令时它运行良好,它也适用于参数。所以 ls -l
工作得很好。
但是,当我输入无法识别的命令时,服务器或客户端(我不知道是哪一个)会出错并且输出会以某种方式延迟。我会告诉你我的意思。
这是我的 server.c
:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#define SOCK_PATH "echo_socket"
int main(void)
{
int s, s2, t, len;
struct sockaddr_un local, remote;
char str[1024];
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
local.sun_family = AF_UNIX;
strcpy(local.sun_path, SOCK_PATH);
unlink(local.sun_path);
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(s, (struct sockaddr *)&local, len) == -1) {
perror("bind");
exit(1);
}
if (listen(s, 5) == -1) {
perror("listen");
exit(1);
}
for(;;) {
int done, n;
printf("Waiting for a connection...\n");
t = sizeof(remote);
if ((s2 = accept(s, (struct sockaddr *)&remote, &t)) == -1) {
perror("accept");
exit(1);
}
printf("Connected.\n");
done = 0;
do {
memset(str,0,strlen(str));
n = recv(s2, str, 1024, 0);
if (n <= 0) {
if (n < 0) perror("recv");
done = 1;
}
int cpid = fork();
int e;
if (cpid == 0)
{
printf("executing: %s\n", str);
dup2(s2, STDOUT_FILENO);
dup2(s2, STDERR_FILENO);
//system(str);
execl("/bin/sh", "sh", "-c", str, (char *) 0)
str[0] = 0;
exit(0);
}
wait();
} while (!done);
close(s2);
}
return 0;
}
这是我的 client.c
:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SOCK_PATH "echo_socket"
int main(void)
{
int s, t, len;
struct sockaddr_un remote;
char str[1024];
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
printf("Trying to connect...\n");
remote.sun_family = AF_UNIX;
strcpy(remote.sun_path, SOCK_PATH);
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
if (connect(s, (struct sockaddr *)&remote, len) == -1) {
perror("connect");
exit(1);
}
printf("Connected.\n");
while(str[0] = '[=11=]', printf("Server shell> "), fgets(str, 1024, stdin), !feof(stdin)) {
if (str[0] == '\n') continue;
str[strlen(str) - 1] = '[=11=]';
if (send(s, str, strlen(str), 0) == -1) {
perror("send");
exit(1);
}
str[0] = '[=11=]';
if ((t=recv(s, str, 1024, 0)) > 0) {
str[t] = '[=11=]';
printf("%s\n", str);
} else {
if (t < 0) perror("recv");
else printf("Server closed connection\n");
exit(1);
}
}
close(s);
return 0;
}
这里是一个用法和输出的例子:
Trying to connect...
Connected.
Server shell> ls
client
client.c
echo_socket
server
server.c
Server shell> m
sh: 1:
Server shell> df
m: not found
Server shell> df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 3596128 2655360 738376 79% /
udev 10240 0 10240 0% /dev
tmpfs 204880 4872 200008 3% /run
tmpfs 512196 39624 472572 8% /dev/shm
tmpfs 5120 4 5116 1% /run/lock
tmpfs 512196 0 512196 0% /sys/fs/cgroup
/dev/sda8 3750424 215924 3324276 7% /home
/dev/sda7 365639 65527 276714 20% /tmp
/dev/sda5 1791524 296880 1385588 18% /var
tmpfs 102440 4 102436 1% /run/user/116
tmpfs 102440 8 102432 1% /run/user/1000
/dev/sr0 57632 57632 0 100% /media/cdrom0
Operativsystem 1953512444 1804472304 149040140 93% /media/sf_Operativsystem
如您所见,在我输入无法识别的 m
之前,它工作正常。
有人可以帮帮我吗?如果您需要更多信息,请告诉我。
您的客户端收到的响应不正确。
recv
可以接收任意数量的字节 - 从一个字节到所有可用数据。
您只调用了 recv
一次,这意味着您可能不会阅读整个回复。您需要一直调用它,直到响应结束。但是你怎么知道那是什么时候?
典型的方法是在发送响应之前发送响应的长度,或者在响应之后发送一个标记,表示 "the response is done"。前者很难预知答案,所以你可以选择后者。
我建议使用 NUL 字节 ('[=12=]'
) 来标记响应的结尾,因为它不应该出现在文本中。服务器在执行完程序后应该发送一个,客户端应该一直接收和打印直到它看到一个NUL字节。 (然后它应该打印直到 NUL 字节的所有内容;您可能会在收到 NUL 字节的同一调用中收到部分响应!)
我正在尝试用 C 编写一个简单的服务器和客户端,它们将充当 shell。
服务器以系统命令的形式从客户端接收数据,执行它们并将输出重定向到套接字。然后客户端读取套接字,并将其打印到标准输出。当我输入 ls
或 df
等现有系统命令时它运行良好,它也适用于参数。所以 ls -l
工作得很好。
但是,当我输入无法识别的命令时,服务器或客户端(我不知道是哪一个)会出错并且输出会以某种方式延迟。我会告诉你我的意思。
这是我的 server.c
:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#define SOCK_PATH "echo_socket"
int main(void)
{
int s, s2, t, len;
struct sockaddr_un local, remote;
char str[1024];
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
local.sun_family = AF_UNIX;
strcpy(local.sun_path, SOCK_PATH);
unlink(local.sun_path);
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(s, (struct sockaddr *)&local, len) == -1) {
perror("bind");
exit(1);
}
if (listen(s, 5) == -1) {
perror("listen");
exit(1);
}
for(;;) {
int done, n;
printf("Waiting for a connection...\n");
t = sizeof(remote);
if ((s2 = accept(s, (struct sockaddr *)&remote, &t)) == -1) {
perror("accept");
exit(1);
}
printf("Connected.\n");
done = 0;
do {
memset(str,0,strlen(str));
n = recv(s2, str, 1024, 0);
if (n <= 0) {
if (n < 0) perror("recv");
done = 1;
}
int cpid = fork();
int e;
if (cpid == 0)
{
printf("executing: %s\n", str);
dup2(s2, STDOUT_FILENO);
dup2(s2, STDERR_FILENO);
//system(str);
execl("/bin/sh", "sh", "-c", str, (char *) 0)
str[0] = 0;
exit(0);
}
wait();
} while (!done);
close(s2);
}
return 0;
}
这是我的 client.c
:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SOCK_PATH "echo_socket"
int main(void)
{
int s, t, len;
struct sockaddr_un remote;
char str[1024];
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
printf("Trying to connect...\n");
remote.sun_family = AF_UNIX;
strcpy(remote.sun_path, SOCK_PATH);
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
if (connect(s, (struct sockaddr *)&remote, len) == -1) {
perror("connect");
exit(1);
}
printf("Connected.\n");
while(str[0] = '[=11=]', printf("Server shell> "), fgets(str, 1024, stdin), !feof(stdin)) {
if (str[0] == '\n') continue;
str[strlen(str) - 1] = '[=11=]';
if (send(s, str, strlen(str), 0) == -1) {
perror("send");
exit(1);
}
str[0] = '[=11=]';
if ((t=recv(s, str, 1024, 0)) > 0) {
str[t] = '[=11=]';
printf("%s\n", str);
} else {
if (t < 0) perror("recv");
else printf("Server closed connection\n");
exit(1);
}
}
close(s);
return 0;
}
这里是一个用法和输出的例子:
Trying to connect...
Connected.
Server shell> ls
client
client.c
echo_socket
server
server.c
Server shell> m
sh: 1:
Server shell> df
m: not found
Server shell> df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda1 3596128 2655360 738376 79% /
udev 10240 0 10240 0% /dev
tmpfs 204880 4872 200008 3% /run
tmpfs 512196 39624 472572 8% /dev/shm
tmpfs 5120 4 5116 1% /run/lock
tmpfs 512196 0 512196 0% /sys/fs/cgroup
/dev/sda8 3750424 215924 3324276 7% /home
/dev/sda7 365639 65527 276714 20% /tmp
/dev/sda5 1791524 296880 1385588 18% /var
tmpfs 102440 4 102436 1% /run/user/116
tmpfs 102440 8 102432 1% /run/user/1000
/dev/sr0 57632 57632 0 100% /media/cdrom0
Operativsystem 1953512444 1804472304 149040140 93% /media/sf_Operativsystem
如您所见,在我输入无法识别的 m
之前,它工作正常。
有人可以帮帮我吗?如果您需要更多信息,请告诉我。
您的客户端收到的响应不正确。
recv
可以接收任意数量的字节 - 从一个字节到所有可用数据。
您只调用了 recv
一次,这意味着您可能不会阅读整个回复。您需要一直调用它,直到响应结束。但是你怎么知道那是什么时候?
典型的方法是在发送响应之前发送响应的长度,或者在响应之后发送一个标记,表示 "the response is done"。前者很难预知答案,所以你可以选择后者。
我建议使用 NUL 字节 ('[=12=]'
) 来标记响应的结尾,因为它不应该出现在文本中。服务器在执行完程序后应该发送一个,客户端应该一直接收和打印直到它看到一个NUL字节。 (然后它应该打印直到 NUL 字节的所有内容;您可能会在收到 NUL 字节的同一调用中收到部分响应!)