简体   繁体   中英

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?

I run the following C program with the following command in one terminal.

./main 1337

Then I run the following command in another terminal.

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

But it does not allow UDP from multiple senders.

Could anybody show me how to make the server to handle UDP packets from multiple senders simultaneously and send the stdout/stderr back to the respective sender?

#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;
}

EDIT: It seems that it is better to connect system()'s stdout/err to the socket. Remy Lebeau provides a solution without this part. If anybody else knows how to fulfill this part, please add it to the code.

Your server is connect() 'ing its UDP socket to the 1st client that sends a command, and then all subsequent sends and reads are only with that specific client.

To allow multiple clients, you need to get rid of connect() altogether, and then call recvfrom() in a loop, using sendto() to send each command's result to the sockaddr that was reported by recvfrom() , eg:

#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;
}

Datagram (UDP) sockets don't have the concept of a "connection", so it's impossible for any UDP peer to know when its remote peer has disconnected from it. The only information it can get is that it stops receiving packets from its remote peer.

One option is to use timeouts, where it decides it's disconnected if it hasn't received any packets within a certain period of time, but that doesn't work with all higher-level protocols. Another option is to use TCP instead of UDP, which provides transport-level disconnection (via FIN or RST packets).

Note that when you call the connect(2) syscall on a UDP socket, all that's doing is setting a value in memory used for the default destination for any packets sent with send(2) . The pair of calls connect(address) and send() on a UDP socket is equivalent to calling sendto(address) on that socket.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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