linux網絡編程之廣播


Linux網絡編程之廣播

作者:Eric(wongpz@foxmail.com)
時間:2012-09-14


常見的TCP和UDP通信大都使用的是點對點的單播方式,這種方式可以很方便進行交互處理,在多個交互同時進行時,網絡帶寬占的比較多。廣播是由一個主機向一個網絡上所有主機發送消息的方式,需要的網絡帶寬相對單播來說,降低很多。
廣播的地址是將IP地址中主機部分全部置為1,即xxx.xxx.xxx.255。255.255.255.255這一特殊的廣播地址可以向全世界進行廣播,但一般路由器都會屏蔽。
廣播發送的目的MAC地址為FF:FF:FF:FF:FF:FF。
一般發送廣播步驟如下:
(1)確定廣播接口名字,如eth0;
(2)確定廣播地址,通過ioctl函數,請求碼為SIOCGIFBRDADDR,從而得到廣播地址;
(3)使用廣播地址進行廣播;

由於TCP協議是端到端的協議,通信之前必須建立三次握手才能發送數據,而廣播是一種一對多的通信方式,所以TCP不支持廣播。一般在局域網中,廣播用來探測服務器地址或客戶端的發現。

編程實現:
服務器端:

/*********************************************************************
 * Filename: bcast_server.c
 * Description: 廣播服務器端代碼
 * Author: Eric(wongpz@foxmail.com)
 * Date: 2012-9-14
 ********************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <linux/in.h>
#include <stdlib.h>

#define IP_FOUND "IP_FOUND"
#define IP_FOUND_ACK "IP_FOUND_ACK"
#define PORT 9999

int main(int argc, char*argv[])
{
    int ret = -1;
    int sock;
    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;

    sock = socket(AF_INET, SOCK_DGRAM, 0); //建立數據報套接字
    if (sock < 0)
    {
        perror("sock error");
        return -1;
    }

    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);

    //將地址結構綁定到套接字上
    ret = bind(sock, (struct sockaddr*) &server_addr, sizeof(server_addr));
    if (ret < 0)
    {
        perror("bind error");
        return -1;
    }

    /**
     * 循環等待客戶端
     */
    while (1)
    {
        timeout.tv_sec = 100;
        timeout.tv_usec = 0;

        //文件描述符集合清0
        FD_ZERO(&readfd);

        //將套接字描述符加入到文件描述符集合
        FD_SET(sock, &readfd);

        //select偵聽是否有數據到來
        ret = select(sock + 1, &readfd, NULL, NULL, &timeout); //偵聽是否可讀
        switch (ret)
        {
        case -1: //發生錯誤
            perror("select error:");
            break;
        case 0: //超時
            printf("select timeout\n");
            break;
        default:
            if (FD_ISSET(sock,&readfd))
            {
                count = recvfrom(sock, buffer, 1024, 0,
                        (struct sockaddr*)&from_addr, &from_len); //接收客戶端發送的數據

                //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(sock, buffer, strlen(buffer), 0,
                            (struct sockaddr*) &from_addr, from_len);
                }
            }
            break;
        }
    }
    return 0;
}


客戶端:

/*********************************************************************
 * Filename: bcast_client.c
 * Description:廣播客戶端代碼
 * Author: Eric(wongpz@foxmail.com)
 * Date: 2012-9-14
 ********************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<netdb.h>
#include <sys/ioctl.h>
#include <net/if.h>

#define IP_FOUND "IP_FOUND"
#define IP_FOUND_ACK "IP_FOUND_ACK"
#define IFNAME "eth0"
#define MCAST_PORT 9999

int main(int argc, char*argv[])
{
    int ret = -1;
    int sock = -1;
    int j = -1;
    int so_broadcast = 1;
    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;

    //建立數據報套接字
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        perror("create socket failed:");
        return -1;
    }

    // 獲取所有套接字接口
    ifc.ifc_len = sizeof(buffer);
    ifc.ifc_buf = buffer;
    if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0)
    {
        perror("ioctl-conf:");
        return -1;
    }
    ifr = ifc.ifc_req;
    for (j = ifc.ifc_len / sizeof(struct ifreq); --j >= 0; ifr++)
    {
        if (!strcmp(ifr->ifr_name, "eth0"))
        {
            if (ioctl(sock, SIOCGIFFLAGS, (char *) ifr) < 0)
            {
                perror("ioctl-get flag failed:");
            }
            break;
        }
    }

    //將使用的網絡接口名字復制到ifr.ifr_name中,由於不同的網卡接口的廣播地址是不一樣的,因此指定網卡接口
    //strncpy(ifr.ifr_name, IFNAME, strlen(IFNAME));
    //發送命令,獲得網絡接口的廣播地址
    if (ioctl(sock, SIOCGIFBRDADDR, ifr) == -1)
    {
        perror("ioctl error");
        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(MCAST_PORT);

    //默認的套接字描述符sock是不支持廣播,必須設置套接字描述符以支持廣播
    ret = setsockopt(sock, 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;  //超時時間為2秒
        timeout.tv_usec = 0;
        ret = sendto(sock, IP_FOUND, strlen(IP_FOUND), 0,
                (struct sockaddr*) &broadcast_addr, sizeof(broadcast_addr));
        if (ret < 0)
        {
            continue;
        }

        //文件描述符清0
        FD_ZERO(&readfd);
        //將套接字文件描述符加入到文件描述符集合中
        FD_SET(sock, &readfd);
        //select偵聽是否有數據到來
        ret = select(sock + 1, &readfd, NULL, NULL, &timeout);
        switch (ret)
        {
        case -1:
            break;
        case 0:
            perror("select timeout\n");
            break;
        default:
            //接收到數據
            if (FD_ISSET(sock,&readfd))
            {
                count = recvfrom(sock, buffer, 1024, 0,
                        (struct sockaddr*) &from_addr, &from_len); //from_addr為服務器端地址
                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;
}


Makefile文件:

OBJS_SERVER = bcast_server.o
OBJS_CLIENT = bcast_client.o
LIBS_SERVER =
LIBS_CLIENT =

CFLAGS = -c
CC = gcc

PROS = bcast_client bcast_server

all: $(PROS)

.c.o:
    $(CC) $(CFLAGS) $<

bcast_client: $(OBJS_CLIENT)
    $(CC) -o $@ $^ $(LIBS_SERVER)

bcast_server: $(OBJS_SERVER)
    $(CC) -o $@ $^ $(LIBS_CLIENT)

clean:
    rm -rf $(PROS) $(OBJS_CLIENT) $(OBJS_SERVER)


運行截圖:



備注:
以上代碼參考於http://blog.csdn.net/chenjin_zhong/article/details/7270213,獲取網絡接口部分有改動。

具體代碼及文檔可以在如下地址下載:

http://download.csdn.net/detail/ericdev/4570901


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM