C语言 C 中的 UDP 套接字编程:2 个客户端和 1 个服务器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15053072/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
UDP Socket Programming in C: 2 Clients and 1 Server
提问by user2104647
Im very new to network programming. I have a UDP client/server that sends a message to a server in lower or uppercase. The server receives the message and relays it back with switches cases. I cant figure out how I would instead of relaying it back to the first client, sending it to client2. heres my code.
我对网络编程很陌生。我有一个 UDP 客户端/服务器,它以小写或大写向服务器发送消息。服务器接收消息并将其转发回交换机案例。我不知道如何而不是将它中继回第一个客户端,将其发送到 client2。继承人我的代码。
Server:
服务器:
/*
Simple udp server
*/
#include<stdio.h> //printf
#include<string.h> //memset
#include<stdlib.h> //exit(0);
#include<arpa/inet.h>
#include<sys/socket.h>
#include<ctype.h>
#define BUFLEN 512 //Max length of buffer
#define PORT 8888 //The port on which to listen for incoming data
void die(char *s)
{
perror(s);
exit(1);
}
int main(void)
{
struct sockaddr_in si_me, si_other, si_other2;
int s, i, slen = sizeof(si_other) , recv_len;
char buf[BUFLEN];
//create a UDP socket
if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
{
die("socket");
}
// zero out the structure
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(PORT);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
//bind socket to port
if( bind(s , (struct sockaddr*)&si_me, sizeof(si_me) ) == -1)
{
die("bind");
}
//keep listening for data
while(1)
{
printf("Waiting for data...");
fflush(stdout);
//try to receive some data, this is a blocking call
if ((recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen)) == -1) // read datagram from server socket
{
die("recvfrom()");
}
//print details of the client/peer and the data received
printf("Received packet from %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port)); printf("Data: %s\n" , buf);
//now reply to server socket/the client with the same data
if (sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen) == -1)
{
die("sendto()");
}
}
close(s);
return 0;
}
The Client:
客户端:
/*
Simple udp client
*/
#include<stdio.h> //printf
#include<string.h> //memset
#include<stdlib.h> //exit(0);
#include<arpa/inet.h>
#include<sys/socket.h>
#include<ctype.h>
#define SERVER "192.x.x.x"
#define BUFLEN 512 //Max length of buffer
#define PORT 8888 //The port on which to send data
void die(char *s)
{
perror(s);
exit(1);
}
int main(void)
{
struct sockaddr_in si_other;
int s, s2, i, slen=sizeof(si_other);
char buf[BUFLEN];
char message[BUFLEN];
if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) // create a client socket
{
die("socket");
}
memset((char *) &si_other, 0, sizeof(si_other));
si_other.sin_family = AF_INET;
si_other.sin_port = htons(PORT);
if (inet_aton(SERVER , &si_other.sin_addr) == 0) // Create datagram with server IP and port.
{
fprintf(stderr, "inet_aton() failed\n");
exit(1);
}
while(1)
{
printf("Enter message : ");
gets(message);
int a;
char message2[BUFLEN];
for(a=0;a<=BUFLEN-1;a++)
{
if(message[a] >= 97 && message[a] <= 122)
message2[a] = toupper(message[a]);
else
message2[a] = tolower(message[a]);
}
if (sendto(s, message2, strlen(message2) , 0 , (struct sockaddr *) &si_other, slen)==-1)
{
die("sendto()");
}
//receive a reply and print it
//clear the buffer by filling null, it might have previously received data
memset(buf,'recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen))
', BUFLEN);
//try to receive some data, this is a blocking call
if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen) == -1) // read datagram from client socket
{
die("recvfrom()");
}
puts(buf);
}
close(s);
return 0;
}
回答by little_birdie
Since this has 21K views with no explicit answer, and it is basic understanding of coding for UDP.. I will give it some love.
由于这有 21K 视图,没有明确的答案,而且它是对 UDP 编码的基本理解。我会给它一些爱。
As mentioned in the comments already: In your server code, you receive a message from the client using:
正如评论中已经提到的:在您的服务器代码中,您使用以下命令从客户端收到一条消息:
sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen)
The result of this function is that the message data will be written into bufand the ip address and port number of the socket that sent the message will be filled into si_other(which must be of type struct sockaddr_in).
该函数的结果是将消息数据写入buf并填充发送消息的套接字的IP地址和端口号si_other(必须是类型struct sockaddr_in)。
So then, when you send a response using:
那么,当您使用以下方法发送响应时:
struct sockaddr_in si_client2;
memset((char *) &si_client2, 0, sizeof(si_client2));
si_client2.sin_family = AF_INET;
si_client2.sin_port = htons(CLIENT2_PORT);
if(inet_aton(CLIENT2_HOST, &si_client2.sin_addr) == 0)
perror("inet_aton");
Since si_other, which you are passing as the destination address for sendto, contains the ip/port of the last message you got, the response will always go back to the sender of the last message you got. In many server applications this is a pretty common scenario: you get a request from somewhere, you send a response back to the same place.
由于si_other您作为 的目标地址传递的sendto包含您收到的最后一条消息的 ip/端口,因此响应将始终返回到您收到的最后一条消息的发件人。在许多服务器应用程序中,这是一个非常常见的场景:您从某个地方收到请求,然后将响应发送回同一个地方。
If you want the message to go someplace else, other than the process that sent you the request, then you need to create a different struct sockaddr_in variable which contains the ip address and port of where you would like the message to go.
如果您希望消息发送到其他地方,而不是向您发送请求的进程,那么您需要创建一个不同的 struct sockaddr_in 变量,其中包含您希望消息发送到的 IP 地址和端口。
And in your client code, you already have the code that does that, eg (cleaned up a bit):
在您的客户端代码中,您已经有了执行此操作的代码,例如(稍微清理一下):
##代码##So now, if you use si_client2 in your sendto(), the packet will go to that client.
所以现在,如果您在 sendto() 中使用 si_client2,则数据包将发送到该客户端。
Because it's UDP, delivery is not guaranteed. If there is a process listening for UDP at that ip address, on that port number, then (if no network error occurs) it will get the message. If not, nothing will happen.. your message vanishes into the void.
因为它是 UDP,所以不能保证传送。如果有进程在该 IP 地址上侦听 UDP,则在该端口号上,则(如果没有发生网络错误)它将收到消息。如果没有,什么都不会发生……你的信息消失在虚空中。
Keep in mind that if client 1 and client 2 are both running on the same machine, they will need to use different port numbers, because every destination (whether in a client or server role) must have a unique combination of IP and PORT.
请记住,如果客户端 1 和客户端 2 都在同一台机器上运行,则它们将需要使用不同的端口号,因为每个目的地(无论是客户端还是服务器角色)都必须具有唯一的 IP 和 PORT 组合。
Now, in real life applications, it is rare that a server will ever know the IP and PORT of its clients ahead of time.. usually clients will not use fixed port numbers but instead use "ephemeral ports".. port numbers which the operating system assigns at run-time. Whereas, the client will often be configured with the IP and port of the server.
现在,在现实生活中的应用程序中,很少有服务器会提前知道其客户端的 IP 和端口。通常客户端不会使用固定端口号,而是使用“临时端口”......系统在运行时分配。而客户端通常会配置服务器的 IP 和端口。
So, in most cases, you would have some code in the server that keeps a list of client addresses.. perhaps a simple messaging service would keep a list of the last 100 clients it got messages from... But how this is actually done would be dictated by the needs of the application. For a simple exercise like this one, you can just hard code the addresses as I said...
因此,在大多数情况下,您会在服务器中使用一些代码来保存客户端地址列表......也许一个简单的消息传递服务会保存它从中获取消息的最后 100 个客户端的列表......但这实际上是如何完成的将取决于应用程序的需求。对于像这样的简单练习,您可以像我所说的那样对地址进行硬编码...
The bottom line is that, to send a UDP packet to a specific destination, the sender must know the IP and PORT of that specific destination. And the only way to know that is either to have some configuration data, or for someone (such as the destination) to send a packet ahead of time letting you know of its existence. Just keep in mind that with a UDP socket you can get a message from anywhere, and you'll be given the ip/port along with the message. If you need to send a message, you need to know the ip/port of where you want to send it. It's your applications problem to figure out how it's going to get that information and where to store that information for later use.
底线是,要将 UDP 数据包发送到特定目的地,发送方必须知道该特定目的地的 IP 和端口。知道这一点的唯一方法要么是拥有一些配置数据,要么是让某人(例如目的地)提前发送一个数据包,让您知道它的存在。请记住,使用 UDP 套接字,您可以从任何地方获取消息,并且您将获得 ip/端口以及消息。如果你需要发送消息,你需要知道你想要发送它的IP/端口。弄清楚如何获取该信息以及将这些信息存储在何处以备后用是您的应用程序问题。

