【Linux网络编程】基于UDP协议的socket编程

时间:2025-03-19 08:12:28

目录

    • 1、服务器端创建步骤
    • 2、客户端创建步骤
    • 3、recvfrom函数(经socket接收数据)
    • 4、sendto函数(经socket传送数据)
    • 5、示例
      • 5.1、服务器端
      • 5.2、客户端
    • 6、udp广播
      • 6.1、UDP 广播接收端步骤
      • 6.2、UDP 广播发送端步骤
      • 6.3、接收端示例
      • 6.2、发送端示例
    • 7、udp组播
      • 7.1、UDP 组播接收端步骤
      • 7.2、UDP组播发送端步骤
      • 7.3、接收端示例
      • 7.4、发送端示例

1、服务器端创建步骤

1、建立socket,使用socket()
2、绑定socket,使用bind()
3、以recvfrom()函数接收发送端传来的数据
4、关闭socket,使用close()

2、客户端创建步骤

1、建立Socket,使socket()
2、用sendto()函数向接收端发送数据
3、关闭socket,使用close()函数

3、recvfrom函数(经socket接收数据)

#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
类型:int
描述:Socket 文件描述符,表示已创建的 UDP Socket。

buf
类型:void *
描述:指向接收数据的缓冲区。

len
类型:size_t
描述:缓冲区的大小,即最大可接收的字节数。

flags
类型:int
描述:控制接收行为的标志位,通常设置为 0。常用标志包括:
MSG_WAITALL:等待所有数据到达。
MSG_DONTWAIT:非阻塞模式。

src_addr
类型:struct sockaddr *
描述:指向存储发送方地址的结构体。如果不需要发送方地址,可以设置为 NULL。

addrlen
类型:socklen_t *
描述:指向 src_addr 结构体大小的变量。调用前需要初始化为 src_addr 的大小,调用后会被更新为实际地址结构体的大小。

返回值

  • 成功:返回接收到的字节数。
  • 失败:返回 -1,并设置 errno 以指示错误类型。

4、sendto函数(经socket传送数据)

#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
类型:int
描述:Socket 文件描述符,表示已创建的 UDP Socket。

buf
类型:const void *
描述:指向要发送的数据的缓冲区。

len
类型:size_t
描述:要发送的数据的字节数。

flags
类型:int
描述:控制发送行为的标志位,通常设置为 0。常用标志包括:
MSG_DONTWAIT:非阻塞模式。
MSG_CONFIRM:确认数据链路层可达性。

dest_addr
类型:const struct sockaddr *
描述:指向目标地址的结构体,包含目标 IP 地址和端口。

addrlen
类型:socklen_t
描述:目标地址结构体的大小。

5、示例

5.1、服务器端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[])
{
    // 创建套接字
    int sid = socket(AF_INET, SOCK_DGRAM, 0);
    if (sid < 0)
    {
        perror("socket error");
        return -1;
    }

    // bind
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(10000);
    addr.sin_addr.s_addr = inet_addr(argv[1]);

    if (bind(sid, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind error");
        close(sid);
        return -1;
    }

    char buf[128] = "";
    while (1)
    {
        memset(buf,0,sizeof(buf));
        // 接收数据 recvfrom()
        struct sockaddr_in send_addr;
        int size = sizeof(send_addr);
        int len = recvfrom(sid, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&send_addr, &size);
        if (len < 0)
        {
            perror("recvform error");
            close(sid);
            return -1;
        }
        printf("recv buf = %s\n", buf);


        // char sendip[32];
        // inet_ntop(AF_INET, &send_addr.sin_addr.s_addr, sendip, sizeof(sendip));
        // printf("send ip = %s\n", sendip);

        memset(buf,0,sizeof(buf));
        // 发送数据 sendto
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(10001);
        addr.sin_addr.s_addr = inet_addr(argv[1]);

        // char *str = "hello world";
        printf("please input data:\n");
        fgets(buf, sizeof(buf) - 1, stdin);
        len = sendto(sid, buf, strlen(buf), 0, (struct sockaddr *)&addr, sizeof(addr));
        if (len < 0)
        {
            perror("sento error");
            close(sid);
            return -1;
        }
        printf("sendto len = %d\n", len);

        if(strncmp(buf,"quit",4) == 0)
            break;
    }
    close(sid);
}

5.2、客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[])
{
    // 创建套接字
    int sid = socket(AF_INET, SOCK_DGRAM, 0);
    if (sid < 0)
    {
        perror("socket error");
        return -1;
    }

    //bind
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(10001);
    addr.sin_addr.s_addr = inet_addr(argv[1]);

    if(bind(sid,(struct sockaddr *)&addr,sizeof(addr)) < 0)
    {
        perror("bind error");
        close(sid);
        return -1;
    }

    char buf[128] = "";
    while (1)
    {
        memset(buf,0,sizeof(buf));
        // 发送数据 sendto
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(10000);
        addr.sin_addr.s_addr = inet_addr(argv[1]);

        // char *str = "hello world";
        printf("please input data:\n");
        fgets(buf,sizeof(buf) - 1,stdin);
        int len = sendto(sid, buf, strlen(buf), 0, (struct sockaddr *)&addr, sizeof(addr));
        if (len < 0)
        {
            perror("sento error");
            close(sid);
            return -1;
        }
        printf("sendto len = %d\n", len);

        //接收recvfrom
        memset(buf,0,sizeof(buf));
        struct sockaddr_in client_addr;
        int size = sizeof(client_addr);
        len = recvfrom(sid,buf,sizeof(buf) - 1,0,(struct sockaddr*)&client_addr,&size);
        if (len < 0)
        {
            perror("recv error");
            close(sid);
            return -1;
        }
        printf("recv buf = %s\n",buf);
        if(strncmp(buf,"quit",4) == 0)
            break;
    }
    close(sid);
}

6、udp广播

广播地址

  • 广播地址是网络中所有主机都能接收的特殊 IP 地址。
  • 在 IPv4 中,广播地址分为两种:
    • 受限广播地址:255.255.255.255,发送到该地址的数据包会被同一子网内的所有主机接收。
    • 直接广播地址:例如,192.168.1.255,发送到该地址的数据包会被 192.168.1.0/24 子网内的所有主机接收。

UDP 广播的特点

  • UDP 是无连接的协议,适合广播场景。
  • 广播数据包会被同一网络中的所有主机接收,但只有监听指定端口的应用程序会处理这些数据。

6.1、UDP 广播接收端步骤

1、创建 UDP Socket
使用 socket() 函数创建一个 UDP Socket。

2、绑定本地地址和端口
使用 bind() 函数将 Socket 绑定到广播端口。

3、接收广播数据
使用 recvfrom() 函数接收广播数据。

4、关闭 Socket
使用 close() 函数关闭 Socket。

6.2、UDP 广播发送端步骤

1、创建 UDP Socket
使用 socket() 函数创建一个 UDP Socket。

2、设置广播选项
使用 setsockopt() 函数设置 SO_BROADCAST 选项,允许发送广播数据。

3、设置广播地址
定义目标广播地址(如 255.255.255.255)和端口号。

4、发送数据到广播地址
使用 sendto() 函数将数据发送到广播地址。

5、关闭 Socket
使用 close() 函数关闭 Socket。

示例

6.3、接收端示例

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[])
{
    // 创建套接字
    int sid = socket(AF_INET, SOCK_DGRAM, 0);
    if (sid < 0)
    {
        perror("socket error");
        return -1;
    }

    // bind
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(10000);
    addr.sin_addr.s_addr = INADDR_ANY;  

    if (bind(sid, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind error");
        close(sid);
        return -1;
    }

    char buf[128] = "";
    // 接收数据 recvfrom()
    struct sockaddr_in send_addr;
    int size = sizeof(send_addr);
    int len = recvfrom(sid, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&send_addr, &size);
    if (len < 0)
    {
        perror("recvform error");
        close(sid);
        return -1;
    }
    printf("recv buf = %s\n", buf);

    close(sid);
}

6.2、发送端示例

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[])
{
    // 创建套接字
    int sid = socket(AF_INET, SOCK_DGRAM, 0);
    if (sid < 0)
    {
        perror("socket error");
        return -1;
    }

    //打开广播模式
    int broadcast_on = 1;
    if(setsockopt(sid,SOL_SOCKET,SO_BROADCAST,&broadcast_on,sizeof(broadcast_on))<0)
    {
        perror("setsockopt broadcast error");
        close(sid);
        return -1;
    }
    // bind
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(10000);
    addr.sin_addr.s_addr = inet_addr(argv[1]);

    char buf[128] = "";
    memset(buf, 0, sizeof(buf));
    // 发送数据 sendto
    printf("please input data:\n");
    fgets(buf, sizeof(buf) - 1, stdin);
    int len = sendto(sid, buf, strlen(buf), 0, (struct sockaddr *)&addr, sizeof(addr));
    if (len < 0)
    {
        perror("sento error");
        close(sid);
        return -1;
    }
    printf("sendto len = %d\n", len);

    

    close(sid);
}

7、udp组播

组播地址

  • 组播地址是用于标识组播组的特殊 IP 地址。
  • 在 IPv4 中,组播地址范围为 224.0.0.0 到 239.255.255.255。
  • 常见的组播地址:
    • 224.0.0.1:所有主机组。
    • 224.0.0.2:所有路由器组。

组播的特点

  • 组播数据包只会被加入特定组播组的主机接收。
  • 组播适合一对多的通信场景,如视频流、在线会议等。

7.1、UDP 组播接收端步骤

1、创建 UDP Socket
使用 socket() 函数创建一个 UDP Socket。

2、绑定本地地址和端口
使用 bind() 函数将 Socket 绑定到组播端口。

3、加入组播组
使用 setsockopt() 函数设置 IP_ADD_MEMBERSHIP 选项,加入指定的组播组。

4、接收组播数据
使用 recvfrom() 函数接收组播数据。

5、关闭 Socket
使用 close() 函数关闭 Socket。

7.2、UDP组播发送端步骤

1、创建 UDP Socket
使用 socket() 函数创建一个 UDP Socket。

2、设置组播地址
定义目标组播地址(如 224.0.0.1)和端口号。

3、发送数据到组播地址
使用 sendto() 函数将数据发送到组播地址。

4、关闭 Socket
使用 close() 函数关闭 Socket。

7.3、接收端示例

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define UDP_GROUP "224.1.1.1"

int main(int argc, char *argv[])
{
    // 创建套接字
    int sid = socket(AF_INET, SOCK_DGRAM, 0);
    if (sid < 0)
    {
        perror("socket error");
        return -1;
    }

    // bind
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(10000);
    addr.sin_addr.s_addr = INADDR_ANY;  

    if (bind(sid, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind error");
        close(sid);
        return -1;
    }

    //设置组播    struct ip_mreq结构体 组播的结构体
    struct ip_mreq grep;
    grep.imr_multiaddr.s_addr = inet_addr(UDP_GROUP); //指定组播ip地址
    grep.imr_interface.s_addr = INADDR_ANY;//指定本地网络接口的ip地址,INADDR_ANY:系统自己选择合适接口

    if(setsockopt(sid,IPPROTO_IP,IP_ADD_MEMBERSHIP,&grep,sizeof(grep)) < 0)  //IPPROTO_IP :ip协议层
                                                                            //IP_ADD_MEMBERSHIP 加入组播的选项名称
    {
        perror("setdockopt group error");
        close(sid);
        return -1;
    }


    char buf[128] = "";
    // 接收数据 recvfrom()
    struct sockaddr_in send_addr;
    int size =