UDP 广播编程研究一

UDP 广播编程研究一
(一)介绍
广播是一个主机向一个网络上所有主机发送的操作方式,一对多的,同一个子网内的所有主机都可以收到此广播发送的数据。可见TCP是不支持广播的。
广播IP地址:IP地址一般可分为两部分,右部分是主机ID,左部分是网络ID,广播地址要求主机ID部分为全1。255.255.255.255是一个特殊广播地址,而我的机子:
这里写图片描述
可以看到广播地址:192.168.1.255,可以认为前三个字节192.168.1是网络ID部分。
(二)举例
服务器地址发现,假设服务器A,客户端B,客户端在某个局域网启动的时候,不知道本局域网是否有合适的服务器存在,它会使用广播在本局域网发送一个特定请求,如果有服务器相应了这个请求,则使用相应请求的IP地址进行链接,这也是一种服务器/客户端自动发现的一种常用办法。
代码如下:
广播客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> /*htonl htons ntohs */
#include <sys/ioctl.h>
#include <net/if.h>

#define IP_FOUND "IP_FOUND"
#define IP_FOUND_ACK "IP_FOUND_ACK"
#define  IFNAME "eth0"
#define PORT 8554

int main(int argc, char*argv[])
{
    int ret = -1;
    int sockfd = -1;
    int j = -1;

    /* 保存eth0接口信息 */
    struct ifreq *ifr;
    /* 保存所有接口信息 */
    struct ifconf ifc;

    /* 广播地址 */
    struct sockaddr_in broadcast_addr;
    /* 服务端地址 */
    struct sockaddr_in from_addr; 
    int from_len = sizeof(from_addr);   
    int count = -1;
    /* 读文件描述符集合 */
    fd_set readfd; 
    char buffer[1024];
    struct timeval timeout;
    timeout.tv_sec = 2; //超时时间为2秒
    timeout.tv_usec = 0;

    /* 建立数据报套接字 */
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0))<0)
    {
        perror("Socket error!\n");
        return -1;
    }
    printf("socket ok!\n");

    /* 获得所有网络接口列表(struct ifconf) */
    ifc.ifc_len = sizeof(buffer);
    ifc.ifc_buf = buffer;
    if (ioctl(sockfd, SIOCGIFCONF, (char *) &ifc) < 0)
    {
        perror("ioctl struct ifconf error!\n");
        return -1;
    }
    ifr = ifc.ifc_req;
    for (j = ifc.ifc_len / sizeof(struct ifreq); --j >= 0; ifr++)
    {
        if (!strcmp(ifr->ifr_name, IFNAME))
        {
            if (ioctl(sockfd, SIOCGIFFLAGS, (char *)ifr) < 0)
            {
                perror("ioctl-get flag error!");
            }
            break;
        }
    }

    /* 发送命令,获得eth0网络接口的广播地址 */
    if (ioctl(sockfd, SIOCGIFBRDADDR, ifr) == -1)
    {
        perror("ioctl-get broadcast_addr error\n");
        return -1;
    }

    /* 将获得的广播地址复制到broadcast_addr */
    memcpy(&broadcast_addr, (char *)&ifr->ifr_broadaddr, sizeof(struct sockaddr_in));
    printf("\nBroadcast-IP: %s\n", inet_ntoa(broadcast_addr.sin_addr));
    broadcast_addr.sin_family = AF_INET;
    broadcast_addr.sin_port = htons(PORT); /* 设置广播端口号 */

    /* 默认的套接字描述符sock是不支持广播,必须设置套接字描述符以支持广播 */
    int so_broadcast = 1;
    ret = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &so_broadcast,sizeof(so_broadcast));

    /* 发送多次广播,看网络上是否有服务器存在 */
    int times = 10;
    int i = 0;
    for (i = 0; i < times; i++)
    {
        /* 一共发送10次广播,每次等待2秒是否有回应 */
        timeout.tv_sec = 2;  
        timeout.tv_usec = 0;

        /* 广播发送服务器地址请求 */
        ret = sendto(sockfd, IP_FOUND, strlen(IP_FOUND), 0,(struct sockaddr*) &broadcast_addr, sizeof(broadcast_addr));
        if (ret < 0)
        {
            continue;
        }

        /* 文件描述符清0 */
        FD_ZERO(&readfd);
        /* 将套接字文件描述符加入到文件描述符集合中 */
        FD_SET(sockfd, &readfd);
        /* select侦听是否有数据到来 */
        ret = select(sockfd + 1, &readfd, NULL, NULL, &timeout);
        switch (ret)
        {
            case -1:    break;
            case 0:
                perror("select timeout!\n");
                break;
            default:
                /* 有数据到来 */
                if (FD_ISSET(sockfd,&readfd))
                {
                    // 从服务器端地址接收数据 */
                    count = recvfrom(sockfd, buffer, 1024, 0,(struct sockaddr*) &from_addr, &from_len); 
                    printf("\trecvmsg is %s\n", buffer);

                    if (strstr(buffer, IP_FOUND_ACK))
                    {
                        printf("\tfound server IP is %s, Port is %d\n",inet_ntoa(from_addr.sin_addr),htons(from_addr.sin_port));
                    }
                    return -1;
                }
                break;
            }
    }
    return 0;
}

广播服务器

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> /*htonl htons ntohs */
#include <stdlib.h>

#define IP_FOUND "IP_FOUND"
#define IP_FOUND_ACK "IP_FOUND_ACK"
#define PORT 8554

int main(int argc, char *argv[])
{
    int ret = -1;
    int sockfd = -1;
    /* 服务器端地址 */
    struct sockaddr_in server_addr;
    /* 客户端地址 */
    struct sockaddr_in from_addr;     
    int from_len = sizeof(struct sockaddr_in);
    int count = -1;
    /* 读文件描述符集合 */
    fd_set readfd; 
    char buffer[1024];
    struct timeval timeout;
    timeout.tv_sec = 2;
    timeout.tv_usec = 0;

    /* 建立数据报套接字 */
    sockfd = socket(AF_INET, SOCK_DGRAM, 0); //建立数据报套接字
    if (sockfd < 0)
    {
       perror("Socket error!\n");
        return -1;
    }
    printf("socket ok!\n");

    memset((void*) &server_addr, 0, sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htons(INADDR_ANY );
    server_addr.sin_port = htons(PORT);

    /* 将地址结构绑定到套接字上 */
    if ((ret = bind(sockfd, (struct sockaddr*) &server_addr, sizeof(server_addr)))<0)
    {
        perror("bind error!\n");
        return -1;
    }
    printf("bind ok!\n");

     /* 循环等待客户端 */
    while (1)
    {
        timeout.tv_sec = 100;
        timeout.tv_usec = 0;
        /* 文件描述符清0 */
        FD_ZERO(&readfd);
        /* 将套接字文件描述符加入到文件描述符集合中 */
        FD_SET(sockfd, &readfd);
        /* select侦听是否有数据到来 */
        ret = select(sockfd + 1, &readfd, NULL, NULL, &timeout); 
        switch (ret)
        {
            case -1: 
                perror("select error!\n");
                break;
            case 0:
                printf("select timeout!\n");
                break;
            default:
                /* 有数据到来 */
                if (FD_ISSET(sockfd,&readfd))
                {
                     /* 接收客户端发送的数据 */
                    count = recvfrom(sockfd, buffer, 1024, 0,(struct sockaddr*)&from_addr, (socklen_t *)&from_len);
                    printf("recvfrom %s!\n",buffer);
                    /* from_addr保存客户端的地址结构 */
                    if (strstr(buffer, IP_FOUND))
                    {
                        /* 响应客户端请求,打印客户端的IP地址和端口号 */
                        printf("\nClient connection information:\n\t IP: %s, Port: %d\n",(char *)inet_ntoa(from_addr.sin_addr),ntohs(from_addr.sin_port));

                        /* 将数据发送给客户端 */
                        memcpy(buffer, IP_FOUND_ACK, strlen(IP_FOUND_ACK) + 1);
                        count = sendto(sockfd, buffer, strlen(buffer), 0,   (struct sockaddr*) &from_addr, from_len);
                    }
                }
                break;
        }
    }
    return 0;
}

代码测试:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值