You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改TCP套接字程序实现服务器发命令并获取客户端执行结果

TCP Socket 命令执行与结果回传修改方案

需求说明

当前TCP套接字代码仅实现客户端发送内容、服务器回显的功能,现需修改为:服务端下发Shell命令(如ls、ls -l、ls -l | wc、ls > result.txt等),客户端执行该命令并将执行结果返回给服务端。


修改后的服务端代码(server.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc, char *argv[]) {
    int portno;
    int sockfd, ret;
    struct sockaddr_in serverAddr;
    int newSocket;
    struct sockaddr_in newAddr;
    socklen_t addr_size;
    char buffer[4096];  // 扩大缓冲区以容纳命令执行结果
    pid_t childpid;

    if (argc != 2) {
        printf("Usage: %s <port>\n", argv[0]);
        exit(1);
    }

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        printf("[-]Error in creating socket.\n");
        exit(1);
    }
    printf("[+]Server Socket is created.\n");

    portno = atoi(argv[1]);
    memset(&serverAddr, '\0', sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(portno);
    serverAddr.sin_addr.s_addr = INADDR_ANY;  // 监听所有可用网卡,而非仅127.0.0.1

    ret = bind(sockfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
    if (ret < 0) {
        printf("[-]Error in binding.\n");
        exit(1);
    }
    printf("[+]Bind to port %d\n", portno);

    if (listen(sockfd, 10) == 0) {
        printf("[+]Listening....\n");
    } else {
        printf("[-]Error in listening.\n");
        exit(1);
    }

    while (1) {
        newSocket = accept(sockfd, (struct sockaddr*)&newAddr, &addr_size);
        if (newSocket < 0) {
            printf("[-]Error in accepting connection.\n");
            continue;
        }
        printf("Connection accepted from %s:%d\n", inet_ntoa(newAddr.sin_addr), ntohs(newAddr.sin_port));

        if ((childpid = fork()) == 0) {
            close(sockfd);  // 子进程不需要监听套接字

            while (1) {
                // 服务端输入要下发的命令
                printf("Enter command to send (or 'QUIT' to exit): ");
                fgets(buffer, sizeof(buffer), stdin);
                buffer[strcspn(buffer, "\n")] = '\0';  // 去掉换行符

                if (strcmp(buffer, "QUIT") == 0) {
                    send(newSocket, buffer, strlen(buffer), 0);
                    printf("[-]Disconnected from %s:%d\n", inet_ntoa(newAddr.sin_addr), ntohs(newAddr.sin_port));
                    break;
                }

                // 发送命令给客户端
                send(newSocket, buffer, strlen(buffer), 0);

                // 接收客户端返回的命令执行结果
                bzero(buffer, sizeof(buffer));
                ssize_t bytes_received = recv(newSocket, buffer, sizeof(buffer)-1, 0);
                if (bytes_received < 0) {
                    printf("[-]Error in receiving result.\n");
                    break;
                } else if (bytes_received == 0) {
                    printf("[-]Client disconnected.\n");
                    break;
                }
                buffer[bytes_received] = '\0';  // 确保字符串终止

                printf("\nClient execution result:\n%s\n", buffer);
            }
            close(newSocket);
            exit(0);
        }
    }

    close(sockfd);
    return 0;
}

修改后的客户端代码(client.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>

#define BUFFER_SIZE 4096

// 执行Shell命令并返回输出结果
char* execute_command(const char* cmd) {
    FILE* pipe = popen(cmd, "r");
    if (!pipe) {
        return strdup("Error: Failed to execute command.\n");
    }

    char* result = malloc(BUFFER_SIZE);
    bzero(result, BUFFER_SIZE);
    char buffer[1024];
    size_t total_len = 0;

    while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
        size_t len = strlen(buffer);
        if (total_len + len < BUFFER_SIZE) {
            strcat(result, buffer);
            total_len += len;
        } else {
            // 缓冲区不足时截断并提示
            strncat(result, "[Output truncated due to buffer limit]\n", BUFFER_SIZE - total_len - 1);
            break;
        }
    }

    pclose(pipe);
    return result;
}

int main(int argc, char *argv[]) {
    int portno;
    int clientSocket, ret;
    struct sockaddr_in serverAddr;
    char buffer[BUFFER_SIZE];

    if (argc != 3) {
        printf("Usage: %s <server_ip> <port>\n", argv[0]);
        exit(1);
    }

    clientSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (clientSocket < 0) {
        printf("[-]Error in creating socket.\n");
        exit(1);
    }
    printf("[+]Client Socket is created.\n");

    portno = atoi(argv[2]);
    memset(&serverAddr, '\0', sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(portno);
    
    // 使用传入的服务器IP而非固定127.0.0.1
    if (inet_pton(AF_INET, argv[1], &serverAddr.sin_addr) <= 0) {
        printf("[-]Invalid server IP address.\n");
        exit(1);
    }

    ret = connect(clientSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
    if (ret < 0) {
        printf("[-]Error in connecting to server.\n");
        exit(1);
    }
    printf("[+]Connected to Server.\n");

    while (1) {
        // 接收服务端下发的命令
        bzero(buffer, sizeof(buffer));
        ssize_t bytes_received = recv(clientSocket, buffer, sizeof(buffer)-1, 0);
        if (bytes_received < 0) {
            printf("[-]Error in receiving command.\n");
            break;
        } else if (bytes_received == 0) {
            printf("[-]Server disconnected.\n");
            break;
        }
        buffer[bytes_received] = '\0';

        // 处理退出命令
        if (strcmp(buffer, "QUIT") == 0) {
            printf("[-]Server requested disconnect. Exiting.\n");
            break;
        }

        printf("[+]Received command: %s\n", buffer);
        // 执行命令并获取结果
        char* result = execute_command(buffer);
        // 将结果发送回服务端
        send(clientSocket, result, strlen(result), 0);
        free(result);  // 释放内存
    }

    close(clientSocket);
    return 0;
}

关键修改解析

服务端修改点

  • 命令输入逻辑:新增服务端控制台输入命令的功能,用fgets读取命令并自动去除换行符,支持QUIT命令主动断开连接。
  • 缓冲区扩容:把缓冲区从1024字节调整为4096字节,避免长命令输出被截断。
  • 监听地址优化:将固定127.0.0.1改为INADDR_ANY,允许服务端接收来自任意网卡的客户端连接(按需可改回固定IP)。
  • 参数校验与错误提示:增加命令行参数检查,优化连接、接收过程中的错误提示信息,方便调试。

客户端修改点

  • 命令执行封装:新增execute_command函数,通过popen调用Shell执行命令并捕获标准输出,还处理了缓冲区溢出的截断提示。
  • 动态服务器IP:支持通过命令行参数传入服务端IP,不再固定为127.0.0.1,增强跨机器部署的灵活性。
  • 命令处理流程:接收服务端命令后先判断是否为退出指令,否则执行命令并将结果回传给服务端。
  • 内存管理:手动管理命令执行结果的内存分配与释放,避免内存泄漏问题。

编译与运行说明

  1. 编译服务端:gcc server.c -o server
  2. 编译客户端:gcc client.c -o client
  3. 启动服务端:./server 8080(替换为自定义端口)
  4. 启动客户端:./client 127.0.0.1 8080(替换为服务端实际IP和端口)
  5. 在服务端控制台输入命令(如ls -l),即可接收客户端返回的执行结果。

内容的提问来源于stack exchange,提问作者John Taramas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 10:02:27