网络编程3之UDP与广播组播

目录

 

1.UDP

(1)recvfrom

(2)sendto

2.广播组播

1.广播

2.setsockopt

3.组播


1.UDP

 UDP协议与TCP协议有本质的区别

区别就在于TCP每次传输之前都已一对一的建立连接,通过三次握手与四次挥手来实现连接与断开连接,数据传输过程中数据丢失,错误都会重新发送一份数据来代替之前错误的那一份数据,保证了传输的可靠性,但是传输速度比较低

UDP就不存在这个问题,UDP实现的通讯不需要提前连接,只要直到对方的IP端口号,就可以发送数据,而且断开也不会对对方产生任何影响,可以多个客户端连接一个服务器。

UDP的数据收发与TCP锁调用的函数接口不一样,因为没有提前连接,所以每次发送与接收都需要知道对方的IP与端口。

(1)recvfrom

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

功能:
    接收数据

参数:
    sockfd:数据报套接字,sockfd的返回值
    buf:储存的数组,内存地址
    len:数据的大小
    flags:0
    src_addr:发送端的结构体的地址
    addrlen:发送端的结构体长度的地址

返回值:
    成功返回接受的字节数,失败返回-1

(2)sendto

#include <sys/types.h>
#include <sys/socket.h>
       
size_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

功能:
    发送数据

参数:
    sockfd:数据报套接字,sockfd的返回值
    buf:储存的数组,内存地址
    len:数据的大小
    flags:0
    src_addr:接收端的结构体的地址
    addrlen:接收端的结构体长度

返回值:
    成功返回发送的字节数,失败返回-1

UDP:无连接.

多个客户端可以发送消息给服务器

 发送端:

/*===============================================
*   文件名称:server_udp.c
*   创 建 者:     
*   创建日期:2022年08月17日
*   描    述:
================================================*/
#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>


int main(int argc, char *argv[])
{ /*
	if(argc<2)
	{
		printf("输入要发送的ip地址\n");
		return -1;
	}	*/
	
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建套接字
    if(sockfd<0)
    {
        perror("sockfd");
        return -1;
    }

    struct sockaddr_in server,client;//创建结构体来保存客户端与服务器的数据
    server.sin_family=AF_INET;
    server.sin_port=htons(8888);
    server.sin_addr.s_addr=inet_addr("0");
    int n=sizeof(server);
 

    char buf[64]={0};
    int m=sizeof(server);
    while(1)
    {
    	
    	fgets(buf,sizeof(buf),stdin);//从终端输入数据
    	buf[strlen(buf)-1]='\0';//去除多读到的回车   	
        sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&server,m);//发送数据        
        memset(buf,0,sizeof(buf));
    }
    close(sockfd);

    return 0;
} 

接收端:

/*===============================================
*   文件名称:server_udp.c
*   创 建 者:     
*   创建日期:2022年08月17日
*   描    述:
================================================*/
#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>


int main(int argc, char *argv[])
{ 

	
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建套接字
    if(sockfd<0)
    {
        perror("sockfd");
        return -1;
    }

    struct sockaddr_in server,client;//创建结构体来保存客户端与服务器的数据
    server.sin_family=AF_INET;
    server.sin_port=htons(8888);
    server.sin_addr.s_addr=inet_addr("0");
    int n=sizeof(server);

    int ret = bind(sockfd,(struct sockaddr *)&server,n);//绑定套接字
    if(ret<0)
    {
        perror("bind");
    }   

    char buf[64]={0};
    int m=sizeof(client);
    while(1)
    {
        int n=recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&client,&m);
        printf("n=%d",n);
        printf("clinet IP=%s client port=%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));//打印发送端的数据
        printf("message:%s\n",buf);
        memset(buf,0,sizeof(buf));
    }
    close(sockfd);

    return 0;
} 

运行结果

2.广播组播

1.广播

前面介绍的数据包发送方式只有一个接收方,称为单播

如果同时发给局域网中的所有主机,称为广播

网段中最大的地址称为广播地址,例如,192.168.22.0网段的广播地址为192.068.22.255,向此地址发送数据会被该网段中的所有主机接收

最大的广播是255.255.255.255.

广播特点:同时发送给局域网中的所有主机 UDP

发送广播消息:(client)

1.socket(AF_INET, SOCK_DGRAM, 0);

2.struct sockaddr_in argv[1] //向广播地址发送 //192.168.2.255

3.setsockopt //开广播权限

4.sendto (如果不开权限,会报错)

接收广播消息:(server)

1.socket(AF_INET, SOCK_DGRAM, 0);

2.struct sockaddr_in serveraddr;

3.bind //"0"地址 or 广播地址(只能收广播消息)

4.recvfrom 组播特点:既可以发送多个

广播发送端:

/*===============================================
*   文件名称:server_udp.c
*   创 建 者:     
*   创建日期:2022年08月17日
*   描    述:
================================================*/
#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
/*
void *recv_data(void *arg)
{
	int sockfd= *(int *)arg;
	char buf[64]={0};
	struct sockaddr_in server,client;
    	server.sin_family=AF_INET;
    	server.sin_port=htons(7777);
    	server.sin_addr.s_addr=inet_addr("0");
    	int n=sizeof(server);
    	
	while(1)
	{
		recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&client,&n);
		printf("message:%s\n",buf);
		memset(buf,0,sizeof(buf));
	}
}
*/


int main(int argc, char *argv[])
{ 
	if(argc<2)
	{
		printf("输入要发送的ip地址\n");
		return -1;
	}	
	
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建套接字
    if(sockfd<0)
    {
        perror("sockfd");
        return -1;
    }
    /*
    pthread_t tid;
    pthread_create(&tid,NULL,recv_data,(void *)&sockfd);
    pthread_detach(tid);
    */
    int on=1;//开启广播权限
    int k = setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
    if(k<0)
    {
    	perror("setsockopt");
    	return -1;
    }
    


    struct sockaddr_in server,client;//创界结构体保存输入输出端的信息
    server.sin_family=AF_INET;
    server.sin_port=htons(8888);
    server.sin_addr.s_addr=inet_addr(argv[1]);
    int n=sizeof(server);
 

    char buf[64]={0};
    int m=sizeof(client);
    while(1)
    {
    	
    	fgets(buf,sizeof(buf),stdin);//从终端输入
    	buf[strlen(buf)-1]='\0';   	
        sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&server,m);//发送到接收端
        
        memset(buf,0,sizeof(buf));
    }
    close(sockfd);

    return 0;
} 

广播接收端:

/*===============================================
*   文件名称:server_udp.c
*   创 建 者:     
*   创建日期:2022年08月17日
*   描    述:
================================================*/
#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
/*
void *recv_data(void *arg)
{
	int sockfd= *(int *)arg;
	char buf[64]={0};
	struct sockaddr_in server,client;
    	server.sin_family=AF_INET;
    	server.sin_port=htons(8888);
    	server.sin_addr.s_addr=inet_addr("0");
    	int n=sizeof(server);
    	
	while(1)
	{
		recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&client,&n);
		printf("message:%s\n",buf);
		memset(buf,0,sizeof(buf));
	}
}

*/

int main(int argc, char *argv[])
{ 

	
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建套接字
    if(sockfd<0)
    {
        perror("sockfd");
        return -1;
    }
    /*
        //创建线程
    pthread_t tid;
    pthread_create(&tid,NULL,recv_data,(void *)&sockfd);
    pthread_detach(tid);
*/
    struct sockaddr_in server,client;//创建结构体保存两端的信息
    server.sin_family=AF_INET;
    server.sin_port=htons(8888);
    server.sin_addr.s_addr=inet_addr("0");
    int n=sizeof(server);

    int ret = bind(sockfd,(struct sockaddr *)&server,n);//绑定套接字
    if(ret<0)
    {
        perror("bind");
    }   

    char buf[64]={0};
    int m=sizeof(client);
    while(1)
    {
        recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&client,&m);//接收信息
        printf("clinet IP=%s client port=%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));//打印发送端的信息
        printf("message:%s\n",buf);	
        memset(buf,0,sizeof(buf));
    }
    close(sockfd);

    return 0;
} 

广播结果;

2.setsockopt

#include <sys/types.h>         
#include <sys/socket.h>

int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);

功能:修改套接字的属性
参数:
    sockfd:数据报套接字,sockfd的返回值
    level:属性设置
            SOL_SOCKET:广播属性
            IPPROTO_IP:组播属性
    
    optname    :具体的属性修改,见以下属性设置表    
    optval:广播时,填入on的地址,组播时,填入结构体的地址
    optlen:广播时,填入on的长度,组播时,填入结构体的长度
    
返回值:
    成功返回0,失败返回-1


属性设置表:level
SOL_SOCKET
------------------------------------------------
参数optname 宏的作用 对应参数optaval的类型
SO_BROADCAST 允许发送广播数据 int
SO_DEBUG 允许调试 int
SO_DONTROUTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
SO_LINGER 延迟关闭连接 struct linger
SO_OOBINLINE 带外数据放入正常数据流 int
SO_RCVBUF 接收缓冲区大小 int
SO_SNDBUF 发送缓冲区大小 int
SO_RCVLOWAT 接收缓冲区下限 int
SO_SNDLOWAT 发送缓冲区下限 int
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval
SO_REUSEADDR 允许重用本地地址和端口 int
SO_TYPE 获得套接字类型 int
SO_BSDCOMPAT 与BSD系统兼容 int
======================================================
IPPROTO_IP
------------------------------------------------------
IP_ADD_MEMBERSHIP 加入到组播组中 struct ip_mreq
IP_MULTICAST_IF 允许开启组播报文的接口 struct ip_mreq
    
eg:
    广播,打开广播权限
        
    int on=1;
    int k = setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
    
    组播,把指定ip加入到指定组播中
    
    struct ip_mreqn mreq;
    mreq.imr_multiaddr.s_addr=inet_addr("224.10.10.10");
    mreq.imr_address.s_addr=inet_addr("0");
    
    setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));

3.组播

组播特点:既可以发送多个主机, 又避免像广播一样造成广播风暴 UDP

 组播地址:

224.0.0.1~239.255.255.255

发送组播消息:(client)

1.socket

2.struct sockaddr_in argv[1]//向组播地址发送 //224.10.10.10

3.sendto

接收组播消息:(server) man 7 ip

1.socket

2.struct sockaddr_in serveraddr;

3.struct ip_mreqn mreq; // 组播地址 + "0"地址

4.setsockopt //加入多播组

5.bind

6.recvfrom

struct ip_mreqn {

struct in_addr imr_multiaddr; // IP multicast group address

struct in_addr imr_address; //IP address of local interface

int imr_ifindex; //interface index

};

组播发送端:

/*===============================================
*   文件名称:server_udp.c
*   创 建 者:     
*   创建日期:2022年08月17日
*   描    述:
================================================*/
#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


int main(int argc, char *argv[])
{ 
	if(argc<2)
	{
		printf("输入要发送的ip地址\n");
		return -1;
	}	
	
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建套接字
    if(sockfd<0)
    {
        perror("sockfd");
        return -1;
    }

    struct sockaddr_in server,client;	//创建结构体存储发送端,接收端的信息
    server.sin_family=AF_INET;
    server.sin_port=htons(8888);
    server.sin_addr.s_addr=inet_addr(argv[1]);
    int n=sizeof(server);
 

    char buf[64]={0};	//缓冲区
    int m=sizeof(client);
    while(1)
    {
    	
    	fgets(buf,sizeof(buf),stdin);	//从终端输入
    	buf[strlen(buf)-1]='\0';	//处理输入的回车
    	
        sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&server,m);//发送数据
        
        memset(buf,0,sizeof(buf));//清空缓冲区
    }
    close(sockfd);

    return 0;
} 

组播接收端;

/*===============================================
*   文件名称:server_udp.c
*   创 建 者:     
*   创建日期:2022年08月17日
*   描    述:
================================================*/
#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


int main(int argc, char *argv[])
{ 

	
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建套接字
    if(sockfd<0)
    {
        perror("sockfd");
        return -1;
    }

    struct sockaddr_in server,client;//创建结构体来存储发送端与接收端的信息
    server.sin_family=AF_INET;
    server.sin_port=htons(8888);
    server.sin_addr.s_addr=inet_addr("0");
    int n=sizeof(server);
    
    struct ip_mreqn mreq;
    mreq.imr_multiaddr.s_addr=inet_addr("224.10.10.10");
    mreq.imr_address.s_addr=inet_addr("0");
    
    setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));//把"0"加入到组“224.10.10.10”中
    

    int ret = bind(sockfd,(struct sockaddr *)&server,n);//绑定套接字
    if(ret<0)
    {
        perror("bind");
    }   

    char buf[64]={0};	//缓冲区
    int m=sizeof(client);
    while(1)
    {
        recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&client,&m);//接收数据
        printf("clinet IP=%s client port=%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));//打印发送端的信息
        printf("message:%s\n",buf);//打印数据
        memset(buf,0,sizeof(buf));
    }
    close(sockfd);

    return 0;
} 

运行结果

 

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值