单播:
UDP不像TCP,无需在连接状态下交换数据,因此基于UDP的接收方和发送方也无需经过连接过程。也就是说,不必调用listen()和accept()函数。UDP中只有创建套接字的过程和数据交换的过程。不管是服务器端还是客户端都只需要 1 个套接字。
步骤:
1》创建套接字socket
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain,int type, int protocol);
参数:
domain:协议族
AF_INET IPV4
AF_INET6 IPV6
type:
SOCK_STREAM :
SOCK_DGRAM :UDP
protocol:固定写0
返回值:
成功返回描述套接字sockfd,失败返回-1
2》接收方的:bind(绑定自己的IP/PORT);
3》UDP发送函数 -- sendto
#include <sys/types.h>
#include <sys/socket.h>
ssize_t //套接字
const void *buf, //要发送的消息的首地址
size_t len, //消息大小
int flags, //0
const struct sockaddr *dest_addr,//接收方的IP和端口
socklen_t addrlen//上一个参数的大小
);
返回值:成功则返回实际传送出去的字符数,失败返回-1;
4》UDP接收函数 -- recvfrom
ssize_t recvfrom(int sockfd, //套接字
void *buf, //接收到的内容存放的位置的首地址
size_t len,//接收的大小
int flags,//0
struct sockaddr *src_addr, //提供空间
socklen_t *addrlen //上一个参数的大小,需要写地址
);
返回值:如果正确接收返回接收到的字节数,失败返回-1
单播例题
//单播发
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#define PORT 11224
#define IP "192.168.31.36"
int main()//单播发
{
//1.创建套接字
int fd=socket(AF_INET,SOCK_DGRAM,0);
if(fd==-1)
{
perror("socket");
return -1;
}
struct sockaddr_in fa_addr;//暂时发到该结构体内
fa_addr.sin_port=htons(PORT);
fa_addr.sin_addr.s_addr=inet_addr(IP);
fa_addr.sin_family=AF_INET;
//2.发
int num=0;
while(1)
{
sendto(fd,&num,4,0,(struct sockaddr *)&fa_addr,sizeof(fa_addr));
num++;
sleep(1);
}
return 0;
}
//----------单播收
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#define PORT 11224
#define IP "192.168.31.36"
int main()
{
//1.创建套接字
int fd=socket(AF_INET,SOCK_DGRAM,0);
if(fd==-1)
{
perror("socket");
return -1;
}
//2.绑定
struct sockaddr_in du_addr;
du_addr.sin_port=htons(PORT);
du_addr.sin_addr.s_addr=inet_addr(IP);
du_addr.sin_family=AF_INET;
int res=bind(fd,(struct sockaddr *)&du_addr,sizeof(du_addr));
if(res==-1)
{
perror("bind");
return -1;
}
//3.收
int num=0;
struct sockaddr_in o_addr;//用于保存发送端的端口和地址
socklen_t len = sizeof(o_addr);
while(1)
{
recvfrom(fd,&num,4,0,(struct sockaddr *)&o_addr,&len);
printf("num=%d\n",num);
}
return 0;
}
广播
udp具有广播功能,即一个发送方,多个接收方;
广播:处于局部网中的所有设备都可以接收到消息 比如:校园广播
广播的地址:网络号不变,主机号为255
步骤:
1》创建套接字
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain,int type, int protocol);
参数:
domain:协议族
AF_INET IPV4
AF_INET6 IPV6
type:
SOCK_STREAM :
SOCK_DGRAM :UDP
protocol:固定写0
返回值:成功返回描述网络连接套接字sockfd,失败返回-1
2》设置套接字,具有广播功能
socket创建的UDP套接字支持组播和广播,但是想要使用广播,必须用setsockopt设置广播的功能
int setsockopt(int sockfd,
int level,
int optname,
const void *optval,
socklen_t optlen
);
函数功能:设置套接字的选项
参数: sockfd:socket创建的套接字fd
level--选项所在的级别:
想让套接字有广播功能,就必须把level设置为SOL_SOCKET
SOL_SOCKET -- 广播功能所在的级别
optname--选项所在的名称:
SO_BROADCAST (广播功能)
optval: 整数的地址
int num = 1;//开启功能 &num
0--失能(关闭此功能,系统默认关闭)
1--使能(开启此功能)
optlen:optval的大小 //sizeof(num)
3》发送
IP:网络号不变,主机号为255
port:和接收方保持一致
4》接收方bind
IP:INADDR_ANY
port:和发送方的sendto里面的port保持一致
广播例题
//-----------广播发
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#define PORT 11227
#define IP "172.20.10.4"
int main()//发送端
{
//1.创建套接字
int fd=socket(AF_INET,SOCK_DGRAM,0);//IPV4 UDP
if(fd==-1)
{
perror("socket");
return -1;
}
//2.套接字设置
int num=1;//使能,设置套接子具有广播功能
setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&num,sizeof(num));
//3.发送
num=100;
struct sockaddr_in fa_addr;//发送到此
fa_addr.sin_family=AF_INET;//IPV4
fa_addr.sin_port=htons(PORT);
fa_addr.sin_addr.s_addr=inet_addr(IP);
while(1)//循环发
{
sendto(fd,&num,4,0,(struct sockaddr *)&fa_addr,sizeof(fa_addr));
sleep(1);
num++;
}
return 0;
}
//-----------广播收
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#define PORT 11227
int main()//广播收
{
//1.创建套接子
int fd=socket(AF_INET,SOCK_DGRAM,0);
if(fd==-1)
{
perror("socket");
return -1;
}
//2.绑定
struct sockaddr_in s_addr;
s_addr.sin_family=AF_INET;
s_addr.sin_port=htons(PORT);
s_addr.sin_addr.s_addr=INADDR_ANY;
int res=bind(fd,(struct sockaddr *)&s_addr,sizeof(s_addr));
if(res==-1)
{
perror("bind");
return -1;
}
//3.接收
int num=0;
struct sockaddr_in cun_addr;//读取的方到该结构体内
socklen_t len=sizeof(cun_addr);
while(1)
{
recvfrom(fd,&num,4,0,(struct sockaddr *)&cun_addr,&len);
printf("num:%d\n",num);
}
return 0;
}
多播(组播)
对一组特定的主机发送消息 比如:直播,多播(D类),IP地址分为:224.0.0.0~239.255.255.255
步骤:
1》创建套接字 -- socket,创建UDP套接字
fd = socket(AF_INET,SOCK_DGRAM,0);
2》设置组播功能
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
函数功能:设置套接字的选项
参数:
sockfd: socket创建的套接字
level: 级别
SOL_SOCKET : 广播级别
IPPROTO_IP :组播级别
optname: 选项由level决定,level选的是IPPROTO_IP,optname有两种选择
IP_ADD_MEMBERSHIP 加入组播
IP_MULTICAST_IF 创建组播
optval:设置参数 由optname决定
当optname为:
IP_ADD_MEMBERSHIP(加入组播),optval 是struct ip_mreqn 这个结构体
IP_MULTICAST_IF (创建组播),optval 也是struct ip_mreqn 这个结构体
struct ip_mreqn
{
struct in_addr imr_multiaddr;//多播组的地址224.0.0.0-239.255.255.255 “224.0.0.2” struct in_addr imr_address;//本地的IP地址,填固定的宏INADDR_ANY即可
int imr_ifindex; //物理硬件地址:可以用物理硬件ID函数:if_nametoindex("ens33"); 其中ens33是网卡的名字,通过名字获取编号
}; 头文件:#include <net/if.h>
optlen:optval的大小
3》发送
IP:多播组的地址224.0.0.0-239.255.255.255
选择一个即可 “224.0.0.2”
port:和接收方保持一致
4》接收方bind
IP:INADDR_ANY
port:和发送方的sendto里面的port保持一致
TCP例如:
当服务器结束之后,再次运行会出现bind错误(地址被占用)
解决办法: int sockfd =
socket(AF_INET,SOCK_STREAM,0);
创建套接字之后,用:
int val = 1;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val));
setsockopt(sockfd,SOL_SOCKET,SO_REUSEPORT,&val,sizeof(val));