TCP/IP协议概念通俗讲解, 端口号与套接字的区别

网上对TCPIP协议、以及端口号、套接字以及绑定的概念已经写的很多了。但不是所有的都适合新手,最近下进行FreeRTOS+TCP开发时,发现官方的解释特别通俗易懂,因此对我对简介部分进行了翻译,方便懒得看英文的人也能瞧瞧,内容来源于:https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/index.html。翻译里设计的部分程序中的宏定义的说明,不关心的人自行忽略就是。

 

以太网:数据以以太网帧的方式再本地以太网中传输。

以太网地址:以太网通过网络用于将数据从一个节点搬运到另一个节点。以太网帧中的数据可能只是原始数据,但通常与另一个协议(如Internet协议(IP))相关联,而IP协议又可能携带更多的协议,如UDP或TCP。

同一以太网中的不同节点用它们的MAC地址(硬件地址)来加以区分。MAC地址通常写为6字节(8位值),用冒号分隔,例如00:12:34:56:78:90。本地以太网中的每个节点必须有一个唯一(unique)的MAC地址。

MTU:MTU代表最大传输单元,是硬件(物理层)的一个特性。MTU的大小以字节(octets )为基本单位。

MTU Size:

MTU大小定义了发送到网络或从网络接收到的数据包或帧的最大大小。如果应用程序发送一小块的适合一个帧发送的数据,那么仅仅一个帧将被发送到网络上。如果应用程序发送的数据块大于MTU的大小,那么数据将被分成多个包,每个包将创建一个小于或等于MTU大小的帧。(注:区分包和帧的概念)

在FreeRTOS+TCP程序中,MTU以字节为单位指定,并由FreeRTOSIPConfig.h中的 ipconfigNETWORK_MTU 设置。一些MAC设备被限制在1400。

IP:

Internet协议是TCP/IP和UDP/IP中的“IP”。IP协议簇(suit)是被用于如因特网这样的跨网络(内部网络)通信的协议。每个IP网络中的节点由它们的IP地址标识。IP数据包可以用以太网帧来传输(be carried in),也可以用于传输(carry)TCP和UDP数据。

IP地址

同一IP网络上的不同节点由它们的IP地址标识。例如,您正在用来阅读本文的计算机上的每个网络接口将具有不同的IP地址。又如,您的计算机所连接的网络上的每个远程接口(remote interface )也将具有不同的IP地址。

有两种常用的IP地址格式。目前,绝大多数远程地址使用IPv4格式,这是一个32位的数字,但最常见的写法是四个由点分隔的8位字节(8位数字)。例如“192.168.0.32”。

ARP:

ARP代表地址解析协议。

IP数据包是在IP地址之间发送的,但是封装它们的以太网帧是在MAC(硬件)地址之间发送的。因此,在将IP包发送到以太网之前,必须知道目标IP地址的MAC地址。

地址解析协议(ARP)用于获取MAC地址信息。FreeRTOS+TCP(与大多数(如果不是所有的话)IP栈一样)将IP地址存储到一个ARP表(有时称为ARP缓存)中的MAC地址映射。ARP由TCP/IP协议栈(stack)自动处理。

子网 Subnet:

网允许将IP地址的最重要位解释为路由信息,而将IP地址的最不重要位解释为本地IP网络上的唯一节点地址。本地IP网络(子网)是不使用网关或路由器就可以寻址的网络。子网之间的通信必须通过路由器。

子网掩码 Netmask:

被解释为包含路由信息的位数由子网掩码决定。在子网掩码中设置一个位意味着该位被解释为路由信息。例如,如果IP地址是10.134.134.10,网掩码是255.255.0.0,则10.134提供路由信息,134.10提供本地地址信息。从10.134开始的IP地址可以直接发送到它们在本地网络上的目的地。以任何其他数字开头的IP地址不在本地网络上,因此必须发送到路由器。

路由器 Router:

网络掩码用于将IP地址细分为提供路由信息的字节和提供本地地址信息的字节。如果用子网掩码按位处理的目标IP地址与用子网掩码按位处理的本地IP地址不匹配,则两个IP地址在同一网络上不存在。在这种情况下,发送到目标地址的包不能直接发送,必须发送到路由器进行智能的网络间路由。

网关 Gateway

网关地址是路由器的IP地址——路由器是通往其他(远程)网络的网关。

FreeRTOS+TCP确定一个IP包是否可以直接发送,或者是否需要发送到路由器。FreeRTOS+TCP用户只需要提供一个网关地址。与IP地址一样,网关的IP地址可以静态地配置为FreeRTOS_IPInit()的参数,也可以从DHCP服务器动态地配置。

TCP:

以太网包可以携带IP包,而IP包又可以携带TCP包。

TCP代表传输控制协议。TCP用于在预先建立的连接上发送和接收数据流。TCP协议本身负责建立连接,并确保正确接收所有传输的数据。

TCP比UDP更可靠,但使用起来更复杂,且需要更多的内存。额外RAM的使用,一部分是因为在数据包被确认正确发送到网络前,需要保留这些已经发送到网络的数据包。

MSS:

MSS代表最大段大小。它定义了在TCP或UDP包中可以发送或接收的最大数据量。它与MTU值的不同之处在于,它的值只适用于数据大小,而不适用于帧大小。因此MSS排除了以太网、IP、TCP或UDP协议头。MSS取决于MTU和最大选项字节数。

下面是一个从1526字节的MTU开始的MSS计算示例。帧内包含的各个header消耗的字节数被减去,得到MSS大小:

1526  MTU size
 -14  Ethernet header size
 -20  IP protocol header size
 -20  TCP protocol header size
 -12  TCP options bytes
----
1460  MSS size

在FreeRTOS+TCP中,MSS值由FreeRTOSIPConfig.h中的ipconfigTCP_MSS设置。如果ipconfigTCP_MSS没有定义,那么它将被设置为默认值1460。

在上面的例子中,计算出的MSS值为1460字节,这对于局域网(LAN)是合适的,但是对于Internet来说可能太大了,为了最大的可靠性,MSS应该限制在1400字节。因此,如果远程节点的IP地址在本地网络之外(请参阅netmask), FreeRTOS+TCP将自动将MSS设置为1400或配置的ipconfigTCP_MSS值的最小值。

端口号(Port Number:

每个单独的网络节点可以运行多个使用相同网络接口的应用程序,因此可以使用相同的IP地址。例如,RTOS应用程序可以同时运行TFTP服务器、echo服务器和Nabto客户机 - 所有这些都使用了TCP/IP协议栈。因此在使用相同的IP地址的情况下,运行在相同网络节点的不同应用程序通过它们的端口号加以区别。

因此,每个TCP或UDP数据包的源地址和目标地址是通过IP地址和端口号组合的。IP地址标识网络上的节点,而端口号标识节点内的应用程序(参见sockets)。

网络套接字(Network Sockets:

套接字在概念上是通信的端点( end point ),而Berkeley sockets API实际上是用于创建、配置、读写和管理套接字的跨平台标准API。

套接字是用网络节点的IP地址和端口号来标识的。

如果一个网络节点想要向网络发送UDP数据,它首先创建一个套接字,然后将数据发送到该套接字。如果一个网络节点想要接收UDP数据,它首先在发送数据的节点所知道的地址上创建一个套接字,然后从该套接字读取数据。

如果一个网络节点希望向网络发送TCP数据,它首先创建一个套接字,将该套接字连接到远程节点上的一个套接字,然后将数据发送到该套接字。如果一个网络节点想要接收TCP数据,首先要创建一个套接字,然后监听套接字上的传入连接。当接收到连接时,它可以(可选地)创建一个新的套接字来处理连接,然后接收新套接字上的数据——这样可以让原来的套接字继续侦听额外的传入连接。

由此可以看出,任何一个网络节点都可以同时参与多个网络会话( network conversations)——每个唯一会话的每一端都使用一个套接字。

套接字还可以用来发送和接收广播和多播通信——它们都是一对多通信的一种形式。

API函数FreeRTOS_socket()用于创建套接字。

绑定 Binding:

如前所述,每个套接字需要一个唯一的地址,地址是IP地址和端口号的组合。

在创建套接字时,它假定已经创建了它的网络节点的IP地址。如果一个套接字有一个IP地址,但没有端口号,则称为“未绑定”。未绑定的套接字无法接收数据,因为它没有完整的地址。

当一个套接字同时具有一个IP地址和一个端口号时,它被称为“绑定到一个端口”或“绑定到一个地址”。绑定套接字可以接收数据,因为它有一个完整的地址。

API函数FreeRTOS_bind()用于将FreeRTOS+TCP套接字绑定到端口号。

如果在FreeRTOSIPConfig.h中将ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND设置为0,那么必须使用FreeRTOS_bind()将套接字绑定到端口号,然后才能使用套接字发送或接收数据。如果在FreeRTOSIPConfig.h的 ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND设置为1一个未绑定套接字将自动绑定到一个端口号它试图第一次发送数据(用于UDP套接字)或连接(用于TCP套接字),但是仍然可以只接收数据后绑定。

客户机 Client:

客户机是主动连接到远程应用程序以从远程应用程序获得服务的应用程序。远程应用程序是服务器。

服务器 Server

服务器是等待并回复来自客户端的请求的应用程序。

客户端需要定位服务器,实现这一点的最简单方法是将服务器绑定到预先约定的地址(还有其他更复杂的方法)。

服务器不需要提前知道客户的地址,它们只需要将它们的回复发送到客户请求发出的地址。因此,,尽管从0xC000到0xFFFF范围内的高端口号是为FreeRTOS+TCP本身保留的,同时许多低端口号(仅根据协定来看)是为公共网络业务(network)保留的,但是通常客户端可以绑定到几乎任何端口号。

静态IP地址

已经注意到,每个网络节点都有一个IP地址。如果IP地址是“静态的”,那么它是预先分配的,不会改变。

FreeRTOS+TCP API函数FreeRTOS_IPInit()接受一个默认的IP地址(在其他地址中)作为参数之一。如果将ipconfigUSE_DHCP设置为0,或者将ipconfigUSE_DHCP设置为1,但是无法联系DHCP服务器,则使用默认的IP地址作为静态IP地址。

DHCP

DHCP代表动态主机控制协议( Dynamic Host Control Protocol.)。

静态IP地址在应用程序开发期间是有用的,但对于产品部署来说是不切实际的,因为:

  • 它们需要硬编码到可执行的二进制文件或外部闪存中。
  • 如果不事先了解产品部署的网络环境,就不能将IP地址预先分配给产品。
  • 我们不知道网络上将存在多少节点,也不知道在任何时刻可能有多少节点处于活动状态。

DHCP为静态IP地址分配提供了另一种选择。DHCP服务器存在于本地网络上,动态地将IP地址分配给同一网络上的节点。当一个启用了网络的产品启动时,它会联系DHCP服务器请求它的IP地址,这样就不需要对每个节点进行静态配置。

如果在FreeRTOSIPConfig.h中将ipconfigUSE_DHCP设置为1,那么FreeRTOS+TCP将尝试从DHCP服务器获取其IP地址,并且只有在无法联系到DHCP服务器时才恢复使用静态IP地址(AutoIP也在路线图中)。

运行FreeRTOS+TCP的设备可以向DHCP服务器注册它的主机名。有关更多信息,请参见ipconfigDHCP_REGISTER_HOSTNAME配置常量。

专家用户可以使用应用程序DHCP钩子(或“回调”)函数来影响DHCP进程。

网络名称解析 Network Name Resolution

使用原始IP地址对远程节点寻址并不总是可行的,因为:

  • IP地址可以改变。
  • 远程计算机的IP地址可能不知道。
  • IP地址不是很好记。

使用人类可读的名称来定位远程节点更为方便。将人类可读(注:即英文或汉字,但不是不易记忆的数字)的名称转换为IP地址的过程称为名称解析。FreeRTOS+TCP可实现DNS, LLMNR和NBNS的功能,从而达到网络域名解析的目的。

 

DNS:

DNS代表域名系统( Domain Name System),是域名解析的一种形式。

dns将静态且易于人类阅读的文本(而非数字)名称映射到IP地址。域名服务器将文本域名解析为适当的IP地址。例如,进入“ping www.freertos.org ”命令控制台的台式电脑将显示一个ping请求被发送到IP地址195.8.66.1(在撰写本文时,IP地址可能会改变)因为一个DNS服务器解决了IP地址195.8.66.1字符串“www.freertos.org”。

如果在FreeRTOSIPConfig.h中将ipconfigUSE_DNS设置为1,那么FreeRTOS+TCP API函数FreeRTOS_gethostbyname()可用于将文本名称解析为IP地址。

与运行FreeRTOS+TCP的节点的IP地址一样,可以将域名服务器的IP地址静态地配置为FreeRTOS_IPInit()的参数,也可以从DHCP服务器动态地配置。

 

LLMNR:

LLMNR代表链接本地多播名称解析( Link Local Multicast Name Resolution),这是一种用于名称解析的协议。

LLMNR是一种在局域网中使用的多播协议。它是所有主要web浏览器用于解析不包含点(' . ')的名称的方法。例如,如果尝试打开web页面 :http://my_freertos_device/index.html  (注:例子中的链接无法打开)。然后,web浏览器将发送一个LLMNR请求,尝试并解析名称“my_freertos_device”。

所有LLMNR包都被发送到MAC地址01:00:5E:00:00:FC上的IP地址224.0.0.252,因此必须对网络接口(MAC)进行编程,以接受该地址上的包,以便LLMNR发挥作用。另外,必须在FreeRTOSIPConfig中将ipconfigUSE_LLMNR定义为1。用户必须提供回调函数xApplicationDNSQueryHook()的实现,该函数接受一个char指针作为参数,如果传入函数的名称与用于标识节点的名称匹配,则返回pdTRUE。

注:这个解释的不太容易懂,可见百度百科[2]

NBNS:

NBNS(有时称为WINS)代表NetBIO名称服务(NetBIO Name Service),这是一个用于名称解析的协议。

NBNS执行与LLMNR相同的功能,但是使用UDP广播包而不是多播包。浏览器通常在尝试使用LLMNR失败后才尝试使用nbn。

要启用NBNS,必须将FreeRTOSIPConfig.h中的ipconfigUSE_NBNS设置为1。与LLMNR一样,应用程序编写器必须提供xApplicationDNSQueryHook()回调函数,该函数接受字符指针作为参数,如果传递给函数的名称与用于的名称匹配,则返回pdTRUE

大小端模式 Little Endian, Big Endian

不同的mcu以不同的方式存储多字节值,比如两个字节的uint16_t或四个字节的uint32_t。首先存储最重要字节的微控制器称为大端字节控制器。首先存储最低有效字节的微控制器称为小端字节控制器。运行FreeRTOS+TCP的MCU上存储字节的方式称为主机字节顺序(host byte order)。

编写非连接应用程序的人很少需要关心他们的目标MCU如何在内部存储数据。如果数据是以小端序写入内存的,那么它将以小端序从内存中读取回来——因此,读取回来的值将与最初写入的值匹配。

连接mcu时,情况会更复杂,因为不能保证连接网络上的所有mcu具有相同的字节顺序。网络上的所有mcu必须事先同意用于发送和接收数据的字节顺序。用于传输数据的字节顺序称为网络字节顺序(network byte order)。

在TCP/IP网络中,数据是先发送最重要的字节,这使得TCP/IP网络有效地实现了大端口号。因此,一个小端字节序单片机发送TCP / IP网络必须交换字节的顺序出现在多字节值前的值发送到网络上,并且必须交换字节的顺序出现在多字节值从网络接收之前所使用的值。一个大端字节控制器不需要执行任何字节交换,因为MCU的端字节(主机字节顺序)匹配网络的约定端字节(网络字节顺序)。

注意:字节交换是由TCP/IP栈执行的——用户不需要手动交换字节。

参考资料

[1] https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers

[2] https://baike.baidu.com/item/LLMNR/1116392?fr=aladdin

[3] https://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/index.html