目录
一、初识网络协议
1.1 简单理解
1.2 OSI七层模型
1.3 TCP/IP四层(或五层)模型
二、网络传输基本流程
2.1 概念
2.2 报头
三、网络中的地址管理
3.1 认识IP地址
3.2 端口号
3.3 Mac地址
四、认识TCP协议和UDP协议
五、网络字节序
六、socket编程接口
6.1 socket套接字
6.2 socket 常见API
6.3 sockaddr结构
6.4 简单的UDP网络程序
一、初识网络协议
1.1 简单理解
网络协议是计算机网络通信的基础。网络通信的数据传输距离变长,中间可能经过多个主机,这可能引发很多问题,为了保证数据能从主机A可靠的传输到主机B,就要解决以下问题:
- 怎么保证数据先交给下一跳主句。
- 如何正确进行路径选择、定位目标主机。
- 如果报文中间出现错误,或者丢失该如何处理。
- 送达的数据还要解决如何使用数据的问题。
上面的每一个问题都要有协议来解决。可以简单理解成网络协议是一种约定或标准,制定出来大家都需要来遵守,这样就可以让不同的计算机相互顺畅的通信。
网络通信的解决方案,是通过网络的层状结构来实现的。在软件上,绝大部分的解决方案都是层状的,这是因为完成软件的解耦合,未来我们可以非常方便的进行软件的维护(更新、替换化等)。这也是总结出来的规律。
1.2 OSI七层模型
OSI(Open System Interconnection,开放系统互连)七层网络模型称为开放式系统互联参考模型,是一个逻辑上的定义和规范。
- 把网络从逻辑上分为了7层. 每一层都有相关、相对应的物理设备,比如路由器,交换机;
- OSI 七层模型是一种框架性的设计方法,其最主要的功能使就是帮助不同类型的主机实现数据传输;
- 它的最大优点是将服务、接口和协议这三个概念明确地区分开来,概念清楚,理论也比较完整. 通过七个层次化的结构模型使不同的系统不同的网络之间实现可靠的通讯;
- 虽然OSI七层模型在理论上很完善,但在实际应用中,由于其过于复杂和某些层功能重叠,大多数网络实现采用了更为简洁的TCP/IP模型。TCP/IP模型通常被描述为四层(或五层,加上物理层)模型。
1.3 TCP/IP四层(或五层)模型
TCP/IP是一组协议的代名词,它还包括许多协议,组成了TCP/IP协议簇.
TCP/IP通讯协议采用了5层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求.
- 物理层: 负责光/电信号的传递方式。 比如现在以太网通用的网线(双绞 线)、早期以太网采用的的同轴电缆(现在主要用于有线电视)、光纤, 现在的wifi无线网使用电磁波等都属于物理层的概念。物理层的能力决定了最大传输速率、传输距离、抗干扰性等。 集线器(Hub)工作在物理层。
- 数据链路层: 负责设备之间的数据帧的传送和识别。 例如网卡设备的驱动、帧同步(就是说从网线上检测到什么信号算作新帧的开始)、冲突检测(如果检测到冲突就自动重发)、数据差错校验等工作。 有以太网、令牌环网, 无线LAN等标准。 交换机(Switch)工作在数据链路层。
- 网络层: 负责地址管理和路由选择。 例如在IP协议中, 通过IP地址来标识一台主机, 并通过路由表的方式规划出两台主机之间的数据传输的线路(路由)。 路由器(Router)工作在网路层。
- 传输层: 负责两台主机之间的数据传输。 如传输控制协议 (TCP), 能够确保数据可靠的从源主机发送到目标主机。
- 应用层: 负责应用程序间沟通,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。 我们的网络编程主要就是针对应用层
物理层我们考虑的比较少. 因此很多时候也可以称为 TCP/IP四层模型。
不同层解决上面不同的问题:
- 应用层:送达的数据,还要解决如何使用数据的问题。
- 传输层:如果报文中间出现错误,或者丢失。
- 网络层:在转发中,如何进行路径选择,目标主机定位的问题
- 数据链路层:怎么保证把数据先交给下一跳主机。
一般而言
- 对于一台主机, 它的操作系统内核实现了从传输层到物理层的内容;
- 对于一台路由器, 它实现了从网络层到物理层;
- 对于一台交换机, 它实现了从数据链路层到物理层;
- 对于集线器, 它只实现了物理层
二、网络传输基本流程
2.1 概念
数据传输要贯穿整个计算机体系结构,同时也要贯穿整个网络协议栈。
从上到下,网络协议栈中的每一层都负责特定的功能,从最顶层的用户层开始,向下依次是传输层、网络层、数据链路层,最后是网卡。这些层共同协作,确保数据能够安全、准确地从一台计算机传输到另一台计算机。
在计算机体系结构中,数据传输同样是从最顶层的用户层开始,经过操作系统、驱动程序,最终到达硬件层。在这个过程中,数据首先在用户层被处理,然后通过系统调用接口传递给操作系统,操作系统再将其转发给驱动程序。驱动程序负责与硬件设备交互,将数据写入网卡,并通过网络发送出去。
因此,网络协议栈和计算机体系结构中的数据传输过程实际上是相互关联、协同工作的。网络协议栈提供了数据传输的标准和方法,而计算机体系结构则提供了实现这些标准和方法的平台。两者共同作用,使得数据能够在不同的计算机之间进行高效、安全的传输。
不同的操作系统在底层实现网络功能时可能会有所差异,但是它们提供的网络系统调用接口往往具有相似性。这是因为网络协议(如TCP/IP)是标准化的,而系统调用接口作为这些协议与用户空间应用程序之间的桥梁,也需要保持一定的通用性。
两台计算机通过TCP/IP协议通讯的过程如下所示:
跨网段的主机的文件传输。数据从一台计算机到另一台计算机传输过程中要经过一个或多个路由器。
2.2 报头
类似于网购,我们在收到快递时不仅会收到我们的商品,也会收到一张快递单。快递单本身就是一种约定,也是一种协议,这个快递单就是报头。
如果用计算机C语言来表示快递单,需要先描述再组织,即用结构体表示,而协议通常也是用结构化字段表征的。这个结构化字段定义的对象,我们叫做协议报头。
报头是数据包中的一个部分,它包含了关于该数据包的元信息和控制信息。在网络通信中,每个数据包在被发送之前都会被附加一个报头。
以下是报头的简要解释:
- 定义:报头是一组数据,位于数据包的开始部分,用于描述数据包的属性和如何处理数据包。
- 内容:报头包含了一系列字段,每个字段都有特定的功能,如指示数据包的目标地址、源地址、协议类型、数据包长度、校验和等。
- 功能:
目标地址:告诉网络设备数据包应该被发送到哪个设备或网络。
源地址:标识数据包的发送者。
协议:指示数据包所使用的上层协议(如TCP、UDP)。
长度:数据包的总长度,包括报头和数据。
校验和:用于检测数据在传输过程中是否损坏。- 重要性:报头对于确保数据包能够被正确地发送、接收和处理至关重要。没有报头,网络设备将无法确定如何处理收到的数据。
- 层次:在OSI模型的不同层次中,报头的内容和格式会有所不同。例如,网络层的IP报头与传输层的TCP报头是不同的。
数据包封装和分用:
- 不同的协议层对数据包有不同的称谓:应用层中叫request&response,传输层中叫数据段(segment),网络层中叫数据报 (datagram),链路层中叫数据帧(frame)。
- 应用层数据通过协议栈发到网络上时,每层协议都要加上一个数据首部(header),也就是报头。称为封装(Encapsulation)。
- 数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部,根据首部中的 "上层协议字段" 将数据交给对应的上层协议处理。
数据封装的过程:
在上面的网络传输流程图中,经过每一层,都要加上对应的报头,即每一层的要发送的报文=报头+有效载荷。用户在发送请求时要一次封装报头,那么传到另一个服务器/用户时就要考虑解包的过程。比如:1.如何将报文中的报头和有效载荷进行分离;2.任何协议,都要解决如何将自己的有效载荷,交付给上层的那一个协议。(每层协议都必须解决这两个问题)
数据分用的过程:
三、网络中的地址管理
3.1 认识IP地址
IP协议有两个版本, IPv4和IPv6。我这里提到IP协议默认都是指IPv4。
- IP地址是在IP协议中, 用来在互联网上唯一标识和定位设备;
- 对于IPv4来说,IP地址由32位二进制或4个八位二进制数组成的十进制数表示。
- 通常以点分十进制的形式表示,即四个0到255之间的十进制数字,每个数字代表一个字节(8位),并用点号分隔,例如 192.168.0.1
在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址和目的IP地址,分别用于标识数据包的发送方和接收方。IP地址用于在网络上进行数据包的传输和路由选择。
IP地址的组成分为两部分:网络地址和主机地址。
- 网络地址用于标识所连接的网络,决定了数据包在互联网中如何被路由。
- 主机地址则用于标识具体的设备,确保数据包能够被发送到正确的设备。
3.2 端口号
端口号在网络通信中用于标识在同一台设备上运行的不同网络应用程序或服务,即表示一个进程,告诉操作系统, 当前的这个数据要交给哪一个进程来处理。
- 每个端口号是一个2字节16位的数字,范围从0到65535。
- IP地址 + 端口号能够标识网络上的某一台主机的某一个进程。原因:ip地址用来标识互联网中唯一的一台主机。端口号用来标识该指定的机器中进程的唯一性。由于数据传输要贯穿整个计算机体系结构和网络协议栈,网络通信的行为本质就是进程间通信。对于双方而言:1. 先把数据到达自己的机器(ip)2. 找到指定的进程(port:端口号)。所以[ip, port] 就能定位到互联网中唯一一个进程。[ip, port]由socket(套接字/口)实现。
- 一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定。
端口号能用来标识该指定的机器中进程的唯一性,进程pid也能标识进程的唯一性,为什么又要设计端口号而不复用进程pid呢?原因:为了解耦合,进程pid的实现机制改变时不会影响到网络通信。不同的操作系统有不同的PID分配策略,因此复用PID可能会导致跨平台通信的问题。端口号作为网络协议的一部分,其设计遵循了统一的标准,使得不同的操作系统和网络设备之间能够兼容。
3.3 Mac地址
MAC地址即媒体访问控制地址,也称为物理地址或硬件地址。它是一个用于在局域网中唯一标识网络适配器(如网卡)的48位二进制数,即6个字节. 一般用16进制数字加上冒号的形式来表示(例如: 08:00:27:03:fb:19)。
- 在网卡出厂时就确定了, 不能修改. mac地址通常是唯一的,以避免网络中的地址冲突。
- 虚拟机中的mac地址不是真实的mac地址, 可能会与其他虚拟机或物理网卡的地址发生冲突。
- MAC地址用于局域网中的通信,而IP地址用于互联网中的通信。当数据包从局域网传输到互联网时,会进行地址转换,即从MAC地址转换为IP地址。
- 也有些网卡支持用户配置mac地址。
四、认识TCP协议和UDP协议
TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是传输层协议,它们用于在网络中传输数据。以下是TCP和UDP的一些基本特征:
TCP协议
- 传输层协议
- 有连接:TCP在传输数据之前,需要先建立一个连接。这个过程包括三次握手(SYN, SYN-ACK, ACK),以确保双方都准备好进行数据传输。
- 可靠传输:TCP通过序列号、确认应答、超时重传、流量控制和拥塞控制等机制,确保数据能够可靠地传输。
- 面向字节流:TCP将数据组织成一个连续的字节流,发送方和接收方按顺序处理字节流中的数据。
UDP协议
- 传输层协议:UDP也是传输层协议之一,用于在网络中提供不可靠的、无连接的数据传输服务。
- 无连接:UDP在传输数据之前不需要建立连接,因此速度更快,但也不提供数据传输的可靠性。
- 不可靠传输:UDP不保证数据的可靠传输,不进行序列号、确认应答、超时重传等操作,因此可能会丢失或重复数据包。
- 面向数据报:UDP将数据组织成独立的数据报,每个数据报都有头部信息,包括源端口号、目的端口号等。
可靠传输和不可靠传输是中性词。可靠传输需要做更多工作,用在比较复杂的场景。不可靠传输更简单,用在对数据可靠性要求不高的场景。只有不同,没有好坏。
五、网络字节序
我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,网络数据流同样有大端小端之分。 那么如何定义网络数据流的地址呢?
网络字节序(Network Byte Order)是计算机网络中用来表示多字节数据的一种标准方式。由于不同计算机系统可能采用不同的字节序,为了确保网络中不同主机之间的数据正确传输,TCP/IP协议定义了一种标准的网络字节序,即大端字节序(Big-Endian)。
网络字节序规定,在多字节数据中,数值的最高有效字节应位于内存地址的最低位,即所谓的“大端”。这意味着在网络传输过程中,数据的高字节应该先传输,低字节后传输。不管这台主机是大端机还是小端机,都会按照这个TCP/IP规定的网络字节序来发送/接收数据,如果当前发送主机是小端,就需要先将数据转成大端。 否则就忽略,直接发送即可。
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
#include <arpa/inet.h>
- uint32_t htonl(uint32_t hostlong);
功能:将主机字节序的32位无符号整数(hostlong)转换为网络字节序的32位无符号整数。
返回值:返回转换后的网络字节序整数。- uint16_t htons(uint16_t hostshort);
功能:将主机字节序的16位无符号整数(hostshort)转换为网络字节序的16位无符号整数。
返回值:返回转换后的网络字节序整数。- uint32_t ntohl(uint32_t netlong);
功能:将网络字节序的32位无符号整数(netlong)转换为主机字节序的32位无符号整数。
返回值:返回转换后的主机字节序整数。- uint16_t ntohs(uint16_t netshort);
功能:将网络字节序的16位无符号整数(netshort)转换为主机字节序的16位无符号整数。
返回值:返回转换后的主机字节序整数。
六、socket编程接口
6.1 socket套接字
套接字(Socket)是计算机网络编程中用于实现网络通信的一个抽象概念。它提供了一种编程接口,允许不同计算机之间通过网络进行数据传输和通信。
具体来说,套接字可以看作是通信的两个端点,一个是服务器端的套接字,另一个是客户端的套接字。通过套接字,服务器端和客户端可以相互发送和接收数据。
在网络通信中,套接字使用网络协议(如TCP/IP、UDP等)来完成数据的传输和通信。根据所使用的网络协议的不同,套接字可以分为两种类型:
- 流套接字(Stream Socket,也称为面向连接的套接字):基于TCP协议,提供可靠的、面向连接的通信。使用流套接字时,数据可以按照发送的顺序和完整性进行传输,确保数据的准确性。流套接字的通信方式类似于电话通信,需要在通信前先建立连接。
- 数据报套接字(Datagram Socket,也称为无连接的套接字):基于UDP协议,提供不可靠的、无连接的通信。使用数据报套接字时,数据以数据包的形式进行传输,不保证数据的顺序和完整性。数据报套接字适用于一次性发送不需要可靠传输的数据。
6.2 socket 常见API
创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
参数:
- domain:指定socket的地址族,如AF_INET(IPv4)、AF_INET6(IPv6)或AF_UNIX(Unix域)。
- type:指定socket的类型,如SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)或SOCK_RAW(原始socket)。
- protocol:指定socket使用的协议,如IPPROTO_TCP(TCP协议)、IPPROTO_UDP(UDP协议)或IPPROTO_ICMP(ICMP协议)。
返回值:成功时返回一个新的socket文件描述符,失败时返回-1。
绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
参数:
- socket:要绑定的socket文件描述符。
- address:指向要绑定的地址结构的指针。
- address_len:地址结构的长度。
返回值:成功时返回0,失败时返回-1。
开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
参数:
- socket:要监听的socket文件描述符。
- backlog:指定最大连接队列的长度。
返回值:成功时返回0,失败时返回-1。
接收连接请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
参数:
- socket:监听的socket文件描述符。
- address:指向接收到的地址结构的指针。
- address_len:地址结构的长度。
返回值:成功时返回一个新的socket文件描述符,表示已建立的连接,失败时返回-1。
建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
- sockfd:socket文件描述符。
- addr:指向要连接的地址结构的指针。
- addrlen:地址结构的长度。
返回值:成功时返回0,失败时返回-1。
网络编程的时候,socket是有很多不同的类型,每种类型适用于不同的网络通信场景。
理论上而言,我们应该给每种场景都设计一套编程接口,但为了简化编程接口,TCP/IP协议族使用了一个通用的地址结构struct sockaddr,它可以用于多种类型的socket。
以下是几种常见的socket类型及其应用场景的概述:
- Unix socket:
定义:Unix socket是在同一台机器上进行通信的socket,它们通常用于本地进程间通信。
用途:Unix socket可以用于文件路径、命名管道等,它们是本地通信的机制。 - 网络socket:
定义:网络socket使用IP地址和端口号进行通信,它们可以在不同的主机之间建立连接。
用途:网络socket主要用于TCP、UDP和网络层通信,是互联网上最常用的socket类型。 - 原始socket:
定义:原始socket允许应用程序直接与IP层通信,它们通常用于编写网络工具,如ping、traceroute等。
用途:原始socket可以用于执行特定的网络操作,如直接发送和接收IP包。
struct sockaddr是一个通用的地址结构,它定义了网络通信中的一些基本信息,如地址族(地址类型)、地址长度和地址值。通过这个结构,网络编程接口可以处理不同类型的socket,如Unix socket和网络socket。
尽管设计者希望使用一套接口来处理不同的socket类型,但在实际编程中,通常需要根据具体的应用场景选择合适的socket类型。例如,对于网络socket,可能需要使用struct sockaddr_in或struct sockaddr_in6来指定IP地址和端口号;而对于Unix socket,则可能需要使用struct sockaddr_un。
6.3 sockaddr结构
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及UNIX Domain Socke。然而,各种网络协议的地址格式并不相同。
- IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型,16位端口号和32位IP地址。
- IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。
- socket API可以都用struct sockaddr *类型表示,在使用的时候需要强制转化成sockaddr_in。 这样的好处是程序的通用性,可以接收IPv4,IPv6,以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数。
sockaddr结构 :
sockaddr_in 结构:
__SOCKADDR_COMMON宏如下:(##用于将两个字符串拼接起来)
in_addr结构:
6.4 简单的UDP网络程序
简单的UDP网络程序,版本1:服务端接受数据处理并返回给客户端 · 0028025 · zhougitee/Linux学习 - Gitee.com