[英]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.