11.2.3 广播的示例

来源:互联网 发布:dnf韩服改版技能数据 编辑:程序博客网 时间:2024/05/01 17:50

11.2.3  广播的示例

本节中是一个服务器地址发现的代码,假设服务器为A,客户端为B。客户端在某个局域网启动的时候,不知道本局域网内是否有适合的服务器存在,它会使用广播在本局域网内发送特定协议的请求,如果有服务器响应了这种请求,则使用响应请求的IP地址进行连接,这是一种服务器/客户端自动发现的常用方法。

1.广播例子简介

如图11.5所示为使用广播的方法发现局域网上服务器的IP地址。服务器在局域网上侦听,当有数据到来的时候,判断数据是否有关键字IP_FOUND,当存在此关键字的时候,发送IP_FOUND_ACK到客户端。客户端判断是否有服务器的响应IP_FOUND请求,并判断响应字符串是否包含IP_FOUND_ACK来确定局域网上是否存在服务器,如果有服务器的响应,则根据recvfrom()函数的from变量可以获得服务器的IP地址。

 

11.5  利用广播进行服务器IP地址的发现

2.广播的服务器端代码

服务器的代码如下,服务器等待客户端向某个端口发送数据,如果数据的格式正确,则服务器会向客户端发送响应数据。

 

01 

02      #define IP_FOUND "IP_FOUND"                  /*IP发现命令*/

03      #define IP_FOUND_ACK "IP_FOUND_ACK"     /*IP发现应答命令*/

04      void    HandleIPFound(void*arg)

05      {

06      #define BUFFER_LEN 32

07          int ret = -1;

08          SOCKET sock = -1;

09          struct sockaddr_in local_addr;          /*本地地址*/

10          struct sockaddr_in from_addr;           /*客户端地址*/

11      int from_len;

12          int count = -1;

13          fd_set readfd;

14          char buff[BUFFER_LEN];

15          struct timeval timeout;

16          timeout.tv_sec = 2;                     /*超时时间2s*/

17          timeout.tv_usec = 0;

18     

19          DBGPRINT("==>HandleIPFound/n");

20         

21           sock = socket(AF_INET, SOCK_DGRAM, 0);  /*建立数据报套接字*/

22          if( sock < 0 )

23          {

24              DBGPRINT("HandleIPFound: socket init error/n");

25              return;

26          }

27         

28          /*数据清零*/

29          memset((void*)&local_addr, 0, sizeof(struct sockaddr_in));
                                                        /
*清空内存内容*/

30          local_addr.sin_family = AF_INET;            /*协议族*/

31           local_addr.sin_addr.s_addr = htonl(INADDR_ANY);/*本地地址*/

32          local_addr.sin_port = htons(MCAST_PORT);        /*侦听端口*/

33          /*绑定*/

34          ret = bind(sock, (struct sockaddr*)&local_addr, sizeof(local_
            addr));

35          if(ret != 0)

36          {

37              DBGPRINT("HandleIPFound:bind error/n");

38              return;

39          }

40     

41           /*主处理过程*/

42          while(1)

43          {

44              /*文件描述符集合清零*/

45              FD_ZERO(&readfd);

46              /*将套接字文件描述符加入读集合*/

47              FD_SET(sock, &readfd);

48              /*select侦听是否有数据到来*/

49              ret = selectsocket(sock+1, &readfd, NULL, NULL, &timeout);

50              switch(ret)

51               {

52                  case -1:

53                  /*发生错误*/

54                      break;

55                  case 0:

56                      /*超时*/

57                      //超时所要执行的代码

58                     

59                      break;

60                  default:

61                   /*有数据到来*/

62                      if( FD_ISSET( sock, &readfd ) )

63                      {

64                              /*接收数据*/

65                          count = recvfrom( sock, buff, BUFFER_LEN, 0,
                            ( struct sockaddr
*) &from_addr, &from_len );

66                          DBGPRINT( "Recv msg is %s/n", buff );

67                          if( strstr( buff, IP_FOUND ) )
                                /
*判断是否吻合*/

68                          {

69                              /*将应答数据复制进去*/

70                              memcpy(buff, IP_FOUND_ACK,strlen(IP_
                                FOUND_ACK)+1);

71                               /*发送给客户端*/

72                              count = sendto( sock, buff, strlen( buff ),
                                0, ( struct sockaddr
*
) &from_addr, from_
                                len );

73                          }

74                      }

75              }

76          }

77          PRINT("<==HandleIPFound/n");

78

79          return;

80      }

 

服务器端分为如下步骤:

q      16行和第17行定义了服务器等待的超时时间,为2s

q      29行将地址结构清零。

q      30行定义地址协议族为AF_INET

q      31行设置IP地址为任意本地地址。

q      32行设置侦听的端口。

q      34行将本地的地址绑定到一个套接字文件描述符上。

q      42行开始为主处理过程,使用select函数,按照2s的超时时间侦听是否有数据到来。

q      45行文件描述符集合清零。

q      47行将套接字文件描述符加入读集合。

q      49select侦听是否有数据到来。

q      50行查看select的返回值。

q      52select发生错误。

q      55select超时。

q      60行有可读的数据到来。

q      65行接收数据。

q      67行查看接收到的数据是否匹配。

q      70行复制响应数据。

q      72行发送响应数据到客户端。

3.广播的客户端代码

广播的客户端函数代码如下,客户端向服务器端发送命令IP_FOUND,并等待服务器端的回复,如果有服务器回复,则向服务器发送IP_FOUND_ACK,否则发送10遍后退出。

 

01      #define IP_FOUND "IP_FOUND"                  /*IP发现命令*/

02      #define IP_FOUND_ACK "IP_FOUND_ACK"     /*IP发现应答命令*/

03      #define IFNAME "eth0"

04      void    IPFound(void*arg)

05      {

06      #define BUFFER_LEN 32

07          int ret = -1;

08          SOCKET sock = -1;

09          int so_broadcast = 1;

10          struct ifreq ifr;          

11          struct sockaddr_in broadcast_addr;      /*本地地址*/

12          struct sockaddr_in from_addr;           /*服务器端地址*/

13          int from_len;

14          int count = -1;

15          fd_set readfd;

16          char buff[BUFFER_LEN];

17          struct timeval timeout;

18          timeout.tv_sec = 2;                 /*超时时间2s*/

19          timeout.tv_usec = 0;

20     

21         

22           sock = socket(AF_INET, SOCK_DGRAM, 0);/*建立数据报套接字*/

23          if( sock < 0 )

24          {

25              DBGPRINT("HandleIPFound: socket init error/n");

26              return;

27          }

28          /*将需要使用的网络接口字符串名字复制到结构中*/

29          strcpy(ifr.ifr_name,IFNAME,strlen(IFNAME));

30          /*发送命令,获取网络接口的广播地址*/

31          if(ioctl(sock,SIOCGIFBRDADDR,&ifr) == -1)

32              perror("ioctl error"),exit(1);

33          /*将获得的广播地址复制给变量broadcast_addr*/

34          memcpy(&broadcast_addr, &ifr.ifr_broadaddr, sizeof(struct
            sockaddr_in ));

35          broadcast_addr.sin_port = htons(MCAST_PORT);/*设置广播端口*/

36         

37          /*设置套接字文件描述符sock为可以进行广播操作*/

38          ret = setsockopt(sock,

39                  SOL_SOCKET,

40                  SO_BROADCAST,

41              &so_broadcast,

42              sizeof so_broadcast);

43             

44           /*主处理过程*/

45          int times = 10;

46          int i = 0;

47          for(i=0;i<times;i++)

48          {

49              /*广播发送服务器地址请求*/

50              ret = sendto(sock,

51                          IP_FOUND,

52                          strlen(IP_FOUND),

53                          0,

54                          (struct sockaddr*)&broadcast_addr,

55                          sizeof(broadcast_addr));

56              if(ret == -1){

57                  continue;  

58              }

59              /*文件描述符集合清零*/

60              FD_ZERO(&readfd);

61              /*将套接字文件描述符加入读集合*/

62              FD_SET(sock, &readfd);

63              /*select侦听是否有数据到来*/

64              ret = selectsocket(sock+1, &readfd, NULL, NULL, &timeout);

65              switch(ret)

66               {

67                  case -1:

68                      /*发生错误*/

69                      break;

70                  case 0:

71                      /*超时*/

72                      //超时所要执行的代码

73                     

74                      break;

75                  default:

76                   /*有数据到来*/

77                      if( FD_ISSET( sock, &readfd ) )

78                      {

79                          /*接收数据*/

80                          count = recvfrom( sock, buff, BUFFER_LEN, 0,
                            ( struct sockaddr
*) &from_addr, &from_len );

81                          DBGPRINT( "Recv msg is %s/n", buff );

82                          if(strstr(buff, IP_FOUND_ACK))/*判断是否吻合*/

83                          {

84                              printf("found server, IP is %s/n",inet_ntoa
                                (from_addr.sin_addr));

85                          }

86                      break;/*成功获得服务器地址,退出*/

07                      }

08              }

09          }  

90          return;

91      }

 

客户端分为如下步骤:

q      18行和第19行定义了服务器等待的超时时间,为2s

q      22行建立数据报套接字。

q      29行复制网络接口名称。

q      31行获得与网络接口名称对应的广播地址。

q      34行和第35行设置广播的地址和端口。

q      3842行设置可广播地址,因为默认情况下是不可广播的。

q      47行开始为主处理过程,发送多次广播数据,查看网络上是否有服务器存在。

q      5055行发送服务器请求到整个局域网上。

q      60行文件描述符集合清零。

q      62行将套接字文件描述符加入读集合。

q      64select侦听是否有数据到来。

q      65行查看select的返回值。

q      67select发生错误。

q      70select超时。

q      75行有可读的数据到来。

q      65行接收数据。

q      80行查看接收到的数据是否匹配。

原创粉丝点击