网络编程 lesson7 广播组播和本地通信

目录

广播

什么是广播?(了解)

广播发送流程

广播接收流程

组播

什么是组播?

组播地址

组播结构体

组播发送流程

组播接收

本地套接字通信

什么是本地套接字通信?

特性

核心代码

程序实例

服务端

客户端


广播

什么是广播?(了解)

网络编程中的广播是一种通信方式,用于向网络中的多个主机发送消息或数据。它被广泛应用于局域网(LAN)或广域网(WAN)环境中,使得一台主机能够向整个网络中的其他主机发送消息,而不需要逐个发送给每个目标主机。

广播可以被看作是一种单向通信模式,其中发送方将消息广播到网络上的所有主机,而接收方则可以选择是否接收该消息。这使得广播非常适用于一些场景,如发送实时事件通知、传递系统状态更新或进行服务发现等。

在网络编程中,广播通常使用IP协议的特定地址来实现,例如IPv4中的广播地址(通常为255.255.255.255)或IPv6中的多播地址。发送方使用广播地址作为目标地址来发送消息,而接收方可以通过监听广播地址来接收消息。

广播通常使用UDP(用户数据报协议)来发送消息,因为UDP是一种无连接的协议,它不需要在发送前建立连接,这使得广播更加高效。然而,也可以使用TCP(传输控制协议)来进行广播,尽管这需要建立连接并可能带来一些额外的开销。

在实际的网络编程中,广播需要考虑一些安全性和网络规模的问题。由于广播消息可以发送给整个网络,可能存在滥用和安全风险。因此,在实现广播功能时需要进行适当的安全措施,例如对广播消息进行身份验证或加密,以确保只有授权的接收方可以接收和处理广播消息。

总结起来,网络编程中的广播是一种向网络中的多个主机发送消息或数据的通信方式。它利用特定的广播地址和UDP或TCP协议来实现,可以用于实时事件通知、系统状态更新和服务发现等场景。

广播发送流程

  1. 创建用户数据报套接字
  2. 缺省创建的套接字不允许广播数据包,使用setsockopt设置属性
  3. 接收方的地址指定为广播的地址
  4. 指定端口信息
  5. 发送数据包
int on =1;

setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST,&on,sizeof(on));
//设置属性代码

 完整代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char const *argv[])
{
    if(argc<2)
    {
        printf("please input addr and port\n");
        return -1;
    }
    //1.创建数据报套接子
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }

    //设置发送广播权限
    int op = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &op, sizeof(op));

    //填充结构体:广播ip和接受方端口
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(argv[2]));
    addr.sin_addr.s_addr = inet_addr(argv[1]);

    char buf[128];
    while (1)
    {
        fgets(buf, sizeof(buf), stdin);
        sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&addr, sizeof(addr));
    }
    close(sockfd);
    return 0;
}
 
  

广播接收流程

  1. 创建用户数据报套接字
  2. 绑定IP地址(广播IP或者0.0.0.0)和端口
  3. 绑定端口必须和发送方指定的端口相同
  4. 等待接收数据
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char const *argv[])
{
    if(argc<2)
    {
        printf("please input addr and port\n");
        return -1;
    }
    
    //1.创建数据报套接子
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }
    //填充结构体:广播ip和接受方端口
    struct sockaddr_in addr,caddr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(argv[2]));
    addr.sin_addr.s_addr = inet_addr(argv[1]);

    socklen_t len=sizeof(caddr);

    if(bind(sockfd,(struct sockaddr *)&addr,sizeof(addr))<0)
    {
        perror("bind err.");
        return -1;
    }

    char buf[128];
    while (1)
    {
        recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&caddr,&len);
        printf("ip=%s  port=%d :%s\n",inet_ntoa(caddr.sin_addr),\
        ntohs(caddr.sin_port),buf);
    }
    close(sockfd);
    return 0;
}

网络编程 lesson7 广播组播和本地通信_第1张图片

组播

什么是组播?

组播(Multicast)是一种网络通信方式,用于将数据从一个发送者传输给一组特定的接收者。在组播通信中,发送者只需将数据发送到一个特定的组播组地址,而不需要知道接收者的具体地址。这样,所有加入该组播组的接收者都可以接收到相同的数据。

组播通信适用于需要同时将相同数据发送给多个接收者的场景,例如实时视频流、音频流、多媒体广播、在线会议等。相比单播(Unicast)一对一通信和广播(Broadcast)一对所有通信,组播可以减少网络带宽的使用,因为数据只需发送一次,而不是复制多份发送。

在组播通信中,路由器在网络中负责处理组播数据的传递。它们使用一种称为组播路由协议(Multicast Routing Protocol)的协议来确定数据应传递到哪些接收者。常见的组播路由协议包括多协议标签交换(Multiprotocol Label Switching, MPLS)和协议无关组播(Protocol Independent Multicast, PIM)。

组播地址

不分网络地址和主机地址,第1字节的前4位固定为1110 。是D类IP

224.0.0.1-239.255.255.255

网络编程 lesson7 广播组播和本地通信_第2张图片

组播结构体

#include 
#include 
#include 

struct ip_mreq
{
    struct in_addr imr_multiaddr;   /* 指定多播组IP */
    struct in_addr imr_interface;   /* 本地网卡地址,通常指定为 INADDR_ANY--0.0.0.0 */
};

struct ip_mreq mreq;
bzero(&mreq, sizeof(mreq));  // 初始化 mreq 结构体为 0

mreq.imr_multiaddr.s_addr = inet_addr("224.10.10.1");  // 设置多播组 IP 地址
mreq.imr_interface.s_addr = INADDR_ANY;  // 设置本地网卡地址为任意地址

setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
// 设置套接字选项,加入指定的多播组
// 参数说明:
// - sockfd: 套接字描述符
// - IPPROTO_IP: 指定 IP 协议
// - IP_ADD_MEMBERSHIP: 加入多播组的选项
// - &mreq: 指向 ip_mreq 结构体的指针,其中包含要加入的多播组 IP 和本地网卡地址
// - sizeof(mreq): ip_mreq 结构体的大小

组播发送流程

  1. 创建用户数据报套接字
  2. 接收方地址指定为组播地址
  3. 指定端口信息
  4. 发送数据包
//发送端
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char const *argv[])
{
    if(argc<2)
    {
        printf("please input addr and port\n");
        return -1;
    }
    int socked=socket(AF_INET,SOCK_DGRAM,0);
    if(socked<0)
    {
        perror("socket err.");
        exit(-1);
    }
    //绑定结构体
    struct sockaddr_in saddr;
    saddr.sin_family=AF_INET;
    saddr.sin_port=htons(atoi(argv[2]));//输入端口是字符串,这里得转换一下
    saddr.sin_addr.s_addr=inet_addr(argv[1]);//组播ip
     char buf[128];
     while (1)
     {
         fgets(buf,sizeof(buf),stdin);
         sendto(socked,buf,sizeof(buf),0,(struct sockaddr*)&saddr,sizeof(saddr));
     }
     close(socked);
    return 0;
}

组播接收

  1. 创建用户数据报套接字
  2. 加入多播组
  3. 绑定IP地址(加入组的组IP或0.0.0.0)和端口
  4. 等待接收数据

加入多播组的核心代码

//接收端
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


int main(int argc, char const *argv[])
{
    if(argc<2)
    {
        printf("please input addr and port\n");
        return -1;
    }
    //创建套接字
    int socked=socket(AF_INET,SOCK_DGRAM,0);
    if(socked<0)
    {
        perror("socket err.");
        exit(-1);
    }
    //新增操作,将主机IP放到多播组
    struct ip_mreq mreq;//多播结构体
    mreq.imr_multiaddr.s_addr=inet_addr(argv[1]);//地址
    mreq.imr_interface.s_addr=inet_addr("0.0.0.0");
    //添加多播组权限
    setsockopt(socked,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
    
    //绑定结构体
    struct sockaddr_in saddr,caddr;
    saddr.sin_family=AF_INET;
    saddr.sin_port=htons(atoi(argv[2]));//端口
    saddr.sin_addr.s_addr=inet_addr(argv[1]);//地址

    socklen_t len=sizeof(caddr);//提前计算出结构体得长度
    //绑定结构体
    if(bind(socked,(struct sockaddr*)&saddr,sizeof(saddr))<0)//出现问题
    {
        perror("bind err.");
        return -1;
    }
    char buf[128];
    while (1)
    {
        //接收客户端发来代码
        recvfrom(socked,buf,sizeof(buf),0,(struct sockaddr*)&caddr,&len);
        //inet_ntoa从机器到人 ntohs网络字节序到主机
        printf("ip =%s port=%d:%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),buf);    
    }
    close(socked);
    return 0;
}

代码结果

本地套接字通信

什么是本地套接字通信?

本地通信套接字(Local Communication Socket)是一种用于在同一台计算机上进行进程间通信的机制。它允许不同的进程通过在内核中创建的特殊文件描述符来进行相互通信。

本地通信套接字在操作系统中以文件的形式存在,通常称为"Unix域套接字"或"本地域套接字"。与网络套接字不同,本地通信套接字不涉及网络协议栈,而是直接通过内核中的特殊通信机制进行数据传输。

特性

  1. socket同样可以用于本地通信
  2. 创建套接字时使用本地协议AF_UNIX(或AF_LOCAL)
  3. 分为流式套接字和用户数据报套接字
  4. 和其他进程间通信方式相比使用方便、效率更高
  5. 常用于前后台进程通信

核心代码

#include 
#include 

unix_socket = socket(AF_UNIX, type, 0);
// 创建一个 Unix 域套接字
// 参数说明:
// - AF_UNIX: 指定使用 Unix 域协议
// - type: 指定套接字的类型,如 SOCK_STREAM、SOCK_DGRAM 等
// - 0: 在创建套接字时不指定额外选项
struct sockaddr_un {
   sa_family_t sun_family;               /* AF_UNIX */
   char        sun_path[UNIX_PATH_MAX];  /* 本地路径 */
};

struct sockaddr_un myaddr;
bzero(&myaddr, sizeof(myaddr));
myaddr.sun_family = AF_UNIX; 
strcpy(myaddr.sun_path, "mysocket");  // 可以指定路径

程序实例

注意:客户端和服务器是相互依赖的,需要先运行服务器程序,然后再运行客户端程序以建立连接并进行通信。

服务端

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc, char const *argv[])
{
    //1.创建本读通信套接字
    int sockfd=socket(AF_UNIX,SOCK_STREAM,0);//这里选用了流式套接字
    if (sockfd<0)
    {
        perror("socker err.");
        return -1;
    }
    //2.填充本地通信套接字
    struct sockaddr_un saddr;
    saddr.sun_family=AF_UNIX;
    strcpy(saddr.sun_path,"./test");
    unlink("./test");//删除本地套接字
    //3.绑定本地套接字
    if(bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr))<0)
    {
        perror("bind err");
        return -1;
    }
    //4.开启监听
    if(listen(sockfd,5)<0)
    {
        perror("listen err.");
        return -1;
    }
    //5.接收连接
    int acceptfd=accept(sockfd,NULL,NULL);//不关心发送者是谁,这里填NULL
    if(accept<0)
    {
        perror("accept err.");
        return -1;
    }
    //6.接收
    char buf[128];
    while (1)
    {
        int ret=recv(acceptfd,buf,sizeof(buf),0);
        if(ret<0)
        {
            perror("recv err.");
        }else if (ret == 0)//在三次握手时出现了
        {
            printf("client exit.\n");
            break;
        }else
        {
            printf("buf:%s\n",buf);
        }
    }
    close(sockfd);
    close(acceptfd);
    return 0;
}

客户端

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc, char const *argv[])
{
    //1.创建本读通信套接字
    int sockfd=socket(AF_UNIX,SOCK_STREAM,0);//这里选用了流式套接字
    if (sockfd<0)
    {
        perror("socker err.");
        return -1;
    }
    //2.填充本地通信套接字
    struct sockaddr_un saddr;
    saddr.sun_family=AF_UNIX;
    strcpy(saddr.sun_path,"./test");
    //3.建立链接
    if(connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr))<0)
    {
        perror("connect err.");
        return -1;
    }
    char buf[128];
    while (1)
    {
        fgets(buf,sizeof(buf),stdin);
        send(sockfd,buf,sizeof(buf),0);//发送信息
    }
    close(sockfd);//关闭套接字
    return 0;
}

结果

网络编程 lesson7 广播组播和本地通信_第3张图片

你可能感兴趣的:(网络编程,网络,tcp/ip)