c - C : 2 Clients and 1 Server 中的 UDP 套接字编程

标签 c udp client

我是网络编程的新手。我有一个 UDP 客户端/服务器,它以小写或大写形式向服务器发送消息。服务器接收消息并使用开关盒将其中继回去。我无法弄清楚我将如何而不是将它中继回第一个客户端,将它发送到客户端 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;
}

最佳答案

因为它有 21K 的浏览量,没有明确的答案,而且它是对 UDP 编码的基本理解。我会喜欢它。

如评论中所述:在您的服务器代码中,您使用以下方式从客户端接收消息:

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

该函数的结果是将消息数据写入buf并将发送消息的socket的ip地址和端口号填入si_other(必须是 struct sockaddr_in 类型)。

那么,当您使用以下方式发送响应时:

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

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

如果您希望消息发送到其他地方,而不是发送请求的进程,那么您需要创建一个不同的 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 和端口。

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

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

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

关于c - C : 2 Clients and 1 Server 中的 UDP 套接字编程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15053072/

相关文章:

java-udp编程-从服务器向客户端发送消息

Java 以数据报包形式接收 Java 对象

c - 如何更改传递给函数的结构的值

c - 字符数组的整数转换

linux - AWS : How to duplicate UDP traffic from PRD to TEST environment?

java - readobject 方法抛出 ClassNotFoundException

widget - VAADIN 客户端组件逻辑

c++ - 已编译的可执行文件被视为特洛伊木马威胁

c++ - 奇怪的图案 : all functions/methods return error-code using the same type in C++

c - 套接字编程,C 语言的服务器和多个客户端