今天学习了UDP网络编程,UDP属于无连接的通信,不用像TCP那样先建立连接,直接发数据包就行。优点是简单、开销小,缺点就是不可靠,发出去的数据对方不一定能收到,也没有重传机制。像直播、语音通话很多就会用UDP,允许丢一点点数据,优先保证速度。
UDP通信
UDP发送端:
- socket 创建套接字
- sendto 直接把数据发给目标地址
UDP接收端:
- socket 创建套接字
- bind 绑定本机IP和端口
- recvfrom 等待接收对方发来的数据
注意:发送端可以不用bind,操作系统会自动随机分配端口;接收端必须bind,固定端口,别人才知道往哪个端口给你发消息。
函数接口:
- socket
int socket(int domain, int type, int protocol);
作用:创建通信端点,拿到一个文件描述符,后续收发数据都靠它。
domain:协议族,写AF_INET代表IPv4
type:套接字类型。SOCK_DGRAM就是UDP;SOCK_STREAM是TCP
protocol:一般直接填0,自动匹配对应协议
返回值:成功返回文件描述符,失败返回‑1。
- sendto
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
UDP发送数据专用函数。因为UDP没有连接,每次发送都要带上目标的IP和端口。
sockfd:socket返回的文件描述符
buf:要发送的数据缓冲区
len:要发送多少字节
flags:一般填0
dest_addr:目标主机的地址结构体,存对方IP+端口
addrlen:地址结构体的大小
返回值:成功返回实际发送的字节数,失败‑1。
- sockaddr_in结构体(IPv4地址)
struct sockaddr_in {
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
};
struct in_addr {
uint32_t s_addr;
};
这个结构体用来装IP和端口。
sin_family:协议族,填AF_INET
sin_port:端口号,必须网络字节序
sin_addr.s_addr:IP地址,同样是网络大端字节序
编程里经常会强制转换成struct sockaddr*传给函数,这是接口的固定写法。
-
htons
uint16_t htons(uint16_t hostshort);
主机字节序转网络大端字节序。
我们电脑大多是小端存储,网络传输统一规定用大端。设置端口的时候必须调用这个函数做转换,不然端口号会错乱。
-
inet_addr
in_addr_t inet_addr(const char *cp);
把字符串形式的IP,比如"192.168.1.100",转换成网络字节序的二进制IP,直接赋值给s_addr。
-
bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
把套接字和本机IP、端口绑定在一起。接收端必须bind。
注意:
-
只能绑定自己本机的IP,不能绑定别的机器IP
-
同一个端口被别的程序占用,bind直接失败
返回值:成功0,失败‑1。
- recvfrom
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
UDP接收数据。不仅拿到数据,还能拿到发送方的IP和端口,收到之后就知道是谁发过来的,后续可以回复对方。
buf:存放收到数据的缓冲区
len:缓冲区最大能收多少字节
src_addr:输出参数,会填充发送者的地址信息
addrlen:传入地址结构体大小,函数内部会修改
返回值:成功返回收到字节数,失败‑1。
- inet_ntoa
char *inet_ntoa(struct in_addr in);
和inet_addr反过来。把网络二进制IP,变回我们看得懂的字符串IP,打印日志的时候经常用。
UDP报文头部
UDP数据包头部一共8个字节:
源端口、目的端口、数据包长度、校验和,剩下全部是应用数据。
特点:
- 实现逻辑简单,代码写起来比TCP少很多
- 资源开销小,传输速度快
- 不可靠:没有确认机制,数据可能丢包、乱序,适合对实时性要求高,能接受少量丢包的场景。
总结:
- UDP无连接,发送端不需要connect,用sendto;接收端必须bind。
- 端口号一定要用htons转网络字节序,IP用inet_addr转换。
- sendto每次都要指定目标地址;recvfrom可以获取发送方地址。
- bind不能绑定别的主机IP,端口被占用会绑定失败。
- UDP优点速度快,缺点不可靠,不保证数据一定到达。