Linux 網絡編程 全解(九)--------UDP和組播、廣播的實現

寫在前面:本文分兩部分,先說UDP的實現,再講下組播的實現。

再後面補充一下UDP廣播的實現。

正文:

一、

1、TCP和UDP通信優缺點


   TCP:面向連接的,可靠數據包傳輸。對於不穩定的網絡層,採取完全彌補的通信方式,丟包重傳機制。

     優點:穩定,數據流量穩定,速度穩定,順序穩定。

     缺點:傳輸速度慢,傳輸效率低,資源開銷大。

     使用場景:數據的完整性要求較高、不追求效率。大數據傳輸、文件傳輸。 

  
  UDP:無連接的,不可靠的數據報傳輸。對於不穩定的網絡層,採取完全不彌補的通信方式,默認還原網絡狀況。


     優點:傳輸速度快、效率高、資源開銷小。

     缺點:不穩定。數據流量不穩定、速度不穩定、順序不穩定。

     使用場景:時效性要求較高,穩定性其次,遊戲、視頻會議、視頻電話。

2、UDP相關的幾個API:recvfrom和sendto函數
   ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
   sockfd: 套接字

   buf:緩衝區地址

   flags:0

   src_addr: 傳出參數,對端地址結構。

   addrlen:傳入傳出

   返回值:成功:返回接受的字節數。  -1,失敗。0,對端關閉。
  
 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
  socket:套接字
  buf:緩衝區

  len:數據長度

  flags:0  

  dest_addr:傳入參數,目標地址結構

  addrlen:地址結構長度

  返回值: 成功:發送的字節數; -1,失敗; 
 

二、UDP Server和UDP Client的實現

    基於TCP的CS模型,其實UDP的CS模型只不過就是砍掉了三次握手,下面貼代碼:

   UDP Server:

#include <stdio.h>
#include <sys/types.h>       
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>

#define SER_PORT (8888)

int main(void)
{
	int sockfd = -1;
	struct sockaddr_in ser_ip,cli_ip;
	ssize_t rev_size = 0;
	unsigned char rd_buf[1024] = {0};
	unsigned char *pStr = "Server recv OK";
	socklen_t cli_addr_len = sizeof(struct sockaddr_in) ;
	
	
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	
	memset(&ser_ip,0,sizeof(ser_ip));
	ser_ip.sin_family = AF_INET;
	ser_ip.sin_port = htons(SER_PORT);
	ser_ip.sin_addr.s_addr = htonl(INADDR_ANY);
	
    bind(sockfd, (const struct sockaddr *)&ser_ip,sizeof(ser_ip));
	
	printf("server prepared OK\n");
	
	while(1)
	{
		memset(rd_buf,0,1024);
		rev_size = recvfrom(sockfd, rd_buf, 1024, 0,
                        (struct sockaddr *)&cli_ip, &cli_addr_len);
						
						
		printf("server role : rev_size = %d, cilent port = %d ,recv context: %s\n  ",rev_size,ntohs(cli_ip.sin_port),rd_buf);
		
		sendto(sockfd, pStr, strlen(pStr), 0,(const struct sockaddr *)&cli_ip, cli_addr_len);
		
		sleep(5);
								
	}
	
	return 0;
}

  UDP Client:

#include "stdio.h"
#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>


#define SER_PORT (8888)

int main(void)
{
	int sockfd = 0;
	const unsigned char * pSendBuf = "This is client";
	struct sockaddr_in ser_ip;
	const char * ser_ip_addr = "127.0.0.1";
	unsigned int  dst_ip_addr;
	unsigned char rd_buf[1024] = {0};
	int rev_size = 0;
	
	ser_ip.sin_family = AF_INET;
	ser_ip.sin_port = htons(SER_PORT);
	inet_pton(AF_INET, ser_ip_addr, &dst_ip_addr);
	ser_ip.sin_addr.s_addr = dst_ip_addr;
	
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	
	printf("client prepared OK\n");
	
	while(1)
	{
		sendto(sockfd, pSendBuf, strlen(pSendBuf), 0,
                      (const struct sockaddr *)&ser_ip, sizeof(ser_ip));
					  
		memset(rd_buf,0,1024);
		rev_size = recvfrom(sockfd, rd_buf, 1024, 0,
                        NULL, NULL);
						
						
		printf("client role : rev_size = %d, recv context: %s\n  ",rev_size,rd_buf);
		
		sleep(5);
		
		
	}

	
	return 0;
}




三、 UDP組播

 1、組播是Server端向局域網中的一個子網內加入了某個組播組的Client批量發送數據,組播的IP地址配置如下:

    224.0.0.0~224.0.0.255 爲預留的組播地址(永久組地址),地址 224.0.0.0 保留不做分配,其它地址供路由協議使用;
    224.0.1.0~224.0.1.255 是公用組播地址,可以用於 Internet; 欲使用需申請。
   224.0.2.0~238.255.255.255 爲用戶可用的組播地址(臨時組地址),全網範圍內有效;
   239.0.0.0~239.255.255.255 爲本地管理組播地址,僅在特定的本地範圍內有效。

2、組播的實現,代碼中也是儘可能的做了註釋:

     組播Server的實現

#include <stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>


#define SER_PORT  (8000)
#define CLI_PORT  (9000)

#define GROUP_IP ("239.0.0.2")
#define LOCAL_IP ("0.0.0.0")


int main(void)
{
	int sockfd = -1;
	struct sockaddr_in ser_addr,cli_addr;
	struct ip_mreq group_addr;
	char muticast_buf[1024] = {"muti caset data"};
	
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	
	/*第一步,綁定Server端的IP地址和端口號*/
	memset(&ser_addr,0,sizeof(ser_addr));
	ser_addr.sin_family = AF_INET;
	ser_addr.sin_port = htons(SER_PORT);
	ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	bind(sockfd, (const struct sockaddr *)&ser_addr,sizeof(ser_addr));

	/*第二步,設置組播地址並指定發送組播數據*/
	memset(&group_addr,0,sizeof(group_addr));
	inet_pton(AF_INET,GROUP_IP,&group_addr.imr_multiaddr); //設置組播地址
	inet_pton(AF_INET,LOCAL_IP,&group_addr.imr_interface); //設置本地IP地址
											
	setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, &group_addr, sizeof(group_addr)); // 制定發送組播數據包
																					  //IP_MULTICAST_IF:指定發送組播數據的命令
																					  
	/*第三步,構造Client端的IP地址和端口號*/
	memset(&cli_addr,0,sizeof(cli_addr));
	cli_addr.sin_family = AF_INET;
	cli_addr.sin_port = htons(CLI_PORT); 
	inet_pton(AF_INET,GROUP_IP,&cli_addr.sin_addr.s_addr);	//注意:這裏的組播客戶端的地址就是組播地址
	
	printf("mutiast config finished ,then send muticast datas\n");
	/*第四步,向制定的組播地址發送數據*/
	while(1)
	{
		//調用sendto來向組播地址發送組播包
		sendto(sockfd, muticast_buf, strlen(muticast_buf), 0,(const struct sockaddr *)&cli_addr, sizeof(cli_addr));
		
		printf("muticast packet send ok\n");
		
		sleep(2);//2s發送一次
	}
	close(sockfd);
	return 0;
}




   組播Client的實現

#include <stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>


#define SER_PORT  (8000)
#define CLI_PORT  (9000)

#define GROUP_IP ("239.0.0.2")
#define LOCAL_IP ("0.0.0.0")


int main(void)
{
	int sockfd = -1;
	struct sockaddr_in cli_addr;
	struct ip_mreq group_addr;
	char recv_buf[1024] = {0};
	int recv_len = -1;
	
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	/*第一步,綁定本地IP地址結構*/
	memset(&cli_addr,0,sizeof(cli_addr));
	cli_addr.sin_family = AF_INET;
	cli_addr.sin_port = htons(CLI_PORT);
	inet_pton(AF_INET,LOCAL_IP,&cli_addr.sin_addr.s_addr);
	
	bind(sockfd, (const struct sockaddr *)&cli_addr,sizeof(cli_addr));
	
	/*第二步,設置組播地址並將client加入組播組*/
	memset(&group_addr,0,sizeof(group_addr));
	inet_pton(AF_INET,GROUP_IP,&group_addr.imr_multiaddr); //設置組播地址
	inet_pton(AF_INET,LOCAL_IP,&group_addr.imr_interface); //設置本地IP地址
											
	setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group_addr, sizeof(group_addr)); // 加入組包組
																					   //IP_ADD_MEMBERSHIP:加入組播組的命令
																					  	
	/*第三步,接受組播數據*/
	while(1)
	{
		memset(recv_buf,0,1024);
		recv_len = recvfrom(sockfd, recv_buf, 1024, 0,NULL, NULL); 
		
		if(recv_len > 0)
		{
			printf("recv_len = %d, recv_buf:%s\n",recv_len,recv_buf);
		}	
		
	}
	close(sockfd);
	return 0;
}









三、UDP廣播

1、先闡述一下IP的東西,IP 地址中 32 位包含兩部分,分別爲:網絡地址和主機地址,網絡地址用來表示子網,主機地址用來表示哪一臺主機。
     子網掩碼用來說明網絡地址和主機地址各佔多少位,子網掩碼(轉成二進制) 爲 1 的位表網絡地址,子網掩碼(轉成二進制)爲 0 的位表主機地址,例如, 255.255.255.0 表前 24 位爲網絡地址,後 8 位爲主機地址,子網掩碼爲 255.255.0.0 表前 16 位爲網絡地址,後 16 位爲主機地址。子網掩碼爲255.255.255.0 中一共可以有 2^24 個網絡,而主機地址決定每個這種網絡可以有多少個主機,這個子網掩碼可以有 2^8 個主機。
     判斷兩個 IP 是否在一個子網內:
網絡標識=IP 地址&子網掩碼, 2 個 IP 地址的網路標識一樣就處於同一個網絡。

2、基於上述,來說明廣播的IP地址,將IP地址的主機地址部分全部置1,則此IP就是該子網內的廣播地址。

    例如:

     IP:  192.168.0.2.  MASK: 255.255.255.0           則廣播地址:192.168.0.255

     IP: 172.16.0.2   MASK: 255.255.0.0                  廣播地址:172.16.255.255

3、廣播的實現,代碼中也是儘可能的做了註釋:

     代碼之前,先ifconfig看一下本地ip地址信息,

注意這兩個IP,inet addr:172.22.199.11 是對外的IP地址,inet addr:127.0.0.1是內部測試的IP地址。

      udp 廣播server部分:

#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> 
#include <string.h>


#define SER_PORT (6666)
#define CLI_PORT  (9000)
#define BROADCAST_IP "172.22.199.255 "

int main(void)
{
	struct sockaddr_in ser_addr,brdcast_sddr;
	char *p_brdcast_buf  = "this is a broadcast test";
	/*第一步,設置server IP地址結構*/
	int sockfd = socket(AF_INET, SOCK_DGRAM,0);
	
	memset(&ser_addr,0,sizeof(ser_addr));
	ser_addr.sin_family = AF_INET;
	ser_addr.sin_port = htons(SER_PORT);
	ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	bind(sockfd, (const struct sockaddr *)&ser_addr,sizeof(ser_addr));

	/*第二步,setsockopt()設定爲發送廣播包*/
	int so_broadcast = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &so_broadcast, sizeof(so_broadcast));//SO_BROADCAST:爲配置廣播選項,
	                                                                                  // so_broadcast :0,關閉廣播;1:開啓廣播
	
	
	/*第三步:設置廣播地址*/
	memset(&brdcast_sddr,0,sizeof(brdcast_sddr));
	brdcast_sddr.sin_family = AF_INET;
	brdcast_sddr.sin_port = htons(CLI_PORT); //注意這裏的端口號一定跟接受廣播客戶端的端口號一致
	
	inet_pton(AF_INET, BROADCAST_IP, &brdcast_sddr.sin_addr.s_addr);

	
	/*第四步,發送廣播包*/
	while(1)
	{
		sendto(sockfd, p_brdcast_buf, strlen(p_brdcast_buf), 0,
                      (const struct sockaddr *)&brdcast_sddr, sizeof(brdcast_sddr));
		printf("broadcast packet send ok\n");
		sleep(2);

	}
	
	
	
	
	
	
	
	
	return 0;
}

udp組播client部分:

#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> 
#include <string.h>


#define CLI_PORT  (9000)



int main(void)
{
	int sockfd = -1;
	struct sockaddr_in cli_addr, ser_sddr;
	unsigned char recv_buf[1024] = {0};
	socklen_t recv_len = 0;
	char ip_addr[16];
	
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	/*第一步,綁定端口號和IP*/
	memset(&cli_addr,0,sizeof(cli_addr));
	cli_addr.sin_family = AF_INET;
	cli_addr.sin_port = htons(CLI_PORT);////注意這裏的端口號一定跟發送廣播的服務器的端口號一致
	cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	bind(sockfd, (const struct sockaddr *)&cli_addr,sizeof(cli_addr));
	
	/*第二步,然後就可以接聽廣播數據了*/
	while(1)
	{
		memset(&ser_sddr,0,sizeof(ser_sddr));
		recvfrom(sockfd, recv_buf, 1024, 0,(struct sockaddr *)&ser_sddr, &recv_len);
		inet_ntop(AF_INET, &ser_sddr.sin_addr.s_addr, ip_addr, 16);
		printf("receive from IP:%s\n",ip_addr);
		printf("receive buffer:%s\n",recv_buf);

		
	}

	
	
	
	
	
	
	
	
	return 0;
}



測試結果一:抓包數據

測試結果二:log信息

測試結果中的這兩個IP,一個是wireshark抓包抓到的,所以相當於抓到的對外IP地址,一個是內部測試的,則log中打印出的是對內IP地址。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章