您现在的位置是:首页 >技术教程 >【网络】-- UDP协议网站首页技术教程

【网络】-- UDP协议

川入 2023-06-14 04:00:02
简介【网络】-- UDP协议

目录

传输层

再谈端口号

端口号范围划分

认识知名端口号(Well-Know Port Number)

两个问题

netstat

pidof

UDP协议 

UDP的特点

UDP的缓冲区

UDP使用注意事项

基于UDP的应用层协议 


传输层

        负责数据能够从发送端传输接收端。

再谈端口号

        端口号(Port) 标识了一个主机上进行通信的不同的应用程序。

        说白了就是进行通讯的不同的进程。所以当底层收到数据时,其如果只知道目标地址,其根据IP地址就能找到对应的主机。但是接下来需要知道的就是,这个数据报应该向上递交给哪一个进程,即根据对应的Port端口号。

        在 TCP/IP协议 中,用 "源IP""源端口号""目的IP""目的端口号""协议号" 这样一个五元组来标识一个通信 (可以通过 netstat -n查看)

端口号范围划分

         0 - 1023:知名端口号,HTTP(80),HTTPS(443),SSH(22),FTP(21 / 23)等这些广为使用的应用层协议,他们的端口号都是固定的 (比如生活中:110一定时报警、120一定是急救)
        1024 - 65535:操作系统动态分配的端口号,客户端程序的端口号,就是由操作系统从这个范围分配的,用户可以自行绑定的端口。

认识知名端口号(Well-Know Port Number)

        有些服务器是非常常用的, 为了使用方便, 人们约定一些常用的服务器 都是用以下这些固定的端口号:
  • ssh服务器,使用22端口
  • ftp服务器,使用21端口
  • telnet服务器,使用23端口
  • http服务器,使用80端口
  • https服务器,使用443
        执行 cat /etc/services 命令,可以看到知名端口号
Note: 我们自己写一个程序使用端口号时,要避开这些知名端口号。

        远程利用windows使用Xshell软件,连接远在外省的机器,因为我们使用的Xshell时客户端,而远端的服务器周而复始的,以启动一个绑定端口是22号的服务:

        进一步查看该服务:

        这个服务以d结尾,表示是守护进程(一般都是,不是强制规定),也可以看见其的SID与PID是相等的。

  • 守护进程:这个进程不退出,一直在后端提供服务。

         所以,当我们用Xshell连接之后,我们就能直接登上了,当我们启动的时候,一登陆成功,这个sshd就会在终端当中,为我们形成加载bash进程,然后给我们打出命令符。让我们进行输指令。

        然后将我们的指令字符串不断以网络发送的形式发送给服务器,然后让服务端执行,然后执行完再将结果给我们。

两个问题

#问: 一个进程是否可以bind多个端口号?

         可以的!

#问: 一个端口号是否可以被多个进程bind?

        不能! 

融汇贯通的理解:
         一个端口号被建立好并和进程绑定,而当有一个报文到了的时候,其是如何将对应的数据交给对应的进程的?


        常识:一个进程在进行服务的时候,除了listen套接字之外,每一次获取新连接(都会得到一个新的sockfd),对应的sockfd就是一个文件描述符。

        换句话说:其实在我们看来一个连接可以说就是一个文件,所以所谓的收到数据的本质就是:将数据放到对应的文件的接收缓冲区里,然后当前进程通过文件描述符进行文件的读取(因为进程与文件是通过文件描述表建立的关系)。无论未来进程想读取数据还是从底层网络里将数据拿上来,我们只要找到了进程,我们就能找到给进程所相关的文件缓冲区。

        所以:对于如何将底层TCP、UDP收到的数据交给特定的网络连接,其实就是转换成了:如何通过底层收到的端口号(携带端口号的相关报文),交付给特定进程。

        其做法:在内核当中其是采取哈希映射的关系,可以理解为key值为端口号,value值为进程的PCB的地址,所以当底层一旦收到数据时,系统当中直接查哈希表,直接找到对应的进程,然后就可以通过我们(我们在网络操作中,调用系统调用读数据的时候传的就是文件描述符)文件描述符,直接放在特定的文件当中,然后进程在上层用户调用read的时候就可以读取到了。

netstat

netstat 是一个用来查看网络状态的重要工具。
语法:netstat [选项]
功能:查看网络状态
常用选项
(显示不全,就需要进行提升)
  • n 拒绝显示别名,能显示数字的全部转化成数字。

  • l 仅列出有在 Listen (监听) 的服务状态。

  • p 显示建立相关链接的程序名。

  • t (tcp)仅显示tcp相关选项。
  • u (udp)仅显示udp相关选项。
  • a (all)显示所有选项,默认不显示LISTEN相关。

pidof

在查看服务器的进程 id 时非常方便 .
语法 pidof [ 进程名 ]
功能 :通过进程名 , 查看进程 id


        其中UDP就属于传输层,并且端口号也是在传输层起作用。 

UDP协议 

#问:几乎任何协议都要首先解决的两个问题:a、如何分离(封装)? b、如何交付?

  1. 16位源端口号:表示数据从哪里来。
  2. 16位目的端口号:表示数据要到哪里去。
  3. 16位UDP长度:表示整个数据报(UDP首部+UDP数据)的长度。
  4. 16位UDP检验和:如果UDP报文的检验和出错,就会直接将报文丢弃。
  • a、如何分离(封装)?

        采用固定报头长度的策略(8个字节)。因为udp是不可靠协议,只要能够保证把数据能够交到对方主机(当然不保证也可以,反正只要发出去)就可以了,所以是固定长度。

        可以保证:将报头和有效载荷的分离。

  • b、如何交付? 

        根据报头中的16位端口号,进行向上交付。因为进程bind了端口号!之所以可以,就是因为这是一个约定,是一个客户端与服务端都知道,并且必须遵守的约定,这就叫做 "协议"

总结:

  • 所以对于应用层的代码编写的时候,都是用uint16_t类型来接收端口号。
  • udp通过固定长度的报头中提取到16位的UDP长度,以此知道如何正确的提取整个完整的报文数据。

即:根据上述可知,UDP是具有将报文一个一个正确接收的能力的!则,UDP是面向数据报的。

#问:如何理解udp报文的本身?

         底层中,这里所谓的报头实际上叫做:struct udp_hdr。传说中的udp报头,以及未来的所有报头,其实就是一个结构体类型(位断)。

struct udp_hdr
{
    uint32_t src_port:16;
    uint32_t dst_port:16;
    uint32_t udp_len:16;
    uint32_t udp_check:16;
}

UDP的特点

UDP传输的过程类似于寄信。

  • 无连接:知道对端的IP和端口号就直接进行传输,不需要建立连接。
  • 不可靠:没有确认机制,没有重传机制。如果因为网络故障该段无法发到对方,UDP协议层也不会给应用层返回任何错误信息。

不可靠:

        指的是中性的特点,没有贬义的意思。在长距离传输的时候,如果要保证可靠性,一定意味着要做更多的工作,就意味着匹配的协议在实践上更复杂,其由于更加的复杂度,所以,其在使用成本以及维护成本上都是更高的。

        所以不可靠并不是十分的不好,一定原则上来说,反而更简单。

  • 面向数据报: 不能够灵活的控制读写数据的次数和数量。

面向数据报:

        应用层交给UDP多长的报文,UDP原样发送,既不会拆分,也不会合并

用UDP传输100个字节的数据:

  • 如果发送端调用一次sendto,发送100个字节,那么接收端也必须调用对应的一次recvfrom,接收100个字节。
  • 而不能循环调用10次recvfrom,每次接收10个字节。

        怎么发就必须怎么收,发的报文必须是一个完整的报文。

#问:sendto / recvfrom / write / read / recv / send ……io类接口?

        在以往看来:这些函数是在网络中进行数据的收发(根本不是!)

        这些函数,其实本质是:拷贝函数!

        应用层用的都是系统调用接口,而目前我们所用的所有协议(通讯原则),全部用的是UDP / TCP提供的接口,所以内核层中的缓冲区一般用的都是传输层协议提供的。

        也就是说:对应传输层 -> 传输,即:缓冲区数据什么时候发,发多少,出错了怎么办,由其关心并处理。所以我们在应用层所写的代码,本质上:还是系统编程。我们将数据交给了参数层的缓冲区,并没有发送出去。

UDP的缓冲区

        UDP没有真正意义上的发送缓冲区。因为调用sendto会直接交给内核,由内核将数据传给网络层协议进行后续的传输动作。所以其对于发送缓冲区并没有特别强的需求,反正有数据就往下交。

        但是,UDP需要具有一定的接收缓冲区因为UDP上面就是应用层,应用层就是用户,如果程序员来不及调用recvfrom(因为,同时可能会到来很多UDP报文),来不及接收就有可能导致有些报文直接就被丢弃了,UDP不可靠,但是也要为丢包所带来的成本负责。(一个报文从一台主机传输到另一台主机,在传输过程中会消耗主机资源和网络资源。如果UDP收到一个报文后仅仅因为上次收到的报文没有被上层读取,而被迫丢弃一个可能并没有错误的报文,这就是在浪费主机资源和网络资源)

        这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致。如果缓冲区满了,再到达的UDP数据就会被丢弃。

        UDP的socket既能读,也能写,这个概念叫做:全双工。

全双工 VS 半双工:

        全双工 :一个文件描述符,如果既能读,同时又能写(可以理解为两个线程,同时一个线程读,一个线程写)。保证:只要接收缓冲区和发送缓冲区不冲突即可,不要用同一个缓冲区。

        半双工:一个文件描述符,再任意时刻,要么只能读,要么只能写,不能二者同时并行(可以理解为两个人正常聊天,只能其中一个人在说话)。

UDP使用注意事项

        我们注意到,UDP协议首部中有一个16位的最大长度,也就是说一个UDP能传输的数据最大长度是64K(包含UDP首部)。

        然而64K在当今的互联网环境下,是一个非常小的数字。

        如果我们需要传输的数据超过64K,就需要在应用层手动的分包,多次发送,并在接收端手动拼装。

基于UDP的应用层协议 

  • NFS:网络文件系统。
  • TFTP:简单文件传输协议。
  • DHCP:动态主机配置协议(比如:笔记本连接网络前,上不了网,本质:没有IP地址。当连上网后,笔记本会自动获取一个IP地址,是路由器给的IP地址,就是路由器中部署了一种服务:DHCP)。
  • BOOTP:启动协议(用于无盘设备启动)。
  • DNS:域名解析协议。

同时,也包括我们字节所写得UDP程序时,自定义的应用层协议。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。