2017年5月12日 星期五

[C] 簡單的 Broadcast 程式


通常使用以下兩種協定來實現
(1) ARP 協定 (Address Resolution Protocol):
利用這個協定來找出 IP 位址到 MAC 位址的對應關係。

當主機 A 準備與主機 B 通信時,如果只知道主機 B 的 IP 位址,則主機 A 會向網域中發送一個 ARP request ,詢問此 IP 的 MAC 是多少 ~

而當前網域中的所有主機都會收到這個 request ,但是只有 B 主機會給予 reply


(2) NTP 協定 (Network Time Protocol):
在支援廣播的區域網路中設定 NTP 協定,可以使 NTP 伺服器每隔一段固定的時間間隔就向網域中發送 "時間資訊" ,客戶端收到時間資訊後再進行處理更新




解釋原理
如果你有上過網路概論一定會知道,在 IP 位址中如果最後一個數字為 255,則可以確定它是一個廣播位址。但是在實際情況下,廣播位址又可分成以下幾類 ...

(1) 網路廣播位址:
網路廣播位址在沒有進行子網路劃分的網路內進行廣播,由於當前的網路均涉及子網劃分,所以這種位址是很少存在的


(2) 受限廣播位址:
以 255.255.255.255 組成的廣播位址,但是在當前 Router 均不轉發此類廣播


(3)子網廣播位址:
這是最常見的廣播方式,比如 140.134 是網路 ID ,那麼一個針對子網 140.134.1.255 的廣播就是子網路 140.134.1 中的廣播


(4) 全部子網廣播位址:
如果繼續以上例為例的話,它的全部子網廣播位址就是 140.134.255.255




具體設計流程如下
(1) 建立 UDP 通訊端 (因為廣播通訊要採 UDP 方式)

(2) 設定通訊端的屬性為 SO_BROADCAST (就是廣播屬性)

(3) 設定廣播位址為 INADDR_BROADCAST ,同時記得指定發送的連接埠

(4) 進行資料收發


我的完整程式碼在此: https://github.com/a1996850622/Broadcast


程式碼
<註> 真正的廣播程式絕對不只我寫的這樣而已, 其中還有很多 BUG 或者需要改進的點, 我今天只是大概運用其原理而已, 其他的先不深入探討

(1) Server 端程式
/* broadcast_server.c */
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <unistd.h>

/*Set port number*/
#define PORT 8888

int main(){
 int sockfd;
 struct sockaddr_in server_addr, client_addr;
 int so_broadcast = 1;
 char buf[1024];

 /*Create an IPv4 UDP socket*/
 if((sockfd = socket(PF_INET, SOCK_DGRAM, 0))<0){
  perror("socket");
  return -1;
 }

 /*SO_BROADCAST: broadcast attribute*/
 if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &so_broadcast, sizeof(so_broadcast))<0){
  perror("setsockopt");
  return -1;
 }

 server_addr.sin_family = AF_INET; /*IPv4*/
 server_addr.sin_port = htons(INADDR_ANY); /*All the port*/
 server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); /*Broadcast address*/

 if((bind(sockfd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr))) != 0){
  perror("bind");
  return -1;
 }

 client_addr.sin_family = AF_INET; /*IPv4*/
 client_addr.sin_port = htons(PORT);  /*Set port number*/
 client_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); /*The broadcast address*/
 int clientlen = sizeof(client_addr);

 while(1){
  printf("Please input your word :> ");
  //scanf("%s", buf);
  fgets(buf, sizeof(buf), stdin); /*U can enter string by yourself*/

  /*Use sendto() to send messages to client*/
  /*sendto() doesn't need to be connected*/
  if((sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&client_addr, (socklen_t)clientlen)) < 0){
   perror("sendto");
   return -1;
  } 

  else
   printf("send msg %s\n", buf);
 }

 close(sockfd);
 return 0;
}


(2) Client 端程式
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <unistd.h>

#define PORT 8888

int main(){
 int sockfd;
 struct sockaddr_in serv_addr, client_addr;
 int yes = 1;
 ssize_t size;
 socklen_t addrlen = sizeof(client_addr);
 char buf[200];

 //Create an IPv4 and UDP socket
 if((sockfd = socket(PF_INET, SOCK_DGRAM, 0))<0){
  perror("socket");
  return -1;
 }

 //Set the struct of sockaddr_in
 serv_addr.sin_family = AF_INET;
 serv_addr.sin_port = htons(PORT);
 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); /*All the host*/

 /*Set communication address can be reused*/
 if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))<0){
  perror("setsockopt");
  return -1;
 }

 if(bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) != 0){
  perror("bind");
  return -1;
 }

 while(1){
  memset(buf, 0, 200); /*Clean up the buffer*/
  size = 0;

  /*Use recvfrom() to receive the messages from server*/
  size = recvfrom(sockfd, buf, 200, 0, (struct sockaddr *)&client_addr, &addrlen);
  if(size<0){
   perror("recvfrom");
   return -1;
  }

  //buf[size] = '\0'; /* '\0' means final character*/

  printf("IP:%s msg: %s\n", inet_ntoa(client_addr.sin_addr), buf);

  /*If the buffer message is "quit", we will close socket fd and end the process*/
  if(strcmp(buf, "quit") == 0){
   printf("system quit!\n");
   close(sockfd);
   return 0;
  }
 }

 close(sockfd);
 return 0;
}




執行結果圖
不需做連線, 直接向網域中所有 host 的8888 port 做廣播
Server 和 Client 都啟動後, 由 Server 向 Client 送 "Hey! How are u today?" 的訊息








Client 將會收到此訊息

沒有留言:

張貼留言