UDP编程

前置知识

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

sockfd:表示要发送数据的套接字描述符。它是由socket函数创建的套接字返回的文件描述符。

buf:是一个指向要发送数据的缓冲区的指针。数据将从这个缓冲区复制到套接字发送。

len:表示要发送的数据的长度,以字节为单位。

flags:是一个可选的参数,用于控制发送操作的行为。它可以取以下标志之一或它们的组合:

0:没有特殊的标志,表示普通的发送操作。
MSG_DONTROUTE:指示数据包不应该被路由。
MSG_OOB:用于发送TCP数据(带外数据)。
MSG_NOSIGNAL:在发送数据时忽略SIGPIPE信号,如果连接已断开,则返回错误而不是导致进程终止。

该函数返回发送的字节数,如果出现错误,返回值为-1。以下是函数返回值的一些可能情况:

= 0:表示发送成功,并返回发送的字节数。

-1:表示发送过程中发生错误。可以通过检查全局变量errno来确定具体错误的类型。常见的错误包括:

EINTR:操作被信号中断。
EAGAIN 或 EWOULDBLOCK:套接字已设置为非阻塞模式,并且发送操作将阻塞。
EBADF:无效的文件描述符。
EFAULT:buf指针指向无效的内存地址。
ENOTCONN:套接字未连接。

       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t recv(int sockfd, void *buf, size_t len, int flags);

sockfd:表示要接收数据的套接字描述符。它是由socket函数创建的套接字返回的文件描述符。

buf:是一个指向接收数据的缓冲区的指针。接收到的数据将被复制到这个缓冲区。

len:表示接收数据的最大长度,以字节为单位。如果接收到的数据长度超过len,则超出部分的数据将被截断。

flags:是一个可选的参数,用于控制接收操作的行为。它可以取以下标志之一或它们的组合:
0:没有特殊的标志,表示普通的接收操作。 MSG_DONTWAIT 或 MSG_NONBLOCK:以非阻塞模式接收数据。
MSG_OOB:接收带外数据。 MSG_PEEK:接收数据但不从接收队列中删除数据。

该函数返回接收到的字节数,如果出现错误,返回值为-1。以下是函数返回值的一些可能情况:
= 0:表示接收成功,并返回接收到的字节数。
0:表示连接已关闭,没有更多数据可接收。

-1:表示接收过程中发生错误。可以通过检查全局变量errno来确定具体错误的类型。常见的错误包括:
EINTR:操作被信号中断。 EAGAIN 或 EWOULDBLOCK:套接字已设置为非阻塞模式,并且没有可用的数据。
EBADF:无效的文件描述符。 EFAULT:buf指针指向无效的内存地址。

       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

sockfd:表示要发送数据的套接字描述符。它是由socket函数创建的套接字返回的文件描述符。

buf:是一个指向要发送数据的缓冲区的指针。数据将从这个缓冲区复制到套接字发送。

len:表示要发送的数据的长度,以字节为单位。

flags:是一个可选的参数,用于控制发送操作的行为。它可以取以下标志之一或它们的组合:
0:没有特殊的标志,表示普通的发送操作。 MSG_DONTROUTE:指示数据包不应该被路由。
MSG_OOB:用于发送紧急数据(带外数据)。
MSG_NOSIGNAL:在发送数据时忽略SIGPIPE信号,如果连接已断开,则返回错误而不是导致进程终止。

dest_addr:是一个指向目标地址结构的指针,其中包含要发送到的目标地址信息。该结构可以是sockaddr、sockaddr_in或sockaddr_in6,具体取决于套接字的地址类型。

addrlen:表示目标地址结构的长度,以字节为单位。对于IPv4地址,通常为sizeof(struct
sockaddr_in);对于IPv6地址,通常为sizeof(struct sockaddr_in6)。

该函数返回发送的字节数,如果出现错误,返回值为-1。以下是函数返回值的一些可能情况:

= 0:表示发送成功,并返回发送的字节数。

-1:表示发送过程中发生错误。可以通过检查全局变量errno来确定具体错误的类型。常见的错误包括:

EINTR:操作被信号中断。 EAGAIN 或 EWOULDBLOCK:套接字已设置为非阻塞模式,并且发送操作将阻塞。
EBADF:无效的文件描述符。
EFAULT:buf或dest_addr指针指向无效的内存地址。 ENOTCONN:套接字未连接。

       #include <sys/types.h>
       #include <sys/socket.h>


       ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

sockfd:表示要接收数据的套接字描述符。它是由socket函数创建的套接字返回的文件描述符。

buf:是一个指向接收数据的缓冲区的指针。接收到的数据将被复制到这个缓冲区。

len:表示接收数据的最大长度,以字节为单位。如果接收到的数据长度超过len,则超出部分的数据将被截断。

flags:是一个可选的参数,用于控制接收操作的行为。它可以取以下标志之一或它们的组合:
0:没有特殊的标志,表示普通的接收操作。 MSG_DONTWAIT 或 MSG_NONBLOCK:以非阻塞模式接收数据。
MSG_OOB:接收带外数据。
MSG_PEEK:接收数据但不从接收队列中删除数据。

src_addr:是一个指向发送者地址结构的指针,用于存储发送者的地址信息。在函数调用之前,需要将该结构初始化为目标地址结构的类型,例如sockaddr_in或sockaddr_in6。

addrlen:是一个指向存储发送者地址结构长度的变量的指针。在函数调用之前,需要将该变量初始化为src_addr指向的结构的大小。

该函数返回接收到的字节数,如果出现错误,返回值为-1。以下是函数返回值的一些可能情况:

= 0:表示接收成功,并返回接收到的字节数。

0:表示连接已关闭,没有更多数据可接收。

-1:表示接收过程中发生错误。可以通过检查全局变量errno来确定具体错误的类型。常见的错误包括:

EINTR:操作被信号中断。
EAGAIN 或 EWOULDBLOCK:套接字已设置为非阻塞模式,并且没有可用的数据。
EBADF:无效的文件描述符。
EFAULT:buf或src_addr指针指向无效的内存地址

UDP流程示意图

在这里插入图片描述

UDP(User Datagram Protocol,用户数据报协议)是一种网络传输协议,位于传输层,提供面向无连接的数据报传输服务。与TCP(Transmission Control Protocol,传输控制协议)相比,UDP更为简单、轻量级,但不提供可靠的数据传输和流控制机制

无连接性:UDP是一种无连接的协议,发送端和接收端在通信之前不需要建立连接。每个UDP数据报都是独立的单元,可以独立发送、接收和处理,没有固定的数据传输顺序。

不可靠性:UDP不提供可靠的数据传输机制。它不保证数据报的完整性、顺序性或可靠性。发送的数据报可能会丢失、重复、乱序或损坏。应用程序需要自行处理这些问题。

高效性:由于UDP没有建立连接和维护状态的开销,以及较小的首部开销,UDP具有较低的通信延迟和网络负载。它适用于实时应用程序或对传输延迟要求较低的场景。

支持广播和多播:UDP支持广播(向局域网内的所有主机发送数据)和多播(向特定组内的主机发送数据)功能,可以在局域网中有效地分发数据。

适用场景:UDP适用于一些特定的应用场景,如实时音视频传输(如语音通话、视频会议)、网络广播、域名解析(DNS)等。在这些场景下,速度和实时性比可靠性更重要。

服务器端代码

没有使用线程和进程,但是可以实现并发处理。

int udpserver()
{
    printf("%s %d\n", __func__, __LINE__);
    struct sockaddr_in addr_in;
    int fd = -1;
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
        printf("%s %d fd<0\n", __func__, __LINE__);
        return 0;
    }

    int b_reuse = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));

    //  2、绑定
    memset(&addr_in, 0, sizeof(addr_in));  // 将变量addr_in置0
    addr_in.sin_port = htons(SERVER_PORT); // 将端口号从本地字节序转换为网络字节序
    addr_in.sin_family = AF_INET;
    addr_in.sin_addr.s_addr = htonl(INADDR_ANY); // 任意IP可以运行
    int rec = bind(fd, (struct sockaddr *)&addr_in, sizeof(addr_in));
    if (rec < 0)
    {
        printf("%s %d rec<0\n", __func__, __LINE__);
        return 0;
    }

    struct sockaddr_in clientin;
    bzero(&clientin, sizeof(clientin));
    char buf[256];
    bzero(buf, sizeof(buf));
    socklen_t addr_len = sizeof(clientin);
    int reclen = -1;
    printf("%s %d bind ok \n", __func__, __LINE__);
    while (1)
    {
        printf("%s %d 111111111111 \n", __func__, __LINE__);
        bzero(buf, sizeof(buf));
        reclen = 0;
        reclen = recvfrom(fd, buf, 256 - 1, 0, (struct sockaddr *)&clientin, &addr_len);
        printf("%s %d reclen  = %d\n", __func__, __LINE__, reclen);
        if (reclen < 0)
        {
            printf("%s %d reclen < 0\n", __func__, __LINE__);
            continue;
        }
        else
        {
            printf("%s %d 11111reclen  = %d\n", __func__, __LINE__, reclen);
        }

        printf("%s %d buf = %s\n", __func__, __LINE__, buf);
        char ipv4_arr[16];

        if (!inet_ntop(AF_INET, (void *)&clientin.sin_addr, ipv4_arr, sizeof(clientin)))
        {
            printf("%s %d err \n", __func__, __LINE__);
        }
        printf("Recived from(%s :%d),data:%s", ipv4_arr, ntohs(clientin.sin_port), buf);
        if (!strncasecmp(buf, "exit", strlen("exit")))
        {
            printf("Recived from(%s :%d) exit", ipv4_arr, ntohs(clientin.sin_port));
            return 0;
        }
    }
    close(fd);
    return 0;
}

客户端代码

执行方法:

/*
.client server_ip server_port
*/

void usage(char *s)
{
    printf("%s %d %s serv_ip ser_port\n", __func__, __LINE__, s);
    printf("%s %d  serv_ip : server ip address\n", __func__, __LINE__);
    printf("%s %d  ser_port: server port (>5000)\n", __func__, __LINE__);
}

/*
.client server_ip server_port
*/
int udpclient(int argc, char *argv[])
{
    printf("%s %d\n", __func__, __LINE__);

    if (argc < 3)
    {
        usage(argv[0]);
    }
    int fd = -1;
    int port = 4004;
    // 1、创建套接字 UDP
    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) //注意这个地方!!!
    {
        printf("%s %d socket < 0\n", __func__, __LINE__);
        return 0;
    }
    port = atoi(argv[2]);

    struct sockaddr_in clientin;
    bzero(&clientin, sizeof(clientin));
    clientin.sin_family = AF_INET;   // 设置地址属性
    clientin.sin_port = htons(port); // 设置端口号
    int rec = inet_pton(AF_INET, argv[1], (void *)&clientin.sin_addr.s_addr);
    if (rec != 1)
    {
        printf("%s %d rec!= 1\n", __func__, __LINE__);
        return 0;
    }
    char buf[1024];
    while (1)
    {
        fprintf(stderr,"pls input string:");
        memset(buf, 0, 1024);
        char *rec_p = fgets(buf, 1024 - 1, stdin);
        if (rec_p == NULL)
        {
            printf("%s %d rec_p == NULL\n", __func__, __LINE__);
            continue;
        }
        sendto(fd, buf, strlen(buf), 0, (struct sockaddr *)&clientin, sizeof(clientin));
        int recstr = strncasecmp(buf, "exit", strlen("exit"));
        if (0 == recstr)
        {
            printf("%s %d q======\n", __func__, __LINE__);
            break;
        }
    }
close(fd);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

li星野

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值