linux网络编程--广播与组播

广播

  前面介绍的数据包发送方式只有一个接收方,称为单播

  如果发送给局域网中的所有主机,称为广播

  只有用户数据报(使用UDP协议)套接字才能广播

广播地址:

  以192.168.1.0(255.255.255.0)网段为例,最大的主机地址192.168.1.255代表该网段的广播地址

  发送该地址的数据包被所有主机接收

  255.255.255.255在所有网段中都代表广播地址

广播发送

  创建用户数据报套接字

  缺省创建的套接字不允许广播数据包,需要设置属性--setsockopt可以设置套接字属性

  接收方指定为广播地址

  指定端口信息

  发送数据包

设置套接口的选项:
  #include <sys/types.h>
  #include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
    sockfd:标识一个套接口的描述字。
    level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。
    optname:需设置的选项。
    optval:指针,指向存放选项待设置的新值的缓冲区
    optlen:optval缓冲区长度。
成功返回0,失败返回-1并设置errno
广播发送代码:
sockfd=socket()
......
int on=1
setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
.........
sendto(....);

广播接收:

创建用户数据报套接字

绑定ip地址(广播ip或0.0.0.0)和端口

  绑定的端口必须和发送方的指定的端口相同

等待接收数据

实例如下:

send.c

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

#define  err_log(errlog)  do{perror(errlog); exit(1);}while(0)
#define   N  128

// ./send 192.168.0.255 10000
int main(int argc, const char *argv[])
{
    int sockfd;
    struct sockaddr_in broadcastaddr;
    struct sockaddr_in clientaddr;
    socklen_t addrlen = sizeof(clientaddr);
    char buf[N] = {};

    if(argc < 3)
    {
        fprintf(stderr, "usage:%s serverip port.
", argv[0]);
        return -1;
    }

    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        err_log("fail to socket");
    }

    broadcastaddr.sin_family = AF_INET;
    broadcastaddr.sin_addr.s_addr = inet_addr(argv[1]);
    broadcastaddr.sin_port = htons(atoi(argv[2]));

    int on = 1;

    if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0)
    {
        err_log("fail to setsockopt");
    }

    while(1)
    {
        printf("Input > ");
        fgets(buf, N, stdin);
        buf[strlen(buf)-1] = '';

        if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&broadcastaddr, addrlen) < 0)
        {
            err_log("fail to sendto");
        }

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

        printf("%s
", buf);

    }

    close(sockfd);

    return 0;
}

recv.c

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

#define  err_log(errlog)  do{perror(errlog); exit(1);}while(0)
#define   N  128

int main(int argc, const char *argv[])
{
    int sockfd;
    struct sockaddr_in broadcastaddr;
    struct sockaddr_in clientaddr;
    socklen_t addrlen = sizeof(clientaddr);
    char buf[N] = {};

    if(argc < 3)
    {
        fprintf(stderr, "usage:%s serverip port.
", argv[0]);
        return -1;
    }

    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        err_log("fail to socket");
    }

    broadcastaddr.sin_family = AF_INET;
    broadcastaddr.sin_addr.s_addr = inet_addr(argv[1]);
    broadcastaddr.sin_port = htons(atoi(argv[2]));

    if(bind(sockfd, (struct sockaddr*)&broadcastaddr, sizeof(broadcastaddr)) < 0)
    {
        err_log("fail to bind");
    }


    while(1)
    {
        if(recvfrom(sockfd, buf, N, 0, (struct sockaddr*)&clientaddr, &addrlen) < 0)
        {
            err_log("fail to recvfrom");
        }

        printf("From clientaddr:%s
", buf);
        strcat(buf, " from server...");

    }

    close(sockfd);


    
    return 0;
}

编译运行测试

  ./send 192.168.1.255  10000

分别在两台局域网内的机器上测试

./recv 192.168.1.255  10000 收到

./recv 192.168.1.255  10000 收到

(2)组播(多播)

  单播方式只能发送给一个接收方

  广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常通信

  组播(多播)是一种折中的方式只有加入某个多播组的主机才能收到数据

  多播方式既可以发送给多个主机,又能避免广播那样带来带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)

组播发送:

  创建用户数据报套接字

  接收方地址指定为组播地址

  指定端口信息

  发送数据包

组播接收:

  创建用户数据报套接字

  加入多播组

  绑定ip地址(加入组的组ip或0.0.0.0)和端口---绑定的端口必须和发送方指定的端口相同

  等待接收数据

加入多播组

struct ip_mreq

{

  struct in_addr imr_multiaddr;

  struct in_addr  imr_interface;

}

   struct ip_mreq req;
  bzero(&req,sizeof(req)) req.imr_multiaddr.s_addr = inet_addr(argv[1]); req.imr_interface.s_addr = htonl(INADDR_ANY); if(setsockopt(sockfd, IPPROTO_IP,IP_ADD_MEMBERSHIP, &req, sizeof(req)) < 0) { err_log("fail to setsockopt"); }

 

send.c

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

#define  err_log(errlog)  do{perror(errlog); exit(1);}while(0)
#define   N  128

// ./send 192.168.0.255 10000
int main(int argc, const char *argv[])
{
    int sockfd;
    struct sockaddr_in groupcastaddr;
    struct sockaddr_in clientaddr;
    socklen_t addrlen = sizeof(clientaddr);
    char buf[N] = {};

    if(argc < 3)
    {
        fprintf(stderr, "usage:%s serverip port.
", argv[0]);
        return -1;
    }

    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        err_log("fail to socket");
    }

    groupcastaddr.sin_family = AF_INET;
    groupcastaddr.sin_addr.s_addr = inet_addr(argv[1]);
    groupcastaddr.sin_port = htons(atoi(argv[2]));

    while(1)
    {
        printf("Input > ");
        fgets(buf, N, stdin);
        buf[strlen(buf)-1] = '';

        if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&groupcastaddr, addrlen) < 0)
        {
            err_log("fail to sendto");
        }

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

        printf("%s
", buf);

    }

    close(sockfd);

    return 0;
}

recv.c

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

#define  err_log(errlog)  do{perror(errlog); exit(1);}while(0)
#define   N  128

int main(int argc, const char *argv[])
{
    int sockfd;
    struct sockaddr_in groupcastaddr;
    struct sockaddr_in clientaddr;
    socklen_t addrlen = sizeof(clientaddr);
    char buf[N] = {};

    if(argc < 3)
    {
        fprintf(stderr, "usage:%s serverip port.
", argv[0]);
        return -1;
    }

    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        err_log("fail to socket");
    }

    groupcastaddr.sin_family = AF_INET;
    groupcastaddr.sin_addr.s_addr = inet_addr(argv[1]);
    groupcastaddr.sin_port = htons(atoi(argv[2]));

    if(bind(sockfd, (struct sockaddr*)&groupcastaddr, sizeof(groupcastaddr)) < 0)
    {
        err_log("fail to bind");
    }

    struct ip_mreq req;
    req.imr_multiaddr.s_addr = inet_addr(argv[1]);
//    req.imr_interface.s_addr = inet_addr("192.168.0.196");
    req.imr_interface.s_addr = htonl(INADDR_ANY);

    if(setsockopt(sockfd, IPPROTO_IP,IP_ADD_MEMBERSHIP, &req, sizeof(req)) < 0)
    {
        err_log("fail to setsockopt");
    }

    while(1)
    {
        if(recvfrom(sockfd, buf, N, 0, (struct sockaddr*)&clientaddr, &addrlen) < 0)
        {
            err_log("fail to recvfrom");
        }

        printf("From clientaddr:%s
", buf);

    }

    close(sockfd);


    
    return 0;
}
原文地址:https://www.cnblogs.com/bwbfight/p/9304386.html