簡體   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