目录
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
功能: 接收数据
参数: sockfd:数据报套接字,sockfd的返回值 buf:储存的数组,内存地址 len:数据的大小 flags:0 src_addr:发送端的结构体的地址 addrlen:发送端的结构体长度的地址
返回值: 成功返回接受的字节数,失败返回-1
(2)sendto
#include
功能: 发送数据
参数: sockfd:数据报套接字,sockfd的返回值 buf:储存的数组,内存地址 len:数据的大小 flags:0 src_addr:接收端的结构体的地址 addrlen:接收端的结构体长度
返回值: 成功返回发送的字节数,失败返回-1
UDP:无连接.
多个客户端可以发送消息给服务器
发送端:
/*===============================================
* 文件名称:server_udp.c
* 创 建 者:
* 创建日期:2022年08月17日
* 描 述:
================================================*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
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
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
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;
}
运行结果
参考阅读
发表评论