【问题标题】:How to send variable using UDP datagram in linux? [closed]如何在 linux 中使用 UDP 数据报发送变量? [关闭]
【发布时间】:2016-01-31 08:20:56
【问题描述】:

我有一个使用 UDP 的客户端和一个服务器: 客户端.c:

#include<stdio.h> //printf
#include<string.h> //memset
#include<stdlib.h> //exit(0);
#include<arpa/inet.h>
#include<sys/socket.h>

#define SERVER "127.0.0.1"
#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, i, slen=sizeof(si_other);
    char buf[BUFLEN];
    char message[BUFLEN];

    if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
    {
        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) 
    {
        fprintf(stderr, "inet_aton() failed\n");
        exit(1);
    }

    while(1)
    {
        printf("Enter message : ");
        gets(message);

        //send the message
        if (sendto(s, message, strlen(message) , 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)
        {
            die("recvfrom()");
        }

        puts(buf);
    }

    close(s);
    return 0;
}

当然还有我的服务器:

#include<stdio.h> //printf
#include<string.h> //memset
#include<stdlib.h> //exit(0);
#include<arpa/inet.h>
#include<sys/socket.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;

    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);
    memset(buf,'\0',BUFLEN);         
        //try to receive some data, this is a blocking call
        if ((recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen)) == -1)
        {
            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 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;
}

此代码运行良好。但是,我只想使用 UDP 发送一条消息。如果有一些变量,例如:intintstringint。如何隐藏到buf 以及更重要的 - 如何在服务器中接收变量?

【问题讨论】:

  • 有几件事:首先不要使用gets,它很危险并且自C99标准以来已被弃用并在C11标准中完全删除,请改用fgets。其次,你提供给recvfrom的地址结构长度是可以修改的,每次调用函数都需要设置。
  • 至于您的问题,如果您想在主机之间发送不同的数据,那么您可能应该发明某种协议来告诉您发送什么类型的数据.使用具有固定标头的结构(包括数据包类型以及有效负载大小)是一种解决方案。
  • @JoachimPileborg - 你能举个例子(回答问题)应该如何看待来自客户端的最简单数据包和服务器中接收的数据。
  • 不太好,TBH。它具有货物狂热的“memset”,假设传输总是文本,并且首先花费周期将接收缓冲区归零是确保空终止字符串的唯一方法:(
  • 一种简单的方法是将变量编码为 ASCII 文本(例如使用 snprintf()),然后使用您已有的代码发送该文本。然后接收器需要解析接收到的 ASCII 文本以再次提取原始值(例如使用 atoi())。这将是一种序列化方法,正如 DavidSchwartz 所提到的,这正是您真正要问的。序列化是将数据转换为可以放入 UDP 数据包(或 TCP 流)的一系列字节,其对应物是反序列化,接收方将它们重新组合成数据。

标签: c linux sockets udp


【解决方案1】:

因为系统可能在其二进制类型的细节上有所不同(例如,little-endian 与 big-endian、ints 的大小、结构的填充、空指针值等),因此应使用协议在传输过程中保留类型的语义。这可以通过使用一种著名的语义保留协议(例如,XDR https://en.wikipedia.org/wiki/External_Data_Representation、Google 的 protobuf https://en.wikipedia.org/wiki/Protocol_Buffers)或通过手动将类型编码为可移植格式(例如,将所有类型转换为字符串)来完成在发送方并在接收方将它们解析回二进制值)。

【讨论】:

  • IMO:转换为/从 ASCII 是最简单的方法(snprintf() 和 atoi()。但是如果你想在不同的时间发送不同的数据类型,那么 buffer[] 应该有第一个字节编码(可能由服务器和客户端都使用的枚举)指示哪些数据在缓冲区的其余部分[]
猜你喜欢
  • 2019-03-20
  • 2012-07-24
  • 2010-12-21
  • 1970-01-01
  • 1970-01-01
  • 2012-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多