前言

说明:

  • demo 基于 Linux。

5. UDP 网络编程

UDP 是无连接的,不需要建立连接。

5.1 UDP 的工作原理

参考图:

主机B的数据包中包含目的主机的IP+端口号。

其中IP是把数据的目的主机地址,端口号是目的主机对用的程序。

路由器小知识

  • IP:主机地址。如目的IP,每个路由器都会检查IP,若在本地,就往本地端口转发,若不在,就往外端口发。(在转发过程中是不会变的)
  • MAC:设备地址。目的MAC,下一个物理连接的设备地址。(在转发过程中可能会变,因为参考的是下一个,而不是最终目的)

5.2 UDP 的高效性

UDP 不需要建立连接和响应校验,实时性比 TCP 高。

一般用在网络实时传递的视频或者音频中,因为丢失部分数据也不会影响太大。

5.3 实现 UDP 服务端/客户端

5.3.1 概念

UDP 服务器和客户端均只需一个套接字:

  • TCP 中,套接字之间应该是一对一的关系。
  • UDP 中,不管是服务器端还是客户端都只需要 1 个套接字,就可以任意传输。

如图:

5.3.2 UDP 的数据 I/O 函数

/*
sock: 用于传输数据的 UDP 套接字
buff: 保存待传输数据的缓冲地址值
nbytes: 待传输的数据长度,以字节为单位
flags: 可选项参数,若没有则传递 0
to: 存有目标地址的 sockaddr 结构体变量的地址值
addrlen: 传递给参数 to 的地址值结构体变量长度
成功时返回传输的字节数,失败时返回 -1
*/
#include <sys/socket.h>
ssize_t sendto(int sock, void *buff, size_t nbytes, int flags,
struct sockaddr *to, socklen_t addrlen); /*
sock: 用于传输数据的 UDP 套接字
buff: 保存待传输数据的缓冲地址值
nbytes: 待传输的数据长度,以字节为单位
flags: 可选项参数,若没有则传递 0
from: 存有发送端地址信息的 sockaddr 结构体变量的地址值
addrlen: 保存参数 from 的结构体变量长度的变量地址值。
成功时返回传输的字节数,失败时返回 -1
*/
#include <sys/socket.h>
ssize_t recvfrom(int sock, void *buff, size_t nbytes, int flags,
struct sockaddr *from, socklen_t *addrlen);

5.3.3 UDP 客户端地址分配

TCP 中客户端地址可以设置也可以系统分配,(TCP connect() 函数自动完成分配IP&端口号),建立连接后就固定使用。

UDP 中客户端中调用 sendto() 函数时自动分配 IP 和 端口号,首次调用才分配,分配后使用直至程序结束(有兴趣可以看看UDP打洞技术)。

也可以在调用 sendto() 函数前使用 bind() 函数绑定本机 IP。

5.4 UDP 的数据传输特性

TCP 是流式的数据传输,消息没有边界,需要应用层自己去定义消息边界。

UDP 是数据报传输,所以协议保证了一次只能接收一个数据报。

个人表达:数据边界意思是,数据会不会自动分割,比如两个结构体连续存在一段内存中,那是有边界的,结构体把其分割了。若把其数据拷贝到数组里面,那是无边界的,因为分不清从哪里才是分割线

所以UDP中本端发 N 次到对端,对端就得收 N 次。

5.5 UDP 调用 connect 函数

UDP套接字分

  • 未连接(unconnected)UDP 套接字。
  • 已连接(connect)UDP 套接字。

了解下 sendto() 函数传输数据过程

  1. 第 1 阶段:向 UDP 套接字注册目标 IP 和端口号。
  2. 第 2 阶段:传输数据。
  3. 第 3 阶段:删除 UDP 套接字中注册的目标地址信息。

其实需要频繁发送,那第一阶段和第三阶段是重复多余的,所以可以使用 已连接(connect)UDP 套接字。

创建已连接 UDP 套接字

  • 注意:这里的已连接并不是与对端建立连接,而是绑定目标端口到 UDP socket 中,后面调用 sendto() 就不用执行①、③步了。
sock = socket(PF_INET, SOCK_DGRAM, 0);
memset(&adr, 0, sizeof(adr));
adr.sin_family = AF_INET;
adr.sin_addr.s_addr = inet_addr(argv[1]);
adr.sin_port = htons(atoi(argv[2]));
connect(sock, (struct sockaddr *)&adr, sizeof(adr));

小知识

  • UDP 是可以使用 bind() 函数的,主要是配置本地IP和端口号。若不适用,则由系统随机分配。
  • UDP 是可以使用 connect() 函数的,主要是配置远端IP和端口号。若不使用,则每次调用 sendto() 函数时都要设置、删除远端IP和端口号,耗时。

参考

最新文章

  1. 使用Struts2实现数据校验
  2. appium-UI automator viewer 无[ resource-id ]项
  3. Android安全问题 程序锁
  4. UNIX标准化及实现之选项
  5. #291 div.2
  6. javascript isNaN
  7. python装饰器实现对异常代码出现进行监控
  8. 关于EffictiveC++笔记
  9. spring boot 配置 fastjson 替代 Jackson (并解决返回字符串带双引号问题)
  10. Linux 第十五天
  11. grep,sed,awk用法整理
  12. 修复运行 tasklist 命令时提示 ERROR: Not found
  13. BZOJ3676[Apio2014]回文串——回文自动机
  14. Xcode调试LLDB
  15. Pandas简易入门(一)
  16. tomcat管理页面403 Access Denied的解决方法
  17. PAT甲级1055 The World&#39;s Richest【排序】
  18. Eclipse.Error.gen already exists but is not a source folder.
  19. 马士兵hadoop第五课:java开发Map/Reduce
  20. [Ubuntu] fg、bg让你的进程在前后台之间切换

热门文章

  1. Hibernate框架(二)POJO对象的操作
  2. 14 shell 函数
  3. php混淆加密解密实战
  4. 《基于JQuery和CSS的特效整理》系列分享专栏
  5. vue3 封装简单的 tabs 切换组件
  6. Spark编程基础_RDD编程
  7. 给你的Mac 整个好用的命令行iTerm2 + zsh + oh-my-zsh + powerlevel10k
  8. 【字符串+排序】宇宙总统 luogu-1781
  9. MySQL架构及优化原理
  10. UnitTest 用法