祝各位道友念头通达
GitHub Gitee 语雀 打赏

UDP 组播 广播 客户端 服务端 (linux C)

组播网络调试过程中碰见的问题

1.在虚拟机和windows之间组播或是广播通信的时候,如果接收端或者发送端是windows,需要绑定到虚拟网卡的IP地址,绑定代码需要用到参数 IP_MULTICAST_IF

//以下两行是指定网卡发送数据包
unsigned long addr = inet_addr("192.168.206.137");
// IP_MULTICAST_IF 该参数设置组播的网络接口,会从给定的网络接口发送,另一个网络接口会忽略此数据
// INADDR_ANY 是选定默认的网络接口,一般由系统内部自行进行选择,一般会选择 路由表 绑定一个明确的地址
setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&addr, sizeof(addr));

2.在python接收组播代码中有绑定到网卡的代码示例,如下文 python 接收组播示例

udp 广播, 服务端, 一直发送数据

适用单网卡,多网卡需要自行绑定网卡

    int scoket_id = -1;
    scoket_id = socket(AF_INET, SOCK_DGRAM, 0);
    if(scoket_id < 1) {
        printf("-error- create socket failed %d\n", scoket_id);
        exit(errno);
    }

    struct sockaddr_in server_c, client_c;
    int slen = sizeof(server_c);
    int clen = sizeof(client_c);

    server_c.sin_family = AF_INET;
    server_c.sin_addr.s_addr = htonl(INADDR_ANY);
    server_c.sin_port = htons(4444);

    if(bind(scoket_id,(struct sockaddr*)&server_c, slen) < 0) {
        perror("-error- bind error");
        exit(errno);
    }

    client_c.sin_family = AF_INET;
    client_c.sin_port = htons(1111);
    inet_pton(AF_INET,"192.168.100.255",&client_c.sin_addr.s_addr);
	// 注意 广播的范围地址, 同网段下的 255

    /*
     * 设置广播属性,打开服务器广播权限
     */
    if (setsockopt(scoket_id, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
        fprintf(stderr, "initSvr: Socket options set error.\n");
        exit(errno);
    }

    printf("-----------> 1\n");
    unsigned char buff[10] = {'1','2','3','4','5','6','7','8','8','0'};
    while (1)
    {
        int rlen = sendto(scoket_id, buff, 10, 0, (struct sockaddr*)&client_c, clen);
        printf("-send data- %d \n", rlen);
        sleep(1);
    }

udp 组播, 一直接收数据

适用单网卡,多网卡需要自行绑定网卡

    int scoket_id = -1;
    scoket_id = socket(AF_INET, SOCK_DGRAM, 0);
    if(scoket_id < 1) {
        printf("-error- create socket failed %d\n", scoket_id);
        exit(errno);
    }
    pnode_tmp->node_info.socket_c = scoket_id;
    pnode_tmp->send_fail_n = 0;

    struct sockaddr_in server_c;
    struct ip_mreq mreq;
    int slen = sizeof(server_c);
    int clen = sizeof(mreq);

    server_c.sin_family = AF_INET;
    server_c.sin_addr.s_addr = htonl(INADDR_ANY);
    server_c.sin_port = htons(4444);     
   
    if(bind(scoket_id,(struct sockaddr*)&server_c, slen) < 0) {
        perror("-error- bind error");
        exit(errno);
    }

    /*加入多播组*/  
    mreq.imr_interface.s_addr = inet_addr("192.168.100.100");  
    mreq.imr_multiaddr.s_addr = inet_addr("232.0.0.10");  
    if (setsockopt(scoket_id, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
    {  
        perror("setsockopt");
        return -1;  
    }
    
    printf("-----------> 1\n");
    unsigned char buff[10] = {'1','2','3','4','5','6','7','8','8','0'};
    while (1)
    {
		int rlen = recvfrom(sockfd, buff, 10, 0, (struct sockaddr *)&server_c, &slen);
        printf("-recv data- %d \n", rlen);
        sleep(1);
    }

udp 组播, 一直发送数据

适用单网卡,多网卡需要自行绑定网卡

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
 
int main(int argc, char **argv)
{
    struct sockaddr_in peeraddr;
    int sockfd;
    unsigned int socklen;
 
    /* 创建 socket 用于UDP通讯 */
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket\n");
        exit(errno);
    }
    printf("socket created success!!!\n");
    socklen = sizeof(struct sockaddr_in);
 
    /* 设置对方的端口和IP信息 */
    memset(&peeraddr, 0, socklen);
    peeraddr.sin_family = AF_INET;
    peeraddr.sin_port = htons(4444);
 
    if (inet_pton(AF_INET, "224.0.0.10", &peeraddr.sin_addr) <= 0) {
        printf("wrong group address!\n");
        exit(0);
    }
 
    /* 循环接受用户输入的消息发送组播消息 */
    unsigned char buff[10] = {'1','2','3','4','5','6','7','8','8','0'};
    while(1) {
        if (sendto(sockfd, buff, 10, 0, (struct sockaddr *)&peeraddr, sizeof(struct sockaddr_in)) < 0) {
            printf("sendto error!\n");
            exit(errno);
        }
        printf("client sendto success!\n");
        sleep(1);
    }
    return 0;
}

python 接收组播数据

适用多网卡,已经设置了指定IP(192.168.206.1)的网口

import threading, socket, select, traceback, sys, struct, re, random
from time import sleep
isclose = 0
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #创建一个套接字
udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
udp_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255)
udp_socket.setsockopt(
    socket.IPPROTO_IP,
    socket.IP_ADD_MEMBERSHIP,
    socket.inet_aton("232.0.0.1") +   #指定组播号
    socket.inet_aton("192.168.206.1") #指定网卡,如果是虚拟机,需要绑定到指定虚拟机的端口号
)
udp_socket.bind(('0.0.0.0', 4444)) #绑定本地信息,自己电脑的ip和程序的端口。ip一般不用写,表示本机的任意一个ip,端口大于常用的1023就可以

while True:
  try:
      infds, outfds, errfds = select.select([udp_socket,], [], [], 5)
      if len(infds) > 0:
          udp_data = udp_socket.recvfrom(1024)           #接收数据,1024表示本次接收的最大字节数
          print(udp_data)
          if len(udp_data[0]) == 21:
              fmt = '!HBffffH'
              #c8 c9 01 41 58 a3 d7 41 58 a3 d7 41 58 a3 d7 41 58 a3 d7 00 15
              
              rbuf = struct.unpack(fmt, udp_data[0])
              if rbuf[0] == 0xc8c9:
                  print(rbuf)
                  arg1 = round(rbuf[2], 2)
                  arg2 = round(rbuf[3], 2)
                  arg3 = round(rbuf[4], 2)
                  arg4 = round(rbuf[5], 2)
          else:
            print("数据格式不正确...")
      else:
          print("正常连接...")
          
      if isclose != 0:
          udp_socket.shutdown(socket.SHUT_RDWR)
          break
  except Exception as ex:
          traceback.print_exc()
          print(ex)
          break;
posted @ 2022-06-23 16:58  韩若明瞳  阅读(784)  评论(0编辑  收藏  举报