C语言 使用 popen() 通过套接字执行命令



任何人都可以帮我尝试实现以下服务器和客户端吗?

服务器:

#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
int main(void) {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in serv_addr = { 0 };
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(1234);
    bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    listen(sock, 128);
    struct sockaddr_in cli_addr = { 0 };
    socklen_t cli_addrlen = sizeof(cli_addr);
    int acc_sock = accept(sock, (struct sockaddr *)&cli_addr, &cli_addrlen);
    printf("[+] Connected n");
    char buf[1024];
    ssize_t nread;
    memset(buf, 0, sizeof(buf));
    int a;
    while (1) {
        nread = read(0, buf, 1024);
        write(acc_sock, buf, nread);
        memset(buf, 0, sizeof(buf));
        while ((read(acc_sock, buf, 1024)) != 0) {
            printf("%s", buf);
            memset(buf, 0, sizeof(buf));
        }
    }
}

服务器所做的只是从stdin扫描命令并通过套接字将其发送到客户端。然后扫描客户端响应并将其打印出来stdout

客户端:

#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[]) {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in serv = { 0 };
    char buf[1024];
    char command[1024];
    memset(buf, 0, sizeof(buf));
    memset(command, 0, sizeof(command));
    int nread;
    FILE *in;
    extern FILE *popen();
    serv.sin_family = AF_INET;
    serv.sin_port = htons(atoi(argv[1]));
    serv.sin_addr.s_addr = inet_addr("127.0.0.1");
    int a;  
    connect(sock, (struct sockaddr*)&serv, sizeof(serv));
    while (1) {
        nread = read(sock, buf, 1024);
        in = popen(buf, "r");
        while ((fgets(command, sizeof(command), in)) != NULL) {;
            write(sock, command, sizeof(command));
        }
        memset(buf, 0, sizeof(buf));
    }
    return 0;
}

本质上,客户端接收服务器扫描的命令,使用 popen() 执行它,并逐行发送命令行的内容,直到NULL

问题是代码在第一个命令之后突然停止工作。 命令的传输和命令的输出是令人满意的,但是在打印第一个命令的输出后,程序只是停止工作。我认为fgets()有问题,但我可能是错的。这个问题有什么解决方案吗?

警告:这可能[或可能]不符合您的需求,因为我在下面更正的代码中颠倒了客户端和服务器循环的含义。正如我在上面的评论中提到的:

像这样的应用程序的正常方向是客户端连接到服务器,客户端将命令[从stdin读取]馈送到服务器[popen]并反馈结果。这就是ssh的工作方式。你的方向是相反的。你得到的是你触发sshd并等待ssh连接,然后sshdssh发送命令。换句话说,应该切换各自两侧的回路。

扭转这种情况是事情对我来说唯一有意义的方式。如果反转不适用于您想要的用例,下面的代码可能仍然会给你一些想法。

我通过引入标志字符的概念来表示输出结束,从而解决了挂起问题。我从RS-232 PPP[点对点]协议中借用了这个概念。

标志字符只是一个给定的值(例如 0x10 (,这不太可能是正常数据的一部分。由于您的数据很可能是asciiutf-8,因此可以使用0x00-0x1F范围内的任何[未使用的]字符(即不使用制表符,cr,换行符等(。

如果您需要传输标志字符(即您的数据必须是完整的二进制范围0x00-0xFF(,我已经包含了一些数据包编码/解码例程,以实现上述PPP中使用的转义码。我已经对它们进行了编码,但实际上并没有将它们挂钩。在这种情况下,标志 [和转义] 字符可以是任何二进制值 [通常分别为 0xFF0xFE]。

简单起见,我将两端合并为一个.c文件。使用 -s [first] 调用服务器。

无论如何,这是经过测试的代码[请原谅无端的样式清理]:

// inetpair/inetpair -- server/client communication
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
typedef unsigned char byte;
#define BUFMAX          1024
int port = 1234;
int opt_svr;
int opt_debug;
#define FLAG            0x10
#define ESC             0x11
#define ESC_FLAG        0x01
#define ESC_ESC         0x02
#define dbgprt(_fmt...) 
    do { 
        if (opt_debug) 
            printf(_fmt); 
    } while (0)
// client
int
client(void)
{
    int sock;
    struct sockaddr_in serv = { 0 };
    char *cp;
    char buf[BUFMAX + 1];
    int nread;
    int flag;
    int exitflg;
    sock = socket(AF_INET, SOCK_STREAM, 0);
    serv.sin_family = AF_INET;
    serv.sin_port = htons(port);
    serv.sin_addr.s_addr = inet_addr("127.0.0.1");
    connect(sock, (struct sockaddr *) &serv, sizeof(serv));
    while (1) {
        cp = fgets(buf,BUFMAX,stdin);
        if (cp == NULL)
            break;
        exitflg = (strcmp(buf,"exitn") == 0);
        // send the command
        nread = strlen(buf);
        write(sock, buf, nread);
        if (exitflg)
            break;
        while (1) {
            dbgprt("client: PREREADn");
            nread = read(sock, buf, 1024);
            dbgprt("client: POSTREAD nread=%dn",nread);
            if (nread <= 0)
                break;
            cp = memchr(buf,FLAG,nread);
            flag = (cp != NULL);
            if (flag)
                nread = cp - buf;
            write(1,buf,nread);
            if (flag)
                break;
        }
    }
    close(sock);
    return 0;
}
// server
int
server(void)
{
    struct sockaddr_in serv_addr = { 0 };
    int sock;
    int acc_sock;
    char buf[BUFMAX + 1];
    char command[BUFMAX + 1];
    ssize_t nread;
    FILE *pin;
    FILE *xfin;
    char *cp;
    struct sockaddr_in cli_addr = { 0 };
    opt_debug = ! opt_debug;
    dbgprt("[+] Startingn");
    sock = socket(AF_INET, SOCK_STREAM, 0);
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(port);
    bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
    listen(sock, 128);
    while (1) {
        socklen_t cli_addrlen = sizeof(cli_addr);
        dbgprt("[+] Waiting for connectionn");
        acc_sock = accept(sock,(struct sockaddr *)&cli_addr,&cli_addrlen);
        dbgprt("[+] Connectedn");
        xfin = fdopen(acc_sock,"r");
        while (1) {
            dbgprt("[+] Waiting for commandn");
            cp = fgets(buf,BUFMAX,xfin);
            if (cp == NULL)
                break;
            cp = strchr(buf,'n');
            if (cp != NULL)
                *cp = 0;
            dbgprt("[+] Command '%s'n",buf);
            if (strcmp(buf,"exit") == 0)
                break;
            pin = popen(buf, "r");
            while (1) {
                cp = fgets(command, BUFMAX, pin);
                if (cp == NULL)
                    break;
                nread = strlen(command);
                write(acc_sock, command, nread);
            }
            pclose(pin);
            command[0] = FLAG;
            write(acc_sock,command,1);
        }
        fclose(xfin);
        close(acc_sock);
        dbgprt("[+] Disconnectn");
    }
}
// packet_encode -- encode packet
// RETURNS: (outlen << 1)
int
packet_encode(void *dst,const void *src,int srclen)
{
    const byte *sp = src;
    byte *dp = dst;
    const byte *ep;
    byte chr;
    int dstlen;
    // encode packet in manner similar to PPP (point-to-point) protocol does
    // over RS-232 line
    ep = sp + srclen;
    for (;  sp < ep;  ++sp) {
        chr = *sp;
        switch (chr) {
        case FLAG:
            *dp++ = ESC;
            *dp++ = ESC_FLAG;
            break;
        case ESC:
            *dp++ = ESC;
            *dp++ = ESC_ESC;
            break;
        default:
            *dp++ = chr;
            break;
        }
    }
    dstlen = dp - (byte *) dst;
    dstlen <<= 1;
    return dstlen;
}
// packet_decode -- decode packet
// RETURNS: (outlen << 1) | flag
int
packet_decode(void *dst,const void *src,int srclen)
{
    const byte *sp = src;
    byte *dp = dst;
    const byte *ep;
    byte chr;
    int flag;
    int dstlen;
    // decode packet in manner similar to PPP (point-to-point) protocol does
    // over RS-232 line
    ep = sp + srclen;
    flag = 0;
    while (sp < ep) {
        chr = *sp++;
        flag = (chr == FLAG);
        if (flag)
            break;
        switch (chr) {
        case ESC:
            chr = *sp++;
            switch (chr) {
            case ESC_FLAG:
                *dp++ = FLAG;
                break;
            case ESC_ESC:
                *dp++ = ESC;
                break;
            }
            break;
        default:
            *dp++ = chr;
            break;
        }
    }
    dstlen = dp - (byte *) dst;
    dstlen <<= 1;
    if (flag)
        dstlen |= 0x01;
    return dstlen;
}
int
main(int argc, char **argv)
{
    char *cp;
    --argc;
    ++argv;
    for (;  argc > 0;  --argc, ++argv) {
        cp = *argv;
        if (*cp != '-')
            break;
        switch (cp[1]) {
        case 'd':
            opt_debug = 1;
            break;
        case 'P':
            port = atoi(cp + 2);
            break;
        case 's':
            opt_svr = 1;
            break;
        }
    }
    if (opt_svr)
        server();
    else
        client();
    return 0;
}

客户端从不关闭sock。因此服务器的循环

    while ((read(acc_sock, buf, 1024)) != 0) {
        printf("%s", buf);
        memset(buf, 0, sizeof(buf));
    }

永不终止。您需要某种机制来通知服务器所有命令的输出都已发送。也许类似于HTTP分块传输编码。

最新更新