C语言多服务器多播组网框架

使用技术:

UDP ipv4 禁止本地回环 允许端口复用 超时等待 限制跳点 Echo

功能描述:

任意endpoint可主动发送多播,也可以收到信息后自动多播

前期准备:

开启操作系统多播广播功能,关闭系统防火墙,使用物理路由还需在路由开启广播多播功能

发送端和接收端运行不分先后 可无限复制多个接收端模拟集群

发送端:

#define _GNU_SOURCE
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>

#define MULTICAST_GROUP "239.0.0.1"
#define MULTICAST_PORT 55501
//发送端
int main()
{
    int server_sockfd;
    struct sockaddr_in server_sockaddr, multi_sockaddr, recv_sockaddr;
    memset(&server_sockaddr, 0, sizeof(server_sockaddr));
    memset(&multi_sockaddr, 0, sizeof(multi_sockaddr));
    memset(&recv_sockaddr, 0, sizeof(recv_sockaddr));
    socklen_t multi_sockaddr_len = sizeof(multi_sockaddr);
    socklen_t server_sockaddr_len = sizeof(server_sockaddr);
    socklen_t recv_sockaddr_len = sizeof(recv_sockaddr);
    ssize_t send_bytes, recv_bytes;
    char send_buf[1024] = "multicast from ubuntu";
    char recv_buf[1024] = {0};
    char recv_sockaddr_ip[INET_ADDRSTRLEN] = {0};
    // 用于加入多播网络
    struct ip_mreq mreq = {0};
    // 用于超时等待
    struct timeval tv = {0};
    tv.tv_sec = 1;
    tv.tv_usec = 0;
    // ipv4 udp
    server_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (server_sockfd == -1)
    {
        perror("socket");
    }
    // 地址端口复用
    int optval = 1;
    setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
    // 超时等待
    setsockopt(server_sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
    // 限制跳点
    uint8_t ttl = 1;
    setsockopt(server_sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
    // 禁止本地回环
    uint8_t loop = 0;
    setsockopt(server_sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
    // 绑定接收端口,与多播端口一致,监听任意地址
    server_sockaddr.sin_family = AF_INET;
    server_sockaddr.sin_port = htons(MULTICAST_PORT);
    server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    if ((bind(server_sockfd, (struct sockaddr *)&server_sockaddr, server_sockaddr_len)) == -1)
    {
        perror("bind");
    }
    // 加入多播组
    inet_pton(AF_INET, MULTICAST_GROUP, &mreq.imr_multiaddr.s_addr);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    setsockopt(server_sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
    // 设置发送地址为多播组
    multi_sockaddr.sin_family = AF_INET;
    multi_sockaddr.sin_port = htons(MULTICAST_PORT);
    inet_pton(AF_INET, MULTICAST_GROUP, &multi_sockaddr.sin_addr.s_addr);
    while (1)
    {
        sleep(1);
        send_bytes = sendto(server_sockfd, send_buf, strlen(send_buf),
                            0, (struct sockaddr *)&multi_sockaddr, multi_sockaddr_len);
        if (send_bytes == -1)
        {
            perror("sendto");
        }
        recv_bytes = recvfrom(server_sockfd, recv_buf, sizeof(recv_buf),
                              0, (struct sockaddr *)&recv_sockaddr, &recv_sockaddr_len);
        if (recv_bytes == -1)
        {
            perror("recvfrom");
        }
        inet_ntop(AF_INET, &recv_sockaddr.sin_addr, recv_sockaddr_ip, sizeof(recv_sockaddr_ip));
        // 收到消息则打印日志
        if (recv_bytes > 0)
        {
            printf("INET_ADDR : %s PORT : %d Message : %s\n", recv_sockaddr_ip, ntohs(recv_sockaddr.sin_port), recv_buf);
            memset(recv_buf, 0, sizeof(recv_buf));
            memset(&recv_sockaddr, 0, sizeof(recv_sockaddr));
        }
    }
    close(server_sockfd);
    return 0;
}

接收端:

#define _GNU_SOURCE
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>

#define MULTICAST_GROUP "239.0.0.1"
#define MULTICAST_PORT 55501
//接收端
int main()
{
    int server_sockfd;
    struct sockaddr_in server_sockaddr, multi_sockaddr, recv_sockaddr;
    memset(&server_sockaddr, 0, sizeof(server_sockaddr));
    memset(&multi_sockaddr, 0, sizeof(multi_sockaddr));
    memset(&recv_sockaddr, 0, sizeof(recv_sockaddr));
    socklen_t multi_sockaddr_len = sizeof(multi_sockaddr);
    socklen_t server_sockaddr_len = sizeof(server_sockaddr);
    socklen_t recv_sockaddr_len = sizeof(recv_sockaddr);
    ssize_t send_bytes, recv_bytes;
    char send_buf[1024] = "multicast from kali";
    char recv_buf[1024] = {0};
    char recv_sockaddr_ip[INET_ADDRSTRLEN] = {0};
    struct ip_mreq mreq = {0};
    server_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (server_sockfd == -1)
    {
        perror("socket");
    }
    // 禁止本地回环
    uint8_t loop = 0;
    setsockopt(server_sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
    // 允许端口复用
    int optval = 1;
    setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
    // 绑定地址为监听任意,绑定监听端口为多播端口
    server_sockaddr.sin_family = AF_INET;
    server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_sockaddr.sin_port = htons(MULTICAST_PORT);
    if ((bind(server_sockfd, (struct sockaddr *)&server_sockaddr, server_sockaddr_len)) == -1)
    {
        perror("bind");
    }

    // 加入多播组
    mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_GROUP);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    setsockopt(server_sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
    // 向多播组发送消息用
    multi_sockaddr.sin_family = AF_INET;
    multi_sockaddr.sin_port = htons(MULTICAST_PORT);
    inet_pton(AF_INET, MULTICAST_GROUP, &multi_sockaddr.sin_addr.s_addr);
    while (1)
    {
        recv_bytes = recvfrom(server_sockfd, recv_buf, sizeof(recv_buf),
                              0, (struct sockaddr *)&recv_sockaddr, &recv_sockaddr_len);
        if (recv_bytes == -1)
        {
            perror("recvfrom");
        }
        inet_ntop(AF_INET, &recv_sockaddr.sin_addr, recv_sockaddr_ip, sizeof(recv_sockaddr_ip));
        // 如果收到了消息就打印
        if (recv_bytes > 0)
        {
            printf("INET_ADDR : %s PORT : %d Message : %s\n", recv_sockaddr_ip, ntohs(recv_sockaddr.sin_port), recv_buf);
            memset(recv_buf, 0, sizeof(recv_buf));
            memset(&recv_sockaddr, 0, sizeof(recv_sockaddr));
        }
        sleep(1);
        // echo一条消息,也可主动发起
        send_bytes = sendto(server_sockfd, send_buf, strlen(send_buf),
                            0, (struct sockaddr *)&multi_sockaddr, multi_sockaddr_len);
        if (send_bytes == -1)
        {
            perror("sendto");
        }
    }
    close(server_sockfd);
    return 0;
}

相关推荐

  1. C语言服务器框架

    2024-03-26 06:02:02       18 阅读
  2. IP

    2024-03-26 06:02:02       42 阅读
  3. UDP<span style='color:red;'>多</span><span style='color:red;'>播</span>

    UDP

    2024-03-26 06:02:02      12 阅读
  4. C语言基于TCP的线程服务器

    2024-03-26 06:02:02       19 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-26 06:02:02       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-26 06:02:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-26 06:02:02       20 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-26 06:02:02       20 阅读

热门阅读

  1. npm常用命令详解

    2024-03-26 06:02:02       20 阅读
  2. 数据分析面试题(11~20)

    2024-03-26 06:02:02       19 阅读
  3. Web框架开发-BBS项目预备知识

    2024-03-26 06:02:02       14 阅读
  4. linux系统中docker镜像创建、导入导出和执行

    2024-03-26 06:02:02       18 阅读
  5. H3CNE:FTP

    H3CNE:FTP

    2024-03-26 06:02:02      18 阅读
  6. 502(bad gateway),404等网页状态码

    2024-03-26 06:02:02       16 阅读
  7. 【Docker】docker和docker-compose一键安装脚本(linux)

    2024-03-26 06:02:02       17 阅读
  8. Istio 部署 Spring Coud 微服务应用

    2024-03-26 06:02:02       17 阅读
  9. Word字号与磅值与行距

    2024-03-26 06:02:02       19 阅读
  10. 洛谷刷题 | B3623 枚举排列

    2024-03-26 06:02:02       15 阅读
  11. 图论记录之最短路迪杰斯特拉

    2024-03-26 06:02:02       15 阅读
  12. 文心一言 vs GPT-4 —— 全面横向比较

    2024-03-26 06:02:02       17 阅读
  13. Sequelize一个易用且基于 promise 的 Node.js ORM 工具

    2024-03-26 06:02:02       17 阅读