目录

传输层

再谈端口号

端口号范围划分

认识知名端口号(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、如何交付?

16位源端口号:表示数据从哪里来。16位目的端口号:表示数据要到哪里去。16位UDP长度:表示整个数据报(UDP首部+UDP数据)的长度。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程序时,自定义的应用层协议。

参考文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: