UDP:用户数据包协议
UDP用户数据报协议: 无连接,不可靠的协议,UDP不需要连接,所以进行高效率传输
适用情况:
- 在接收到数据.给出应答较为困难的网络
- 用于广播/组播
- QQ/微信 视频通话/语音电话
流媒体,VoIP,IPTV等网络服务
通信流程 --- 无连接(connect accept)的过程
UDP 无法判断客户端是否退出: 使用心跳包, 使用客户端, 定时给服务器发送内容
UDP流程:(类似发短信)
server:
创建数据报套接字(socket(,SOCK_DGRAM,))----->有手机
绑定网络信息(bind())-----------> 绑定IP和port(发短信知道发给谁)
接收信息(recvfrom())------------>接收信息,同时可以获取到发送者的IP和port
关闭套接字(close())-------------->接收完毕
client:
创建数据报套接字(socket())----------------------->有手机
指定服务器的网络信息------------------------------>有对方号码
发送信息(sendto())---------------------------->发送短信,根据填充的结构体信息
关闭套接字(close())--------------------------->发送完
注意:
1、对于TCP是先运行服务器,客户端才能运行。
2、对于UDP来说,服务器和客户端运行顺序没有先后,因为是无连接,所以服务器和客户端谁先开始,没有关系,
3、UDP一个服务器可以同时连接多个客户端。想知道是哪个客户端登录,可以在服务器代码里面打印IP和端口号。
以下内容面试可能会问: 感兴趣可以自己测试一下
4、UDP,客户端当使用send的时候,上面需要加connect,,这个connect不是代表连接的作用,而是指定客户端即将要发送给谁数据。这样就不需要使用sendto而用send就可以。
5、在TCP里面,也可以使用recvfrom和sendto,使用的时候将后面的两个参数都写为NULL就OK。
int socket(int domain, int type, int protocol);
头文件: #include
#include
domain:协议族
AF_UNIX, AF_LOCAL 本地通信
AF_INET ipv4
AF_INET6 ipv6
type:套接字类型
SOCK_STREAM: 流式套接字
SOCK_DGRAM: 数据报套接字
protocol:协议 - 填0 自动匹配底层 ,根据type系统默认自动帮助匹配对应协议
传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP
网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)
成功:socket文件描述符
失败:-1,设置错误码
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
头文件:#include
#include
#include
#include
sockfd:套接字文件描述符
addr:通信结构体(提供的是通用结构体,需要根据选择通信方式,填充对应 结构体-通信当 时socket第一个参数确定,需要强转)
通用结构体:
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
ipv4通信结构体:
struct sockaddr_in {
sa_family_t sin_family; ----协议族
in_port_t sin_port; ----端口
struct in_addr sin_addr; ----ip结构体
};
struct in_addr {
uint32_t s_addr; --ip地址
};
addrlen:结构体大小
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
sockfd:套接字描述符
buf:接收缓存区的首地址
len:接收缓存区的大小
flags:0 接收数据 并 阻塞
MSG_DONTWAIT: 设置非阻塞
src_addr: 发送端的网络信息结构体的指针(对方的 caddr)
addrlen:发送端的网络信息结构体的大小的指针(对方的 caddr)
返回值:
成功:接收的字节个数,接收到的数据为0 : 0
失败:-1
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t
sockfd:套接字描述符
buf:发送缓存区的首地址
len:发送缓存区的大小
flags:0 发送消息并阻塞
src_addr:接收端的网络信息结构体的指针
addrlen:接收端的网络信息结构体的大小
返回值:
成功:发送的字节个数
失败:-1
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
char buf[128] = {0};
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("sockfd err");
return -1;
}
struct sockaddr_in saddr, caddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[1]));
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
int len = sizeof(caddr);
if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
{
perror("bind is err");
exit(0);
}
printf("bind ok\n");
printf("等待接收客户端内容\n");
while (1)
{
int recvbyte = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&caddr, &len);
if (recvbyte < 0)
{
perror("bind is err");
exit(0);
}
else
{
printf("ip:%s port:%d 内容:%s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), buf);
}
}
close(sockfd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
int main(int argc, char const *argv[])
{
char buf[128] = {0};
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
perror("sockfd err");
return -1;
}
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(atoi(argv[2]));
saddr.sin_addr.s_addr = inet_addr(argv[1]);
int len = sizeof(saddr);
while (1)
{
fgets(buf, sizeof(buf), stdin);
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = '\0';
sendto(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&saddr, len);
}
close(sockfd);
return 0;
}