C语言网络编程实现广播、组播及本地通信

本文详细介绍了C语言中如何实现网络通信的三种方式:广播、组播和本地通信。首先讲解了广播的概念、广播地址、流程及代码实现;接着讨论了组播的特点、地址、流程,并提供了代码示例。然后,阐述了本地通信的场景和优势,以及TCP和UDP本地通信的流程。最后,给出了一个UDP群聊聊天室的实践项目,作为巩固学习成果的练习。
摘要由CSDN通过智能技术生成

一、广播(broadcast)

1.概念

单播:前几篇博客介绍的数据包发送方式只有一个接受方,称为单播

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

组播:如果只发给局域网内的部分主机,称为组播

同一个sockfd 只能处理一种接收方式,如果即想发单播又想发广播,还想发组播,需要三个sockfd。只有用户数据报(使用UDP协议)套接字才能广播。

2.广播地址

以192.168.1.0 (255.255.255.0) 网段为例,最大的主机地址192.168.1.255代表该网段的广播地址(具体以ifconfig 命令查看到的 broadcast 后面的为准)发到该地址的数据包被所有的主机接收。

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

广播能发给局域网所有主机的原理

因为广播的数据包比较特殊,他的目的mac地址全是f(ff:ff:ff:ff:ff:ff) 这个数据包会发给交换机,交换机是工作在链路层的,交换机看到这样目的mac全是f的数据包,就会将该数据包发给局域网内的所有主机。到达主机后,进行拆包,看到目的mac是广播的mac,则允许通过。到达网络层一看IP地址是广播的IP地址,则可以通过。到达传输层,只要端口号匹配,则数据就能到达应用层。

广播的应用:ARP请求,通过ip地址获取对方的mac地址,使用的就是广播。

3.广播的流程

发送者:

        创建套接字 socket( )

        设置为允许发送广播权限 setsockopt( )

        填充广播信息结构体 sockaddr_in

        发送数据 sendto( )

接收者:

        创建套接字 socket( )

        填充广播信息结构体 sockaddr_in

        将套接字与广播信息结构体绑定 bind( )

        接收数据 recvfrom( )

getsockopt函数说明:

int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
功能:设置允许发送广播

#include <sys/types.h>
#include <sys/socket.h>
参数:
        @sockfd: socket函数返回的套接字
        @level:SOL_SOCKET 套接字级别
        @optname:SO_BROADCAST 允许发送广播
        @optval:int on = 1
        @optlen:sizeof(on)
返回值:成功返回0,失败返回-1,置位错误码

示例:
int on = 1;
if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on))){
        ERRLOG("setsockopt error");
}

4.代码实现

//接收端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#define N 128

#define ERRLOG(errmsg) do{\
                perror(errmsg);\
                printf("%s--%s(%d)\n", __FILE__, __func__, __LINE__);\
                exit(-1);\
            }while(0)

int main(int argc, const char *argv[]){
    if(3 != argc){
        printf("Usage : %s <ip> <port>\n", argv[0]);
        exit(-1);
    }
    //1.创建用户数据报式套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == sockfd){
        ERRLOG("socket error");
    }

    //创建服务器广播信息结构体
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    //端口号随便定  ip地址需要是  广播的ip地址
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[2]));
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t addrlen = sizeof(server_addr);

    //3.将套接字和广播信息结构体进行绑定
    if(-1 == bind(sockfd, (struct sockaddr *)&server_addr, addrlen)){
        ERRLOG("bind error");
    }


    //定义一个结构体,保存客户端的信息
    struct sockaddr_in client_addr;
    memset(&server_addr, 0, sizeof(client_addr));//清空
    socklen_t clientaddrlen = sizeof(client_addr);

    char buff[N] = {0};

    while(1){
        //接收数据,如果想要给对方回应,就必须保存对方的网络信息结构体
        //如果不回应,后两个参数写 NULL 也行
        if(-1 == recvfrom(sockfd, buff, N, 0, (struct sockaddr *)&client_addr, &clientaddrlen)){
            ERRLOG("recvfrom error");
        }
        printf("%s(%d):%s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buff);
        
        #if 0
        //组装应答信息
        strcat(buff, "--sever");
        if(-1 == sendto(sockfd, buff, N, 0, (struct sockaddr *)&client_addr, clientaddrlen)){
            ERRLOG("sendto error");
        }
        #endif
        memset(buff, 0, N);
    }
    
    //关闭套接字
    close(sockfd);

    return 0;
}
//发送端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#define N 128

#define ERRLOG(errmsg) do{\
                perror(errmsg);\
                printf("%s--%s(%d)\n", __FILE__, __func__, __LINE__);\
                exit(-1);\
            }while(0)

int main(int argc, const char *argv[]){
    if(3 != argc){
        printf("Usage : %s <ip> <port>\n", argv[0]);
        exit(-1);
    }
    //1.创建用户数据报套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == sockfd){
        ERRLOG("socket error");
    }

    //设置套接字允许发送广播  setsockopt
    int on = 1;
    if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on))){
        ERRLOG("setsockopt error");
    }

    //填充服务器广播信息结构体
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[2]));
    //广播的ip地址
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t addrlen = sizeof(server_addr);

    char buff[N] = {0};
    while(1){
        printf("input your msg:");
        fgets(buff, N, stdin);
        buff[strlen(buff)-1] = '\0';//清除 \n
        if(0 == strcmp(buff, "quit")){
            break;
        }
        if(-1 == sendto(sockfd, buff, N, 0, (struct sockaddr *)&server_addr, addrlen)){
            ERRLOG("sendto error");
        }
        #if 0
        //因为客户端已经知道服务器的网络信息了 所以后两个参数可以传NULL
        if(-1 == recvfrom(sockfd, buff, N, 0, NULL, NULL)){
            ERRLOG("recvfrom error");
        }
        printf("recv:[%s]\n", buff);
        #endif
        memset(buff, 0, N);
    }
    //关闭套接字
    close(sockfd);

    return 0;
  • 10
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值