【Linux网络编程】传输层中的TCP和UDP(UDP篇)
目录
- 【Linux网络编程】传输层中的TCP和UDP(UDP篇)
- 传输层
- 再谈端口
- 端口号范围划分
- 认识知名端口号
- netstat
- iostat
- pidof
- xargs
- UDP协议
- UDP协议端格式
- UDP的特点
- 面向数据报
- UDP的缓冲数据
- UDP使用注意事项
- 基于UDP的应用层协议
作者:爱写代码的刚子
时间:2024.5.11
前言:本篇博客将会深入介绍UDP,了解UDP的机制
传输层
负责数据能够从发送端传输接收端
再谈端口
端口号(Port)标识了一个主机上进行通信的不同的应用程序;
在TCP/IP协议中, 用 “源IP”, “源端口号”, “目的IP”, “目的端口号”, “协议号” 这样一个五元组来标识一个通信(可以通过 netstat -n查看);
端口号范围划分
0 - 1023: 知名端口号, HTTP, FTP, SSH等这些广为使用的应用层协议, 他们的端口号都是固定的.
1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的.
认识知名端口号
- ssh服务器, 使用22端口
- ftp服务器, 使用21端口
- telnet服务器, 使用23端口
- http服务器, 使用80端口
- https服务器,使用443
执行下面的命令可以看见一些知名端口号
- vim /etc/services查看所有端口号
所以我们自己写一个程序使用端口号时,要避开这些知名端口号
【问题】:1. 一个进程是否可以bind多个端口号? 2. 一个端口号是否可以被多个进程bind?
- 一个进程通常只能绑定一个端口号。端口号是用来标识网络通信中的特定应用程序或服务的,因此一个进程通常只需要一个端口来监听来自其他进程的连接请求或发送数据。但是,在某些情况下,一个进程可能会使用多个端口来处理不同类型的通信,比如一个Web服务器可能同时监听HTTP(端口80)和HTTPS(端口443)连接。
- 一般情况下,一个端口号只能被一个进程绑定。这是因为端口号是唯一的,用于标识不同的网络服务或应用程序。 如果多个进程都试图绑定到同一个端口号,通常会导致端口冲突,其中只有一个进程能够成功绑定,而其他进程可能会收到绑定失败的错误。
netstat
netstat是一个用来查看网络状态的重要工具
语法:netstat [选项]
功能:查看网络状态
常用选项:
- n 拒绝显示别名,能显示数字的全部转化成数字
- l 仅列出有在 Listen (监听) 的服务状态
- p 显示建立相关链接的程序名(进程名)
- t (tcp)仅显示tcp相关选项
- u (udp)仅显示udp相关选项
- a (all)显示所有选项,默认不显示LISTEN相关
- netstat
- sudo netstat -nltp完整显示进程名(不隐藏)
iostat
iostat 是一个系统性能监控工具,用于报告CPU利用率和设备(磁盘、终端、网络等)的使用情况。它能够提供关于系统I/O活动的详细信息,包括每个磁盘的读写速率、平均响应时间、CPU利用率等。
iostat # 显示所有设备的统计信息
iostat -d # 显示磁盘的统计信息
iostat -x # 显示扩展信息,包括每个设备的更详细的统计数据
iostat -t # 显示时间戳
iostat -c # 显示CPU利用率
pidof
在查看服务器的进程id时非常方便.
语法:pidof [进程名]
功能:通过进程名, 查看进程id
- pidof httpd根据 httpd 这个进程名拿到所有的pid,每个数字代表一个正在运行的 httpd 进程的进程 ID
xargs
使用管道时是以标准输入的方式交给kill命令,我们如果想以命令行参数的形式交给kill的话就需要借助xargs命令了
UDP协议
网络套接字编程时用到的各种接口,是位于应用层和传输层之间的一层系统调用接口,这些接口是系统提供的,我们可以通过这些接口搭建上层应用,比如HTTP。我们经常说HTTP是基于TCP的,实际就是因为HTTP在TCP套接字编程上搭建的。
而socket接口往下的传输层实际就是由操作系统管理的,因此UDP是属于内核当中的,是操作系统本身协议栈自带的,其代码不是由上层用户编写的,UDP的所有功能都是由操作系统完成,因此网络也是操作系统的一部分。
UDP协议端格式
【问题】:UDP如何将报头和有效载荷进行分离?采用定长报头
【问题】:收到了报文但是如何知道交给上层的哪一个协议?
根据16位(内核和协议确定的)的目的端口号,交给对应的进程。(通过端口号来标定目标进程)
- 16位UDP长度, 表示整个数据报(UDP首部+UDP数据)的最大长度;
- 如果校验和出错,就会直接丢弃;
UDP的特点
UDP传输的过程类似于寄信
- 无连接: 知道对端的IP和端口号就直接进行传输, 不需要建立连接;
- 不可靠: 没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层 返回任何错误信息;
- 面向数据报:不能够灵活的控制读写数据的次数和数量;
面向数据报
应用层交给UDP多长的报文, UDP原样发送, 既不会拆分, 也不会合并;
用UDP传输100个字节的数据:
- 如果发送端调用一次sendto, 发送100个字节, 那么接收端也必须调用对应的一次recvfrom, 接收100个字节;而不能循环调用10次recvfrom,每次接收10个字节;
UDP的缓冲数据
- UDP没有真正意义上的 发送缓冲区. 调用sendto会直接交给内核, 由内核将数据传给网络层协议进行后 续的传输动作;
- UDP具有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报的顺序(乱序就是一种不可靠性)和发送UDP报的顺序一致; 如果缓冲区满了, 再到达的UDP数据就会被丢弃;
全双工的概念:UDP的socket既能读,也能写(之前的多线程版的聊天室既能读也能写说明是全双工的)
UDP使用注意事项
我们注意到, UDP协议首部中有一个16位的最大长度. 也就是说一个UDP能传输的数据最大长度是64K(包含UDP首部).
然而64K在当今的互联网环境下, 是一个非常小的数字.
如果我们需要传输的数据超过64K, 就需要在应用层手动的分包, 多次发送, 并在接收端手动拼装;
协议本质是约定,Linux操作系统是用C语言写的,UDP报头其实是自定义结构体(位段),是类型就可以定义变量,开辟空间。操作系统也要对多个同时存在的UDP报文进行管理(sk_buff结构体来管理)。
struct udp_header {
uint32_t src_port:16; //源端口
uint32_t dst_port:16; //目的端口
uint32_t length:16; //数据包长度
uint32_t check_code:16; //校验和
};
struct sk_buff
{
char *start;
char *end;
char *pos;
int type;
//...
struct sk_buff *next;
};
操作系统中也存在大量的sk_buff,操作系统采用链表的形式来管理sk_buff。
基于UDP的应用层协议
-
NFS: 网络文件系统
-
TFTP: 简单文件传输协议
-
DHCP: 动态主机配置协议
-
BOOTP: 启动协议(用于无盘设备启动)
-
DNS:域名解析协议
也包括自己写的UDP程序时自定义的应用层协议