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



我在一个终端中使用以下命令运行以下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;
}

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

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

为了允许多个客户端,您需要完全摆脱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] = '';
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] = '';
}
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(套接字没有";"连接";,因此,任何UDP对等体都不可能知道其远程对等体何时与它断开连接。它能得到的唯一信息是停止接收来自远程对等体的数据包。

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

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

最新更新