3

我对网络编程很陌生。我有一个 UDP 客户端/服务器,它以小写或大写形式向服务器发送消息。服务器接收消息并通过 switch case 将其转发回来。我不知道如何将其转发回第一个客户端,将其发送到客户端 2。这是我的代码。

服务器:

/*
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;
}

客户端:

/*
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,'\0', 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;
}
4

1 回答 1

9

由于这有 21K 视图没有明确的答案,这是对 UDP 编码的基本理解。我会给它一些爱。

正如评论中已经提到的:在您的服务器代码中,您使用以下命令从客户端接收消息:

recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen))

这个函数的结果是消息数据将被写入buf,并且发送消息的套接字的IP地址和端口号将被填写si_other(必须是类型struct sockaddr_in)。

那么,当您使用以下命令发送响应时:

sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen)

由于si_other您作为 的目标地址传递的sendto包含您收到的最后一条消息的 ip/port,因此响应将始终返回给您收到的最后一条消息的发件人。在许多服务器应用程序中,这是一个非常常见的场景:您从某个地方收到请求,然后将响应发送回同一个地方。

如果您希望消息发送到其他地方,而不是向您发送请求的进程,那么您需要创建一个不同的 struct sockaddr_in 变量,其中包含您希望消息发送到的 IP 地址和端口。

在您的客户端代码中,您已经拥有执行此操作的代码,例如(清理了一下):

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");

所以现在,如果您在 sendto() 中使用 si_client2,数据包将发送到该客户端。

因为它是UDP,所以不能保证交付。如果有一个进程在该 IP 地址、该端口号上侦听 UDP,那么(如果没有发生网络错误)它将收到该消息。如果没有,什么都不会发生.. 你的信息消失在虚空中。

请记住,如果客户端 1 和客户端 2 都在同一台机器上运行,它们将需要使用不同的端口号,因为每个目标(无论是客户端还是服务器角色)都必须具有唯一的 IP 和 PORT 组合。

现在,在现实生活中的应用程序中,服务器很少会提前知道其客户端的 IP 和 PORT。通常客户端不会使用固定端口号,而是使用“临时端口”。系统在运行时分配。然而,客户端通常会配置服务器的 IP 和端口。

因此,在大多数情况下,您将在服务器中保留一些代码来保存客户端地址列表。也许一个简单的消息传递服务会保留它从其获取消息的最后 100 个客户端的列表......但这实际上是如何完成的将由应用程序的需要决定。对于像这样的简单练习,您可以按照我所说的对地址进行硬编码......

底线是,要将 UDP 数据包发送到特定目的地,发送者必须知道该特定目的地的 IP 和 PORT。知道这一点的唯一方法是拥有一些配置数据,或者让某人(例如目的地)提前发送一个数据包,让您知道它的存在。请记住,使用 UDP 套接字,您可以从任何地方获取消息,并且您将获得 ip/port 以及消息。如果您需要发送消息,则需要知道要发送消息的 ip/端口。您的应用程序的问题是弄清楚它将如何获取该信息以及将这些信息存储在何处以供以后使用。

于 2015-08-11T21:51:35.490 回答