资讯详情

C++ 网络编程(第二篇)

文章目录

  • 前言
  • 一、OSI七层网络模型
  • 二、TCP/IP 四层模型
  • 三、协议
  • 四、常见协议
    • UDP协议
    • TCP协议
    • IP协议
    • 以太网帧协议
    • ARP协议
    • 封装
    • 分用
  • 五、UDP通信流程
  • 六、TCP的通信流程
  • 七、TCP三次握手
  • 八、滑动窗口
    • TCP 流量控制
    • TCP 拥塞控制
      • 慢慢开始避免拥塞
      • 快速重传和快速恢复
  • 九、四次挥手
  • 十、TCP如何提供可靠的数据传输?
  • 十一、TCP粘包和拆包
  • 十二、TCP的状态转换
  • 十三、半关闭
  • 十四、端口复用
  • 总结


前言

以下是网络模型:OSI七层网络模型,TCP/IP四层模型,常见协议。


一、OSI七层网络模型

OSI七层模型,又称 OSI(Open System Interconnection)参考模型,即开放式系统互联。参考模型是国际标准化组织(ISO)一般称为计算机或通信系统之间互联的标准系统 OSI 参考模型或七层模型。它是一个七层抽象的模型体,不仅包括一系列抽象术语或概念,还包括具体协议。 在这里插入图片描述

  1. 物理层:主要定义物理设备标准,如网线接口类型、光纤接口类型、各种传输介质的传输速率等。其主要功能是传输比特流(即从1、0转换为电流强度,到达目的地后转换为1、0,通常称为数模转换和模数转换)。该层的数据称为比特。
  2. 数据链路层:建立逻辑连接、硬件地址搜索、错误验证等功能。定义了如何以帧为单位传输格式化数据,以及如何控制物理介质的访问。将比特组合成字节,然后组合成帧MAC地址访问介质。
  3. 网络层:逻辑地址搜索,在不同地理位置的网络中提供连接和路径选择。Internet它的发展大大增加了从世界各地访问信息的用户数量,网络层是管理这种连接的层。
  4. 传输层:定义了传输数据的协议和端口号( WWW 端口 80 等),如:TCP(传输控制协议,传输效率低,可靠性强,用于传输要求高、数据量大的数据),UDP(用户数据报协议,与TCP 相反,传输可靠性要求低、数据量小的数据,如 QQ 这样传输聊天数据)。 从下层接收的数据主要是分段传输,到达目的地地址后重组。这层数据通常被称为段。
  5. 会话层:通过传输层(端口号:传输端口和接收端口)建立数据传输通道。主要在系统之间发起会话或接受会话请求。
  6. 表示层:数据表示、安全、压缩。主要是解释、加密和解密接收的数据、压缩和解压缩(即将计算机能够识别的东西转换为成人能够识别的东西(如图片、声音等)。
  7. 应用层:网络服务与最终用户的接口。该层为用户的应用程序提供网络服务(如电子邮件、文件传输和终端仿真)。

记忆顺口溜:物、数、网、传、会、表、应。

二、TCP/IP 四层模型

现在 Internet(因特网)使用的主流协议族是 TCP/IP 协议族是一种分层、多协议的通信系统。TCP/IP协议系统是数据链路层、网络层、传输层和应用层的四层协议系统。每层完成不同的功能,并通过多个协议实现,上层协议使用下层协议提供的服务。 TCP/IP 协议在一定程度上参考 OSI 的体系结构。OSI 该模型有物理层、数据链路层、网络层、传输层、会话层、表示层和应用层七层。但这显然有点复杂,所以在 TCP/IP 在协议中,它们被简化为四个层次。 (1)应用层、表示层、会话层提供的服务差别不大,所以在 TCP/IP 在协议中,它们被合并为应用层。 (2)因为传输层和网络层在网络协议中的地位非常重要,所以在 TCP/IP 它们被视为协议中的两个独立层次。 (3)因为数据链路层的内容与物理层相似,所以在 TCP/IP 它们被归并到网络接口层的一个层次。只有四层系统结构 TCP/IP 协议与七层系统结构相结合 OSI 比起来简单多了,就是这样,TCP/IP 在实际应用中,协议效率更高,成本更低。

  1. 应用层:应用层是 TCP/IP 协议的第一层直接为应用程序提供服务。 (1)根据自己的需要使用不同类型的应用程序,并使用邮件传输应用程序 SMTP 使用协议和万维网应用 HTTP 使用协议、远程登录服务应用TELNET 协议。 (2)应用层还能加密、解密、格式化数据。 (3)应用层可以与其他节点建立或解除联系,从而充分节约网络资源。
  2. 传输层:作为 TCP/IP 协议第二层,整个运输层 TCP/IP 协议中起到了中流砥柱的作用。且在运输层中, TCP 和 UDP 也起到了中流砥柱的作用。
  3. 网络层:网络层在 TCP/IP 协议位于第三层。 TCP/IP 网络层可以在协议中建立和终止网络连接 IP 搜索地址等功能。
  4. 网络接口层:在 TCP/IP 在协议中,网络接口层位于第四层。由于网络接口层兼并了物理层和数据链接层,网络接口层不仅是传输数据的物理媒介,而且为网络层提供了准确的线路。(网络电缆、网卡、MAC地址)

三、协议

协议,网络协议的缩写,网络协议是通信计算机双方必须共同遵守的一组协议。如何建立连接,如何相互识别等。只有遵守这一协议,计算机才能相互交流。它的三个要素是:语法、语义和顺序。

网络通信的参与者网络的目的,网络通信的参与者必须遵循相同的规则,称为协议(protocol),它最终反映了在网络上传输的数据包的格式。

协议通常分为几个层次来定义,分层定义是为了使协议的变化不影响其他层次的协议。

四、常见协议

  • 应用层常见的协议有:FTP协议(File Transfer Protocol 文件传输协议),HTTP协议(Hyper Text Transfer Protocol 超文本传输协议),NFS(Network File System 网络文件系统)。
  • 传输层的常见协议有:TCP协议(Transmission Control Protocol 传输控制协议),UDP协议(User Datagram Protocol 用户数据报告协议)。
  • 常见的网络层协议有:IP 协议(Internet Protocol 因特网互联网协议),ICMP 协议(Internet Control Message Protocol 因特网控制报文协议),IGMP 协议(Internet Group Management Protocol 因特网组管理协议)。
  • 常见的网络接口层协议有:ARP协议(Address Resolution Protocol 地址分析协议),RARP协议(Reverse Address Resolution Protocol 反向地址分析协议)。

UDP协议

  1. 源口号:发送方端口号
  2. 目的端口号:接收方端口号
  3. 长度:UDP用户数据报告的最小长度为8(只有第一部)
  4. 验证和:试验UDP用户数据报告在传输中是否有错误,如果有错误,将被丢弃

TCP协议

  1. 源口号:发送方端口号
  2. 目的端口号:接收方端口号
  3. 序列号:本报文段数据第一字节的序列号
  4. 确认序号:期望收到对方下一个报文段的第一个数据字节的序号
  5. 首部长度(数据偏移):TCP 报文段数据起始距离 TCP 报文段的起点有多远,即第一段的长度。单位:32位,即以 4 字节为计算单位
  6. 保留:占 6 目前应将位置保留为未来使用。 0
  7. 紧急 URG :此位置 1 ,表明紧急指针字段有效,告诉系统本报文段有紧急数据,应尽快传输
  8. 确认 ACK:仅当 ACK=1 确认号字段有效,TCP 根据规定,连接建立后,须在连接建立后进行 ACK 置1
  9. 推送 PSH:当两个应用程序交互时信时,有时在一端的应用进程希望在键入一个命令后立即就能够收到对方的响应。在这种情况下,TCP 就可以使用推送(push)操作,这时,发送方TCP 把 PSH 置 1,并立即创建一个报文段发送出去,接收方收到 PSH = 1 的报文段,就尽快地(即“推送”向前)交付给接收应用进程,而不再等到整个缓存都填满后再向上交付
  10. 复位 RST:用于复位相应的 TCP 连接,表示要求对方重新建立连接。
  11. 同步 SYN:仅在三次握手建立 TCP 连接时有效。当 SYN = 1 而 ACK = 0 时,表明这是一个连接请求报文段,对方若同意建立连接,则应在相应的报文段中使用 SYN = 1 和 ACK = 1。因此,SYN 置1 就表示这是一个连接请求或连接接受报文
  12. 终止 FIN:用来释放一个连接。当 FIN = 1 时,表明此报文段的发送方的数据已经发送完毕,并要 求释放运输连接
  13. 窗口:指发送本报文段的一方的接收窗口(而不是自己的发送窗口)
  14. 校验和:校验和字段检验的范围包括首部和数据两部分,在计算校验和时需要加上 12 字节的伪头部
  15. 紧急指针:仅在 URG = 1 时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就 是普通数据),即指出了紧急数据的末尾在报文中的位置,注意:即使窗口为零时也可发送紧急数 据
  16. 选项:长度可变,最长可达 40 字节,当没有使用选项时,TCP 首部长度是 20 字节 IP 协议

IP协议

  1. 版本:IP 协议的版本。通信双方使用过的 IP 协议的版本必须一致,目前最广泛使用的 IP 协议版本号为 4(即IPv4)
  2. 首部长度:单位是 32 位(4 字节)
  3. 服务类型:一般不适用,取值为 0
  4. 总长度:指首部加上数据的总长度,单位为字节
  5. 标识(identification):IP 软件在存储器中维持一个计数器,每产生一个数据报,计数器就加 1,并将此值赋给标识字段
  6. 标志(flag):目前只有两位有意义。标志字段中的最低位记为 MF。MF = 1 即表示后面“还有分片”的数据报。MF = 0 表示这已是若干数据报片中的最后一个。标志字段中间的一位记为 DF,意思是“不能分片”,只有当 DF = 0 时才允许分片
  7. 片偏移:指出较长的分组在分片后,某片在源分组中的相对位置,也就是说,相对于用户数据段的起点,该片从何处开始。片偏移以 8 字节为偏移单位。
  8. 生存时间:TTL,表明是数据报在网络中的寿命,即为“跳数限制”,由发出数据报的源点设置这个字段。路由器在转发数据之前就把 TTL 值减一,当 TTL 值减为零时,就丢弃这个数据报。默认64或者128
  9. 协议:指出此数据报携带的数据时使用何种协议,以便使目的主机的 IP 层知道应将数据部分上交给哪个处理过程,常用的 ICMP(1),IGMP(2),TCP(6),UDP(17),IPv6(41)
  10. 首部校验和:只校验数据报的首部,不包括数据部分。
  11. 源地址:发送方 IP 地址
  12. 目的地址:接收方 IP 地址

以太网帧协议

类型:0x800表示 IP、0x806表示 ARP、0x835表示 RARP

ARP协议

根据 IP 地址找到 MAC地址。

  1. 硬件类型:1 表示 MAC 地址
  2. 协议类型:0x800 表示 IP 地址
  3. 硬件地址长度:6
  4. 协议地址长度:4
  5. 操作:1 表示 ARP 请求,2 表示 ARP 应答,3 表示 RARP 请求,4 表示 RARP 应答

封装

上层协议是如何使用下层协议提供的服务的呢?其实这是通过封装(encapsulation)实现的。应用程序数据在发送到物理网络上之前,将沿着协议栈从上往下依次传递。每层协议都将在上层数据的基础上加上自己的头部信息(有时还包括尾部信息),以实现该层的功能,这个过程就称为封装。

分用

当帧到达目的主机时,将沿着协议栈自底向上依次传递。各层协议依次处理帧中本层负责的头部数据,以获取所需的信息,并最终将处理后的帧交给目标应用程序。这个过程称为分用(demultiplexing)。分用是依靠头部信息中的类型字段实现的。

五、UDP通信流程

#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
	- 参数:
	- sockfd : 通信的fd
	- buf : 要发送的数据
	- len : 发送数据的长度
	- flags : 0
	- dest_addr : 通信的另外一端的地址信息
	- addrlen : 地址的内存大小
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
	- 参数:
	- sockfd : 通信的fd
	- buf : 接收数据的数组
	- len : 数组的大小

六、TCP的通信流程

UDP:用户数据报协议,面向无连接,可以单播,多播,广播, 面向数据报,不可靠 TCP:传输控制协议,面向连接的,可靠的,基于字节流,仅支持单播传输

TCP的通信流程分为两个部分,如下所示,所需要的 socket 套接字函数参考第三篇博客。

  1. 创建一个用于监听的套接字;
    • 监听:监听有客户端的连接
    • 套接字:这个套接字其实就是一个文件描述符
  2. 将这个监听文件描述符和本地的IP和端口绑定(IP和端口就是服务器的地址信息);
    • 客户端连接服务器的时候使用的就是这个IP和端口
  3. 设置监听,监听的fd开始工作;
  4. 阻塞等待,当有客户端发起连接,解除阻塞,接受客户端的连接,会得到一个和客户端通信的套接字(fd);
  5. 通信;
    • 接收数据
    • 发送数据
  6. 通信结束,断开连接。

  1. 创建一个用于通信的套接字(fd)
  2. 连接服务器,需要指定连接的服务器的 IP 和 端口
  3. 连接成功了,客户端可以直接和服务器通信
    • 接收数据
    • 发送数据
  4. 通信结束,断开连接

七、TCP三次握手

  • TCP 是一种面向连接的单播协议,在发送数据前,通信双方必须在彼此间建立一条连接。所谓的“连接”,其实是客户端和服务器的内存里保存的一份关于对方的信息,如 IP 地址、端口号等。
  • TCP 可以看成是一种字节流,它会处理 IP 层或以下的层的丢包、重复以及错误问题。在连接的建立过程中,双方需要交换一些连接的参数。这些参数可以放在 TCP 头部。
  • TCP 提供了一种可靠、面向连接、字节流、传输层的服务,采用三次握手建立一个连接。采用四次挥手来关闭一个连接。

三次握手保证保证了双方建立了连接

这个图蛮有意思。。。

客户端发送请求报文 SYN=1,序号 = x ,表示要和服务器建立链接; 服务器收到请求后将 ACK 置为 1,表示同意链接,同时将 SYN 置为 1,序号 = y,确认序号 = x+1,回应客户端,表示同客户端建立链接; 客户端收到服务器地响应报文,将 ACK=1 ,确认序号 = y +1,发送报文并服务器建立链接。

1、第三次握手是为了防止失效的连接请求到达服务器,让服务器错误打开连接。 2、3次握手”的作用就是双方都能明确自己和对方的收、发能力是正常的。

  • 第一次握手:客户端发送网络包(不可以带数据),服务端收到了。得出结论: 客户端:自己的发送能力正常; 服务端:自己的接收能力正常、客户端的发送能力正常。
  • 第二次握手:服务端发包(不可以带数据),客户端收到了。得出结论: 客户端:自己的发送、接收能力正常,服务器的发送、接收能力正常; 服务端:自己的发送、接收能力正常、客户端的发送能力正常。
  • 第三次握手:客户端发送网络包(可以带数据),服务端收到了。得出结论: 客户端:自己的发送、接收能力正常,服务器的发送、接收能力正常; 服务端:自己的发送、接收能力正常、客户端的发送、接收能力正常。

八、滑动窗口

  • 滑动窗口(Sliding window)是一种流量控制技术。早期的网络通信中,通信双方不会考虑网络的拥挤情况直接发送数据。由于大家不知道网络拥塞状况,同时发送数据,导致中间节点阻塞掉包,谁也发不了数据,所以就有了滑动窗口机制来解决此问题。滑动窗口协议是用来改善吞吐量的一种技术,即容许发送方在接收任何应答之前传送附加的包。接收方告诉发送方在某一时刻能送多少包(称窗口尺寸)。
  • TCP 中采用滑动窗口来进行传输控制,滑动窗口的大小意味着接收方还有多大的缓冲区可以用于接收数据。发送方可以通过滑动窗口的大小来确定应该发送多少字节的数据。当滑动窗口为 0 时,发送方一般不能再发送数据报。
  • 滑动窗口是 TCP 中实现诸如 ACK 确认、流量控制、拥塞控制的承载结构,可以理解为客户端和服务器的接收、发送缓冲区。
  • 下面是流量控制和拥塞控制,我只是搬运,详情参考链接。

TCP 流量控制

主要是为了控制发送方的发送速率,保证接收方能够来得及接收,不至于接收缓冲区满了,发送方还在发送。

接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。

实际上,为了避免此问题的产生,发送端主机会时不时的发送一个叫做窗口探测的数据段此数据段仅包含一个字节来获取最新的窗口大小信息。

TCP 拥塞控制

如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量

慢开始与拥塞避免

  • 发送的最初执行慢开始,令 cwnd = 1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 …
  • 慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能性也就更高。设置一个慢开始门限 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。
  • 如果出现了超时,则令 ssthresh = cwnd / 2,然后重新执行慢开始。

快重传与快恢复

  • 在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。

  • 在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3。

  • 在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令 ssthresh = cwnd / 2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。

  • 慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。

九、四次挥手

客户端和主机都可以主动发起断链,(三次握手是客户端发起),谁先调用close(),谁就先发起断链。四次挥手同三次握手一样也是双向的。断开链接的时候是可以携带数据的。

  • 第一次挥手:客户端发送一个 FIN 段,并包含一个希望接收者看到的自己当前的序列号 K. 同时还包含一个 ACK 表示确认对方最近一次发过来的数据。
  • 服务端将 K 值加 1 作为 ACK 序号值,表明收到了上一个包。这时服务器上层的应用程序会被告知另一端发起了关闭操作,通常这将引起应用程序发起自己的关闭操作。
  • 服务端发起自己的 FIN 段,ACK=K+1, Seq=L。
  • 客户端确认。进入 TIME-WAIT 状态,等待 2 MSL(最大报文存活时间)后释放连接。发送 ACK=L+1 报文给服务器。

  • TCP连接是双向传输的对等的模式,就是说双方都可以同时向对方发送或接收数据。当有一方要关闭连接时,会发送指令告知对方,我要关闭连接了。
  • 这时对方会回一个ACK,此时一个方向的连接关闭。但是另一个方向仍然可以继续传输数据,也就是说,服务端收到客户端的 FIN 标志,知道客户端想要断开这次连接了,但是服务端还可以发送未发完的数据。
  • 客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送 FIN 连接释放报文。
  • 关闭连接时,当收到对方的 FIN 报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方是否现在关闭发送数据通道,需要上层应用来决定,因此,己方 ACK 和 FIN (比如服务器对接收的数据还没有处理完)一般都会分开发。

客户端接收到服务器端的 FIN 报文后进入此状态,此时并不是直接进入 CLOSED 状态,还需要等待一个时间计时器设置的时间 2MSL(最大报文寿命)。这么做有两个理由:

  • 确保最后一个确认报文能够到达。如果 B 没收到 A 发送来的确认报文,那么就会重新发送连接释放请求报文,A 等待一段时间就是为了处理这种情况的发生。
  • 等待一段时间是为了让本连接持续时间内所产生的所有报文都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文。

十、TCP如何提供可靠数据传输的

  • 建⽴连接(标志位):通信前确认通信实体存在。
  • 序号机制(序号、确认号):确保了数据是按序、完整到达。
  • 数据校验(校验和):CRC校验全部数据。
  • 超时重传(定时器):保证因链路故障未能到达数据能够被多次重发。
  • 窗口机制(窗⼝):提供流量控制,避免过量发送。
  • 拥塞控制:同上。

十一、TCP的粘包和拆包

如果两个报文合并成一个了一个就是粘包,如果一个报文被拆成了两个就是拆包。

  • 要发送的数据大于 TCP 发送缓冲区剩余空间大小,将会发生拆包。
  • 待发送数据大于 MSS(最大报文长度),TCP 在传输前将进行拆包。
  • 要发送的数据小于 TCP 发送缓冲区的大小,TCP 将多次写入缓冲区的数据一次发送出去,将会发生粘包。
  • 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。

  • 消息定长:发送端将每个数据包封装为固定长度(不够的可以通过补 0 填充)。
  • 设置消息边界:服务端从网络流中按消息边界分离出消息内容。在包尾增加回车换行符进行分割,例如 FTP 协议。
  • 将消息分为消息头和消息体:消息头中包含表示消息总长度(或者消息体长度)的字段。

十二、TCP的状态转换

十三、半关闭

当 TCP 链接中 A 向 B 发送 FIN 请求关闭,另一端 B 回应 ACK 之后(A 端进入 FIN_WAIT_2状态),并没有立即发送 FIN 给 A,A 方处于半连接状态(半开关),此时 A 可以接收 B 发送的数据,但是 A 已经不能再向 B 发送数据。

#include <sys/socket.h>
int shutdown(int sockfd, int how);
	sockfd: 需要关闭的socket的描述符
	how: 允许为shutdown操作选择以下几种方式:
		SHUT_RD(0): 关闭sockfd上的读功能,此选项将不允许sockfd进行读操作。
					该套接字不再接收数据,任何当前在套接字接受缓冲区的数据将被无声的丢弃掉。
		SHUT_WR(1): 关闭sockfd的写功能,此选项将不允许sockfd进行写操作。
					进程不能在对此套接字发出写操作。
		SHUT_RDWR(2):关闭sockfd的读写功能。相当于调用shutdown两次:首先是 SHUT_RD,然后 SHUT_WR。

使用 close 中止一个连接,但它只是减少描述符的引用计数,并不直接关闭连接,只有当描述符的引用 计数为 0 时才关闭连接。shutdown 不考虑描述符的引用计数,直接关闭描述符。也可选择中止一个方 向的连接,只中止读或只中止写。 注意:

  1. 如果有多个进程共享一个套接字,close 每被调用一次,计数减 1 ,直到计数为 0 时,也就是所用 进程都调用了 close,套接字将被释放。
  2. 在多进程中如果一个进程调用了 shutdown(sfd, SHUT_RDWR) 后,其它的进程将无法操作文件描述符。 但如果一个进程 close(sfd) 将不会影响到其它进程。

十四、端口复用

端口复用最常用的用途是:

  1. 防止服务器重启时之前绑定的端口还未释放
  2. 程序突然退出而系统没有释放端口
#include <sys/types.h>
#include <sys/socket.h>
//设置套接字的属性,不仅仅可以进行端口复用
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
	参数:
		sockfd: 套接字文件描述符
		level:级别,SOL_SOCKET(端口复用级别)
		optname: 选项名称
			SO_REUSEADDR
			SO_REUSEPORT
		optval: 端口复用的值
			1:可以复用
			0:不可以复用
		optlen:optval参数的大小
端口复用,设置时机在服务器绑定端口之前。

总结

牛客网C++课程笔记

标签: 3fd3y96变送器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台