UDP点对点通讯、广播通讯、多播通讯

IP地址的简介:
IP地址是由32位二进制组成:网络号+主机号。同一个物理网络上的所有主机都使用同一个网络ID,网络上的一个主机(包括网络上工作站,服务器和路由器等)有一个主机ID与其对应。IP地址分为A、B、C、D、E5类。常用的是B和C两类。
一个A类IP地址是指, 在IP地址的四段号码中,第一段号码为网络号码,剩下的三段号码为本地计算机的号码。如果用二进制表示IP地址的话,A类IP地址就由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是“0”。
一个B类IP地址是指,在IP地址的四段号码中,前两段号码为网络号码。如果用二进制表示IP地址的话,B类IP地址就由2字节的网络地址和2字节主机地址组成,网络地址的最高位必须是“10”。
一个C类IP地址是指,在IP地址的四段号码中,前三段号码为网络号码,剩下的一段号码为本地计算机的号码。如果用二进制表示IP地址的话,C类IP地址就由3字节的网络地址和1字节主机地址组成,网络地址的最高位必须是“110”。
C类IP地址的子网掩码为255.255.255.0,每个网络支持的最大主机数为256-2=254台

D类IP地址:1110  +多播地址
    即多播地址范围: 224.0.0.1 - 239.255.255.255

全0或全1的网络号、主机号保留作特殊用途:

网络号
主机号
代表的意思
全0
全0
本网络上本主机
全0
host-id
本网络上主机号为host-id的主机
net-id
全1不分配给任何主机,表示某个网络的网络地址,代表的是一个网段
全1全1
表示本网络上所有的主机,是本地网络的广播地址
net-id
全1
对net-id网络上所有主机进行广播,是远程网络的广播地址

/**********************************************************************
* Compiler: GCC
* Last Update:  Sat 12 Nov 2011 11:52:25 PM CST
* File Name: myServerUDP1.c
* Description: UDP点对点通讯服务器端,默认端口地址为PORT
* Usage: ./mySErverUDP1  服务器所绑定的IP地址 端口号
************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#define MAXBUF 1024
#define PORT 60000
 
int main(int argc, char **argv)
{
    int myFd, clientFd;
    struct sockaddr_in myAddr, clientAddr;
    int addrlen = sizeof(clientAddr);
    char buf[MAXBUF];
    char addrTemp[INET_ADDRSTRLEN];

    bzero(&myAddr, sizeof(myAddr));
    bzero(&clientAddr, sizeof(clientAddr));
    //创建套接字,datagrams
    if((myFd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        printf("错误码:%d, %s\n", errno, strerror(errno));
        exit(errno);
    }
    myAddr.sin_family  = AF_INET;
    if (argc == 2) {  //IP地址
        int res;
        if ((res = inet_pton(AF_INET, argv[1], &myAddr.sin_addr)) == -1) {
            printf("错误码:%d, %s\n", errno, strerror(errno));
            exit(errno);
        } else if(res == 0) {
            printf("IP地址无效\n");
            return 0;
        } 
    } else {
        myAddr.sin_addr.s_addr = INADDR_ANY;
    }
    if (argc == 3) {
        myAddr.sin_port = htons(atoi(argv[2]));
    } else {
        myAddr.sin_port = htons(PORT);
    }

    if(bind(myFd, (struct sockaddr *)&myAddr, sizeof(myAddr)) != 0) {
        printf("错误码:%d, %s\n", errno, strerror(errno));
        exit(errno);
    }
    printf("服务器已启动,等待连接中.....\n");
    while (1) {
        bzero(buf, sizeof(buf));
        if (recvfrom(myFd, buf, sizeof(buf) - 1, 0, 
                    (struct sockaddr *)&clientAddr, &addrlen) == -1) {
            printf("错误码:%d, %s\n", errno, strerror(errno));
            exit(errno);
        }
        printf("收到来自%s的消息:%s\n", 
                inet_ntop(AF_INET, &clientAddr.sin_addr, addrTemp, sizeof(addrTemp)), buf);
    }

    return 0;
}


/**********************************************************************
* Compiler: GCC
* Last Update:  Sat 12 Nov 2011 11:39:22 PM CST
* File Name: myClientUDP1.c
* Description: UDP点对点或广播通讯客户端,端口号默认为PORT
* Usage: ./myClientUDP1  对方IP地址 端口号
************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#define MAXBUF 1024
#define PORT 60000
 
int main(int argc, char **argv)
{
    int myFd;
    struct sockaddr_in serverAddr;
    int addrlen = sizeof(serverAddr);
    char buf[MAXBUF];

    bzero(&serverAddr, sizeof(serverAddr));
    bzero(buf, sizeof(buf));
    //创建套接字,datagrams
    if((myFd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        printf("socket()错误码:%d, %s\n", errno, strerror(errno));
        exit(errno);
    }
    /* 设置通讯方式对广播,即本程序发送的一个消息,网络上所有主机均可以收到 */
    int yes = 1;
    if(setsockopt(myFd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes)) != 0) {
        printf("setsockopt()错误码:%d, %s\n", errno, strerror(errno));
        exit(errno);
    }

    //设置对方的IP地址和端口等属性;
    serverAddr.sin_family  = AF_INET;
    if (argc > 1) {  //IP地址
        int res;
        if ((res = inet_pton(AF_INET, argv[1], &serverAddr.sin_addr)) == -1) {
            printf("inet_pton()错误码:%d, %s\n", errno, strerror(errno));
            exit(errno);
        } else if(res == 0) {
            printf("IP地址无效\n");
            return 0;
        } 
    } else {
        printf("Usage: %s 对方IP地址\n", argv[0]);
        return 0;
    }
    if (argc == 3) {
        serverAddr.sin_port = htons(atoi(argv[2]));
    } else {
        serverAddr.sin_port = htons(PORT);
    }
    
    strcpy(buf, "Hello,I'm Client");
    if (sendto(myFd, buf, strlen(buf) + 1, 0, 
                (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1) {
        printf("sendto()错误码:%d, %s\n", errno, strerror(errno));
        exit(errno);
    }
    printf("成功给服务器发送消息:\"%s\"\n", buf);

    return 0;
}


Windows下的一个UDP服务器程序

#include <stdio.h>
#include <Winsock2.h>
#define LISTEN_PORT 8888
int main()
{
    SOCKET socket1;
    WSADATA wsaData;
    int iErrorCode;
    if (WSAStartup(MAKEWORD(2,1),&wsaData)) //调用Windows Sockets DLL
    {
        printf("Winsock无法初始化!\n");
        WSACleanup();
        return 1;
    }
    printf("服务器开始创建SOCKET。\n");
    struct sockaddr_in local;
    struct sockaddr_in from;
    int fromlen =sizeof(from);
    local.sin_family=AF_INET;
    local.sin_port=htons(LISTEN_PORT); ///监听端口
    local.sin_addr.s_addr=INADDR_ANY; ///本机
    socket1=socket(AF_INET,SOCK_DGRAM,0);
    bind(socket1,(struct sockaddr*)&local,sizeof(local));
    while (1)
    {
        char buffer[1024]="\0";
        printf("waiting for message from others-------------\n");
        if (recvfrom(socket1,buffer,sizeof(buffer),0,(struct sockaddr*)
                    &from,&fromlen)!=SOCKET_ERROR)
        {
            printf("Received datagram from %s--%s\n",inet_ntoa(from.sin_addr),buffer);
            给cilent发信息
            sendto(socket1,buffer,sizeof(buffer),0,(struct sockaddr*)&from,fromlen);

        }
        //Sleep(1000);
    }
    closesocket(socket1);

    return 0;
}

如果不设置通讯方式为广播方式的话,进行广播会出现

[test1@localhost 周立发]$ ./myClientUDP1 192.168.1.255
sendto()错误码:13, Permission denied

设置通讯方式为广播方式,进行测试,可以去下载一个  TCP&UDP_SocketTest调试工具v2.2,在windows下开启一个服务器,或者在主机windows下也写一个UDP服务器程序,要是通过无线上网的话,主机没法收到虚拟机的广播消息,不知道怎么回事,通过无线上网就行。
以下是测试过程,windows下
[test1@localhost 周立发]$ ./myClientUDP1 255.255.255.255
成功给服务器发送消息:"Hello,I'm Client"

[test1@localhost 周立发]$ ./myServerUDP1
服务器已启动,等待连接中.....
收到来自192.168.1.104的消息:Hello,I'm Client


主机Win7测试程序的运行结果:

服务器开始创建SOCKET。
aiting for message from others-------------
eceived datagram from 192.168.1.104--Hello,I'm Client

广播成功!

************************************************************************

     广播的缺点:不管主机是否有程序接收广播消息,广播消息一定会被网卡收到并提交给操作系统去处理,所以会造成网络上流量增大,对不接收广播消息的主机造成一定的负担。
     像广播一样,多播消息一样会被复制发到网络所有主机的网卡上,但只有宣布加入某个多播组的主机的网卡才会把数据提交给操作系统去处理。如果没有加入组,则网卡直接将数据丢弃。

************************************************************************

多播服务器端编程步骤:
1、调用socket()创建套接字
2、设置一个多播的IP地址和端口
3、向此多播地址发送数据

多播客户端编程步骤:
1、调用socket()创建套接字
2、步聚2可选,设置本上回环许可IP_MULTICAST_LOOP.默认该选项是激活的。
3、步聚3可选,设置IP_MULTICAST_TTL,每经过一个路由器,TTl的值自动减1。该值默认为1,防止多播报文往局域网外传播,可对其进行设置(0~255)
4、首先对下面这个结构体赋值
struct ip_mreq
{
        struct in_addr imr_multiaddr;   /* IP multicast address of group */
        struct in_addr imr_interface;   /* local IP address of interface */
};
然后调用setsockopt将本机加入多播组
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
            sizeof(struct ip_mreq))
5、调用bind()绑定本地套接字
6、recvfrom()
7、退出广播组setsockopt (socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq
)

示例程序:

/**********************************************************************
* Compiler: GCC
* Last Update:  Sat 12 Nov 2011 11:33:01 PM CST
* File Name: myServerMulticast2.c
* Description: 多播客户端
* Usage: myServerMulticast2 多播地址 [端口号(默认为PORT)]
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#define MAXBUF 1024
#define PORT 60000
 
int main(int argc, char **argv)
{
    int myFd;
    struct sockaddr_in mCastAddr;
    int addrlen = sizeof(mCastAddr);
    char buf[MAXBUF];

    bzero(&mCastAddr, sizeof(mCastAddr));
    bzero(buf, sizeof(buf));
    //创建套接字
    if((myFd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        printf("socket()错误码:%d, %s\n", errno, strerror(errno));
        exit(errno);
    }

    mCastAddr.sin_family  = AF_INET;
    if (argc > 1) {  //IP地址
        int res;
        if ((res = inet_pton(AF_INET, argv[1], &mCastAddr.sin_addr)) == -1) {
            printf("inet_pton()错误码:%d, %s\n", errno, strerror(errno));
            exit(errno);
        } else if(res == 0) {
            printf("IP地址无效\n");
            printf("请输入一个多播地址:224.0.0.1 ~ 239.255.255.255\n");
            return 0;
        } 
    } else {
        printf("Usage: %s 多播IP地址\n", argv[0]);
        return 0;
    }
    
    if (argc == 3) {
        mCastAddr.sin_port = htons(atoi(argv[2]));
    } else {
        mCastAddr.sin_port = htons(PORT);
    }
    
    //设置IP_MULTICAST_TTL 为1,即该多播最多只能经过一个路由器.TTL默认即为1
    //u_char ttl = 1;
    //if (setsockopt(myFd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) != 0) {
    //    printf("%s Line number:%d setsockopt()错误码:%d, %s\n", \
    //            __FILE__, __LINE__, errno, strerror(errno));
    //    exit(errno);
    //}

    while (1) {
        bzero(buf, sizeof(buf));
        printf("请输入要发送的消息:");
        if (fgets(buf, sizeof(buf), stdin) == NULL) {
            exit(0);
        }
        if (sendto(myFd, buf, strlen(buf) + 1, 0, 
                    (struct sockaddr *)&mCastAddr, sizeof(mCastAddr)) == -1) {
            printf("sendto()错误码:%d, %s\n", errno, strerror(errno));
            exit(errno);
        }
        printf("成功给对方发送消息:%s", buf);
    }

    return 0;
}

/**********************************************************************
* Compiler: GCC
* Last Update:  Sat 12 Nov 2011 11:17:13 PM CST
* File Name: myClientMulticast2.c
* Description: 
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#define _DEBUG
#define MAXBUF 1024
#define PORT 60000
 
int main(int argc, char **argv)
{
    int myFd, clientFd;
    struct sockaddr_in myAddr, clientAddr;
    struct ip_mreq mreq;
    int addrlen = sizeof(clientAddr);
    char buf[MAXBUF];
    char addrTemp[INET_ADDRSTRLEN];

    bzero(&myAddr, sizeof(myAddr));
    bzero(&clientAddr, sizeof(clientAddr));
    //创建套接字,datagrams
    if((myFd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        printf("%s Line number:%d socket()错误码:%d, %s\n", \
                __FILE__, __LINE__, errno, strerror(errno));
        exit(errno);
    }
    
    //设置本地地址端口信息
    myAddr.sin_family  = AF_INET;
    myAddr.sin_addr.s_addr = INADDR_ANY;
    if (argc == 3) {
        myAddr.sin_port = htons(atoi(argv[2]));
    } else {
        myAddr.sin_port = htons(PORT);
    }

    //绑定本地套接字
    if(bind(myFd, (struct sockaddr *)&myAddr, sizeof(myAddr)) != 0) {
        printf("%s Line number:%d bind()错误码:%d, %s\n", \
                __FILE__, __LINE__, errno, strerror(errno));
        exit(errno);
    }

    //设置IP_MULTICAST_LOOP 
    u_char loop = 1;
    if (setsockopt(myFd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) != 0) {
        printf("%s Line number:%d setsockopt()错误码:%d, %s\n", \
                __FILE__, __LINE__, errno, strerror(errno));
        exit(errno);
    }

    //设置IP_MULTICAST_TTL 为1,即该多播最多只能经过一个路由器
    u_char ttl = 1;
    if (setsockopt(myFd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) != 0) {
        printf("%s Line number:%d setsockopt()错误码:%d, %s\n", \
                __FILE__, __LINE__, errno, strerror(errno));
        exit(errno);
    }

    //对struct ip_mreq这个结构体赋值
    if (argc > 1) {  //设置IP地址
        int res;
        if ((res = inet_pton(AF_INET, argv[1], &mreq.imr_multiaddr)) == -1) {
            printf("%s Line number:%d setsockopt()错误码:%d, %s\n", \
                __FILE__, __LINE__, errno, strerror(errno));
            exit(errno);
        } else if(res == 0) {
            printf("IP地址无效\n");
            printf("请输入一个多播地址:224.0.0.1 ~ 239.255.255.255\n");
            return 0;
        } 
    } else {
        printf("Usage:%s 多播地址(224.0.0.1 ~ 239.255.255.255)\n", argv[0]);
        return(0);
    }
    mreq.imr_interface.s_addr =  INADDR_ANY;
    //将本机加入多播组中
    if (setsockopt (myFd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != 0) {
        printf("%s Line number:%d setsockopt()错误码:%d, %s\n", \
                __FILE__, __LINE__, errno, strerror(errno));
        exit(errno);
    }

    printf("客户端已启动,等待多播消息.....\n");
    while (1) {
        bzero(buf, sizeof(buf));
        if (recvfrom(myFd, buf, sizeof(buf) - 1, 0, 
                    (struct sockaddr *)&clientAddr, &addrlen) == -1) {
            printf("%s Line number:%d recvfrom()错误码:%d, %s\n", \
                    __FILE__, __LINE__, errno, strerror(errno));
            exit(errno);
        }
        printf("收到来自%s的消息:%s", 
                inet_ntop(AF_INET, &clientAddr.sin_addr, addrTemp, sizeof(addrTemp)), buf);
        if (strcmp("exit\n", buf) == 0) {
            break;
        }
    }
    //退出广播组
    if (setsockopt (myFd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) != 0) {
        printf("%s Line number:%d setsockopt()错误码:%d, %s\n", \
                __FILE__, __LINE__, errno, strerror(errno));
        exit(errno);
    }

    return 0;
}
同样,必须在主机有线上网的情况下主机才能收到虚拟机的多播消息,主机通过无线上网收不到!这个问题有待解决。

运行结果:

起始我输入一个非法的多播地址,结果出错了
[test1@localhost 周立发]$ ./myClientMulticast2  244.0.0.88
setsockopt()错误码:22, Invalid argument
//在试图加到一个多播组里出错了
setsockopt (myFd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)

#重新输入一个正确的多播地址,如224.1.1.1
[test1@localhost 周立发]$ ./myServerMulticast2 224.1.1.1
请输入要发送的消息:hello
成功给对方发送消息:hello
请输入要发送的消息:exit
成功给对方发送消息:exit

[test1@localhost 周立发]$ ./myClientMulticast2 224.1.1.1
客户端已启动,等待多播消息.....
收到来自192.168.1.104的消息:hello
收到来自192.168.1.104的消息:exit

主机windows下用TCP/UDP SOCKET调试工具V2.2 也成功收到多播数据。
23:45:05 收到数据:hello
23:45:08 收到数据:exit


@@@@@@

来源:http://book.51cto.com/art/200912/168573.htm

ESP32是一个基于Espressif Systems ESP8266 Wi-Fi SoC开发的开源硬件平台,它支持TCP/IP协议栈,包括UDP(用户数据报协议),可用于广播通信。UDP广播是一种单播到网络所有主机的技术,通过将数据包的目标IP地址设置为255.255.255.255,数据包会被发送到网络内的所有设备。 在ESP32进行UDP广播通信的基本步骤如下: 1. **初始化UDP模块**:首先需要配置ESP32的网络接口,启用UDP功能并设置正确的网络接口。 ```c WiFi.mode(WIFI_STA); // 设置工作模式为接入点 WiFi.begin("SSID", "password"); // 连接Wi-Fi UDPServer server(LOCAL_PORT); // 初始化本地端口服务器 ``` 2. **设置广播地址**:创建一个数据包,目标地址设为全网广播地址。 ```c struct sockaddr_in broadcast_addr; broadcast_addr.sin_family = AF_INET; // 使用IPv4 broadcast_addr.sin_port = htons(BROADCAST_PORT); // 广播端口 broadcast_addr.sin_addr.s_addr = INADDR_BROADCAST; // 全局广播地址 ``` 3. **发送广播消息**:创建一个UDP数据包,并将其发送到广播地址。 ```c char data[] = "Hello, this is a UDP broadcast message"; // 数据内容 server.sendTo(data, sizeof(data), &broadcast_addr, sizeof(broadcast_addr)); ``` 4. **接收响应**:如果其他设备对广播进行了回应,可以通过监听收到的数据包来进行处理。 ```c UdpPacket packet; while (server.receive(&packet)) { Serial.println(packet.remoteIP(), DEC); Serial.print("Message received: "); Serial.println(packet.data(), DEC); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值