目录
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;
}
运行结果