• 深入UDP数据收发(下)


    问题

    UDP 是否其他一对多的数据收发方式?

    UDP 通信中的多播

    多播是向特定组的所有主机传输数据的方法,多播也称之为组播

    多播数据传输的特点:

    • 多播发送者针对特定的多播组,只发送一次数据,组内主机均可收到数据
    • 主机加入特定组,即可接收该组中的多播数据
    • 多播组可在IP地址范围内任意增加

    关键问题: 如何收发多播数据?

    多播组是个 D 类地址 (224.0.0.0 - 239.255.255.255)

    "加入多播组" 可理解为 UDP 网络程序进行的申请

    • 如: 申请接收发往 239.234.111.222 的多播数据
    • 即: 设置属性 (IPPROTO_IP, IP_ADD_MEMBERSHIP)

    发送多播数据的方式,与发送普通 UDP 数据的方式相同

    • 预备操作:属性设置,如:(IPPROTO_IP, IP_MULTICAST_TTL)

    注意事项

    加入同一个多播组的主机不一定在同一个网络

    因此,必须设置多播数据的最多转发次数 (TTL)

    • TTL (即:Time To Live) 是决定数据传输距离的主要因素
    • TTL 用整数表示,并且每经过1个路由器就减少1
    • 当 TTL 变为0时,数据无法继续被传递,只能销毁

    多播程序设计:发送端

    IP_MULTICAST_TTL

    • 用于设置多播的最远传输距离,默认:1

    IP_MULTICAST_IF

    • 用于设置多播数据从哪一个网络接口 (网卡) 发送出去,默认:0.0.0.0

    IP_MULTICAST_LOOP

    • 用于设置多播数据是否发送回本机,默认:1

     

    多播程序设计:接收端

    IP_ADD_MEMBERSHIP

    • 用于申请加入多播组,参数为:多播组地址和本机地址

     

    退出多播组

    UDP 多播程序设计 

    mul_tx.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. int main()
    7. {
    8. int server = 0;
    9. struct sockaddr_in addr = {0};
    10. struct sockaddr_in remote = {0};
    11. char buf[128] = "D.T.SoftWare\n";
    12. int ttl = 0;
    13. int loop = 0;
    14. struct in_addr maddr = {0};
    15. int len = 0;
    16. addr.sin_family = AF_INET;
    17. addr.sin_addr.s_addr = htonl(INADDR_ANY);
    18. addr.sin_port = htons(8888);
    19. remote.sin_family = AF_INET;
    20. remote.sin_addr.s_addr = indet_adr("234.1.1.168");
    21. remote.sin_port = htons(6666);
    22. server = socket(PF_INET, SOCK_DGRAM, 0);
    23. if(server < 0)
    24. {
    25. printf("server socket error\n");
    26. return -1;
    27. }
    28. if(bind(server, (struct sockaddr*)&addr, sizeof(addr)) == -1)
    29. {
    30. printf("udp server bind error\n");
    31. return -1;
    32. }
    33. len = sizeof(ttl);
    34. getsockopt(server, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, &len); // 组播的 TTL 默认为1,只能在本网段下进行组播消息的转发,如果需要将组播消息发送到别的网段上,则需要设置 TTL
    35. printf("default ttl = %d\n", ttl);
    36. ttl = 32;
    37. len = sizeof(ttl);
    38. setsockopt(server, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, len);
    39. printf("current ttl = %d\n", ttl);
    40. len = sizeof(ttl);
    41. getsockopt(server, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, &len);
    42. printf("default loop = %d\n", loop);
    43. /*
    44. loop = 0;
    45. len = sizeof(loop);
    46. setsockopt(server, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, len); // loop 设置为0,则不会将组播消息发送给发送端所在的 IP 地址
    47. printf("current loop = %d\n", loop);
    48. */
    49. maddr.s_addr = inet_addr("192.168.197.128");
    50. len = sizeof(maddr);
    51. setsockopt(server, IPPROTO_IP, IP_MULTICAST_IF, &maddr, len); // 如果本台主机上有多个网口则需要设置是从哪个 IP 地址发送组播消息
    52. len = sizeof(maddr);
    53. getsockopt(server, IPPROTO_IP, IP_MULTICAST_IF, &maddr, &len);
    54. printf("current IP = %s\n", inet_ntoa(maddr));
    55. printf("udp server start success!\n");
    56. while(1)
    57. {
    58. len = sizeof(remote);
    59. sendto(server, buf, strlen(buf), 0, (struct sockaddr*)&remote, len);
    60. sleep(1);
    61. }
    62. close(server);
    63. return 0;
    64. }

    mul_rx.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. int main()
    7. {
    8. int sock = 0;
    9. struct sockaddr_in remote = {0};
    10. struct sockaddr_in addr = {0};
    11. struct ip_mreq group = {0};
    12. char buf[128] = {0};
    13. int r = 0;
    14. socklen_t len = 0;
    15. addr.sin_family = AF_INET;
    16. addr.sin_addr.s_addr = htonl(INADDR_ANY);
    17. addr.sin_port = htons(6666);
    18. if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    19. {
    20. printf("client socket error\n");
    21. return -1;
    22. }
    23. if(bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0)
    24. {
    25. printf("udp client bind errpr\n");
    26. return -1;
    27. }
    28. group.imr_multiaddr.s_addr = inet_addr("234.1.1.168"); // 多播地址
    29. group.imr_interface.s_addr = htonl(INADDR_ANY); // 加入多播组的主机地址,由操作系统来选定是哪一个 ip 地址
    30. setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group));
    31. while(1)
    32. {
    33. len = sizeof(remote);
    34. r = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&remote, &len);
    35. if(r > 0)
    36. {
    37. buf[r] = 0;
    38. printf("Receive: %s\n", buf);
    39. }
    40. else
    41. {
    42. break;
    43. }
    44. }
    45. close(sock);
    46. }

    在实际项目中,因为多播数据的接收方和发送方可能不在同一个网络下,所以需要设置TTL,并且如果一个主机有多个网卡,最好直接指定好接收多播数据的IP地址,否则操作系统就会随机选择一个IP地址作为接收多播数据的地址,导致多播数据可能就接收不到了。

    实验结果如下图所示:

    小结 

    单播:一对一数据发送,即:指定目标主机发送数据

    广播

    • 本地广播:本地局域网广播数据,所有主机均可接收数据
    • 直接广播:指定网络广播数据,目标网络中的主机均可接收数据

    多播(组播)

    • 向指定的多播地址发送数据,"订阅"该地址的主机均可接收数据

    历史遗留问题

     

  • 相关阅读:
    golang中的iota
    【UE4】打包失败 Failed to build UATTempProj.proj
    7、ByteBuffer(方法演示1(切换读写模式,读写))
    初识C++|类与对象(上)
    Python 中 key 参数的含义及用法
    如何从SEO角度写好原创文章并吸引人
    MST2100是一款用于摩托车单相磁电机 调压器的控制IC
    从ELF文件谈起
    Mongodb
    KNN(下):数据分析 | 数据挖掘 | 十大算法之一
  • 原文地址:https://blog.csdn.net/qq_52484093/article/details/126312478