繁体   English   中英

如何使 UDP 套接字接收来自多个发送者的数据包并运行每个数据包中的命令并将 stdout/stderr 打印回原始发送者?

[英]How make UDP socket receive packets from multiple senders and run the commands in each packet and print stdout/stderr back to the original sender?

我在一个终端中使用以下命令运行以下 C 程序。

./main 1337

然后我在另一个终端中运行以下命令。

nc -u localhost 1337 <<< 'ls -l --color=always'

但它不允许来自多个发件人的 UDP。

谁能告诉我如何让服务器同时处理来自多个发送者的 UDP 数据包并将 stdout/stderr 发送回各自的发送者?

#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>

void perrdie(const char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

int main(int argc, char *argv[]) {
  int sockfd;
  if((sockfd=socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
    perrdie("socket");
  }

  struct sockaddr_in bind_addr;
  bind_addr.sin_family=AF_INET;
  bind_addr.sin_port=htons(atoi(argv[1]));
  bind_addr.sin_addr.s_addr=INADDR_ANY;
  if(bind(sockfd, (struct sockaddr*)(&bind_addr),sizeof(bind_addr))<0) {
    perrdie("bind");
  }
  struct sockaddr src_addr;
  socklen_t src_addr_len=sizeof(src_addr);
  char buf[1024];
  int dgram_len;
  if((dgram_len = recvfrom(sockfd, &buf, sizeof(buf), 0, &src_addr, &src_addr_len)) == -1) {
    perrdie("recvfrom");
  }

  if(connect(sockfd, &src_addr, sizeof(src_addr)) == -1) {
    perrdie("connect");
  }
  while(1) {
    fputc('^', stdout);
    fwrite(buf, sizeof(char), dgram_len, stdout);
    fputc('$', stdout);
    fflush(stdout);
    for(int i=0;i<sizeof(buf) && i<dgram_len;++i) {
      if(buf[i]=='\r' || buf[i]=='\n') {
        buf[i]=0;
        system(buf);
        if((dgram_len = recv(sockfd, &buf, sizeof(buf), 0)) == -1) {
          perrdie("recv");
        }
        break;
      }
    }
  }
  return 0;
}

编辑:似乎最好将 system() 的 stdout/err 连接到套接字。 Remy Lebeau 提供了一个没有这部分的解决方案。 如果其他人知道如何完成这部分,请将其添加到代码中。

您的服务器正在connect()其 UDP 套接字连接到发送命令的第一个客户端,然后所有后续发送和读取都仅使用该特定客户端。

要允许多个客户端,您需要完全摆脱connect() ,然后在循环中调用recvfrom() ,使用sendto()将每个命令的结果发送到由recvfrom()报告的sockaddr ,例如:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>

void perrdie(const char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

int main(int argc, char *argv[]) {
  int sockfd;
  if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
    perrdie("socket");
  }

  struct sockaddr_in bind_addr;
  bind_addr.sin_family = AF_INET;
  bind_addr.sin_port = htons(atoi(argv[1]));
  bind_addr.sin_addr.s_addr = INADDR_ANY;
  if (bind(sockfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) < 0) {
    perrdie("bind");
  }

  struct sockaddr_in src_addr;
  socklen_t src_addr_len;
  char buf[1025], *cmd;
  int dgram_len, cmd_len;

  while(1) {
    src_addr_len = sizeof(src_addr);
    if ((dgram_len = recvfrom(sockfd, &buf, sizeof(buf)-1, 0, (struct sockaddr*)&src_addr, &src_addr_len)) < 0) {
      perrdie("recvfrom");
    }
    buf[dgram_len] = '\0';

    fputc('^', stdout);
    fwrite(buf, sizeof(char), dgram_len, stdout);
    fputc('$', stdout);
    fflush(stdout);

    cmd = strtok(buf, "\n");
    while (cmd != NULL) {
      cmd_len = strlen(cmd);
      if (cmd_len > 0 && cmd[cmd_len-1] == '\r') {
        cmd[--cmd_len] = '\0';
      }
      system(cmd);
      // read in and send output back to sender as needed...
      sendto(sockfd, YourSystemOutput, YourSystemOutputSize, 0, &src_addr, &src_addr_len);
      cmd = strtok(NULL, "\n");
    }
  }

  close(sockfd);
  return 0;
}

数据报 (UDP) sockets 没有“连接”的概念,因此任何 UDP 对等点都不可能知道其远程对等点何时与它断开连接。 它可以得到的唯一信息是它停止接收来自其远程对等方的数据包。

一种选择是使用超时,如果在一定时间内没有收到任何数据包,它会决定断开连接,但这不适用于所有更高级别的协议。 另一种选择是使用 TCP 而不是 UDP,它提供传输级断开连接(通过 FIN 或 RST 数据包)。

请注意,当您在 UDP 套接字上调用connect(2)系统调用时,所做的只是在 memory 中设置一个值,该值用于使用send(2)的任何数据包的默认目标。 UDP 套接字上的一对调用connect(address)send()等效于在该套接字上调用sendto(address)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM