【问题标题】:Output is delayed with UNIX domain socket输出因 UNIX 域套接字而延迟
【发布时间】:2016-03-06 21:56:02
【问题描述】:

我正在尝试用 C 编写一个简单的服务器和客户端,它们将充当外壳程序。 服务器以系统命令的形式从客户端接收数据,执行它们并将输出重定向到套接字。客户端然后读取套接字,并将其打印到标准输出。当我输入诸如lsdf 之类的现有系统命令时,它运行良好,并且它也适用于参数。所以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] = '\0', printf("Server shell> "), fgets(str, 1024, stdin), !feof(stdin)) {
    if (str[0] == '\n') continue;
    str[strlen(str) - 1] = '\0';
    if (send(s, str, strlen(str), 0) == -1) {
            perror("send");
            exit(1);
        }
        str[0] = '\0';
        if ((t=recv(s, str, 1024, 0)) > 0) {
            str[t] = '\0';
            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 之前它工作正常。 有人可以帮我吗?如果您需要更多信息,请告诉我。

【问题讨论】:

  • 您的代码中有未定义的行为。可能不是唯一的问题,但确实需要先解决这些问题:memset(str,0,strlen(str))。第一次运行 str 是未初始化的,因此 strlen(str) 可以是任何东西。
  • 请注意,如果recv 恰好接收到 1024 个字节,则 str[t] = '\0'; 是缓冲区溢出。
  • 此外,您不能假设recv 将在一次调用中接收到对等方发送的全部消息。可能需要一个或多个调用来实现这一点。因此,recv 需要在循环中调用,并且消息需要具有允许分隔单独消息的格式(例如,在您的情况下,通过在消息中包含 NUL 字符)。

标签: c linux unix debian unix-socket


【解决方案1】:

您的客户收到的响应不正确。

recv 可以接收任意数量的字节 - 从一个字节到所有可用数据。

您只调用了一次recv,这意味着您可能不会阅读整个回复。您需要继续调用它,直到响应结束。但是你怎么知道那是什么时候呢?

典型的方法是在发送响应之前发送响应的长度,或者在响应之后发送一个标记,表示“响应完成”。前者在你事先不知道回应的情况下很难,所以你可以选择后者。

我建议使用 NUL 字节 ('\0') 来标记响应的结束,因为它不应该出现在文本中。服务器在执行程序后应该发送一个,客户端应该继续接收和打印,直到它看到一个 NUL 字节。 (然后它应该将所有内容打印到 NUL 字节;您可能会在收到 NUL 字节的同一调用中收到部分响应!)

【讨论】:

  • 感谢您的回复!我相信你在这里是正确的。但我无法让它工作。当我使用while ((t=recv(s, str, 1024, 0)) &gt; 0) 时,它似乎根本没有得到任何数据。
  • @ViggoLundén 您是否在 while 循环中打印数据? recv 只会在另一端关闭连接时返回 0。
  • 我尝试了这个:if (strcmp(str, "end") == 0) break; 在 while 循环中,send(s2, "end", 3, 0); 在服务器代码中执行命令后。它仍然不起作用。比如,while 循环永远不会中断。
  • @ViggoLunden 如果字符串是"sh: 1: m: not found\nend" 怎么办?还是"t found\ne" 后跟"nd"?或者输出不是以换行符结尾而是"hello worldend"? (或"hello worlden" 后跟"d"
  • 我让它使用 NUL 字节,问题是我从子进程发送它。不知何故修复了它。感谢您的帮助。
猜你喜欢
  • 2013-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多