14.1 多播

为了解决用户数量庞大,从而需要创建多个套接字的问题,引入了多播的方案。多播方式的数据传输是基于UDP完成的。

14.1.1 多播的数据传输方式及流量方面的优点

  • 多播服务器端针对特定的多播组,只发送一次数据
  • 即使只发送1次数据,但该组内的所有客户端都会接收到数据
  • 多播组数可在IP地址范围内任意增加
  • 加入特定组即可接收发往该多播组的数据

多播组是D类IP地址(224.0.0.0~239.255.255.255)

14.1.2 路由(Routing)和TTL(Time to Live,生存时间),以及加入组的方法

TTL是Time to Live的简写,是决定“数据包传递距离”的主要因素。TTL表示每经过一个路由,其值就会减一,一直减到0为止,数值为0时,也就无法再次传递数据包

  1. struct ip_mreq
  2. {
  3. struct in_addr imr_multiaddr;//第一个成员中写入加入的组IP
  4. struct in_addr imr_interface;//第二个成员中写入加入该组的套接字所属主机的IP地址
  5. }

14.1.3 实现多播Sender 和 Receiver

  • Sender:向AAA组广播文件中保存的新闻信息。
  • Receiver接收传递到AAA组的新闻信息。
  1. //news_sender.c
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<string.h>
  5. #include<unistd.h>
  6. #include<arpa/inet.h>
  7. #include<sys/socket.h>
  8. #define TTL 64
  9. #define BUF_SIZE 30
  10. void error_handling(char *message);
  11. int main(int argc, char *argv[])
  12. {
  13. int send_sock;
  14. struct sockaddr_in mul_adr;
  15. int time_live=TTL;
  16. FILE *fp;
  17. char buf[BUF_SIZE];
  18. if(argc!=3){
  19. printf("Usage: %s <GroupIP> <PORT>\n", argv[0]);
  20. exit(1);
  21. }
  22. send_sock=socket(PF_INET, SOCK_DGRAM, 0);
  23. memset(&mul_adr, 0, sizeof(mul_adr));
  24. mul_adr.sin_family=AF_INET;
  25. mul_adr.sin_addr.s_addr=inet_addr(argv[1]);
  26. mul_adr.sin_port=htons(atoi(argv[2]));
  27. setsockopt(send_sock, IPPROTO_TP, IP_MULTICAST_TTL, (void*)&time_live, sizeof(time_live));
  28. if((fp=fopen("news.txt", "r"))==NULL)
  29. error_handling("fopen() error");
  30. while(!feof(fp))
  31. {
  32. fgets(buf, BUF_SIZE, fp);
  33. sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr*)&mul_adr, sizeof(mul_adr));
  34. sleep(2);
  35. }
  36. fclose(fp);
  37. close(send_sock);
  38. return 0;
  39. }
  40. void error_handling(char *message)
  41. {
  42. fputs(message, stderr);
  43. fputc('\n', stderr);
  44. exit(1);
  45. }
  1. //news_receiver.c
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<string.h>
  5. #include<unistd.h>
  6. #include<arpa/inet.h>
  7. #include<sys/socket.h>
  8. #define BUF_SIZE 30
  9. void error_handling(char *message);
  10. int main(int argc, char *argv[])
  11. {
  12. int recv_sock;
  13. int str_len;
  14. char buf[BUF_SIZE];
  15. struct sockaddr_in adr;
  16. struct ip_mreq join_adr;
  17. if(argc!=3){
  18. printf("Usage: %s <GroupIP> <PORT>\n", argv[0]);
  19. exit(1);
  20. }
  21. recv_sock=socket(PF_INET, SOCK_DGRAM, 0);
  22. memset(&adr, 0, sizeof(adr));
  23. adr.sin_family=AF_INET;
  24. adr.sin_addr.s_addr=htonl(INADDR_ANY);
  25. adr.sin_port=htons(atoi(argv[2]));
  26. if(bind(recv_sock, (struct sockaddr*)&adr, sizeof(adr))==-1)
  27. error_handling("bind() error");
  28. join_adr.imr_multiaddr.s_addr=inet_addr(argv[1]);
  29. join_adr.imr_interface.s_addr=htonl(INADDR_ANY);
  30. setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)&join_adr, sizeof(join_adr));
  31. while(1)
  32. {
  33. str_len=recvfrom(recv_sock, buf, BUF_SIZE-1, 0, NULL, 0);
  34. if(str_len<0)
  35. break;
  36. buf[str_len]=0;
  37. fputs(buf, stdout);
  38. }
  39. close(recv_sock);
  40. return 0;
  41. }
  42. void error_handling(char *message)
  43. {
  44. fputs(message, stderr);
  45. fputc('\n', stderr);
  46. exit(1);
  47. }

14.2 广播

广播定义就是一次性向多个主机发送数据,在这一点上与多播类似,但是传输数据的范围有区别。多播即使在跨越不同网络的情况下,只要加入多播组就可以接收数据。

14.2.1 广播的理解及实现方法

  • 直接广播(Directed Broadcast)
  • 本地广播(Local Broadcast)

二者在代码实现上的差别主要在于IP地址直接广播IP地址中除了网络地址外,其余主机地址全部设置为1

14.2.2 实现广播数据的Sender和Receiver

//news_sender_brd.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int send_sock;
    struct sockaddr_in broad_adr;
    FILE *fp;
    char buf[BUF_SIZE];
    int so_brd=1;
    if(argc!=3){
        printf("Usage : %s <Boradcast IP> <PORT>\n", argv[0]);
        exit(1);
    }

    send_sock=socket(PF_INET, SOCK_DGRAM, 0);
    memset(&broad_adr, 0, sizeof(broad_adr));
    broad_adr.sin_family=AF_INET;
    broad_adr.sin_addr.s_addr=inet_addr(argv[1]);
    broad_adr.sin_port=htons(atoi(argv[2]));

    setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (void*)&so_brd, sizeof(so_brd));
    if((fp=fopen("news.txt", "r"))==NULL)
        error_handling("fopen() error");

    while(!feof(fp))
    {
        fgets(buf, BUF_SIZE, fp);
        sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr*)&broad_adr, sizeof(broad_adr));
        sleep(2);
    }
    close(send_sock);
    return 0;
}
void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}
//news_receiver_brd.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int recv_sock;
    struct sockaddr_in adr;
    int str_len;
    char buf[BUF_SIZE];
    if(argc!=2){
        printf("Usage : %s <PORT>\n", argv[0]);
        exit(1);
    }

    recv_sock=socket(PF_INET, SOCK_DGRAM, 0);
    memset(&adr, 0, sizeof(adr));
    adr.sin_family=AF_INET;
    adr.sin_addr.s_addr=htonl(INADDR_ANY);
    adr.sin_port=htons(atoi(argv[1]));

    if(bind(recv_sock, (struct sockaddr*)&adr, sizeof(adr))==-1)
        error_handling("bind() error");

    while(1)
    {
        str_len=recvfrom(recv_sock, buf, BUF_SIZE-1, 0, NULL, 0);
        if(str_len<0)
            break;
        buf[str_len]=0;
        fputs(buf, stdout);
    }
    close(recv_sock);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}