青岛企业建设网站企业,新乡网站建设制作,企业做网站 里面都写什么,国家高新技术企业有哪些知识引入#xff1a;
端口号#xff1a;
当应用层获得一个传输过来的报文时#xff0c;这时数据包需要知道#xff0c;自己应该送往哪一个应用层的服务#xff0c;这时就引入了“端口号”#xff0c;通过区分同一台主机不同应用程序的端口号#xff0c;来保证数据传输…知识引入
端口号
当应用层获得一个传输过来的报文时这时数据包需要知道自己应该送往哪一个应用层的服务这时就引入了“端口号”通过区分同一台主机不同应用程序的端口号来保证数据传输的可靠性 而我们在之前的学习知道ip地址是唯一的地址标识那么我们借助ip地址和端口号是不是就能找到网络中唯一的一台主机答案是肯定的这也就是传输层协议存在的意义之一 在TCP/IP协议中, 用 源IP, 源端口号, 目的IP, 目的端口号, 协议号 这样一个五元组来标识一个通信 如图我们通过源ip和源端口号就知道信息是从哪来的知道了目的ip和目的端口号就知道信息是去往哪里的。那么就能够实现网络中唯二的两个进程通过网络进行进程间通信了 端口号划分
0 - 1023: 知名端口号, HTTP, FTP, SSH等这些广为使用的应用层协议, 他们的端口号都是固定的.1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的. 查看网络连接状态命令
// -ntpl n-带数字 t-TCP协议 p-服务名称 l-只查看listen服务
sudo netstat -ntpl
// ss命令也可以查网络服务
ss -nltp
常用选项
n 拒绝显示别名能显示数字的全部转化成数字l 仅列出有在 Listen (监听) 的服務状态p 显示建立相关链接的程序名t (tcp)仅显示tcp相关选项u (udp)仅显示udp相关选项a (all)显示所有选项默认不显示LISTEN相关
1.UDP协议
1.1.UDP协议端格式 如图UDP协议报文前8字节为UDP协议的报头后面的即为有效载荷。 我们知道网络传输需要解决的四个问题
报文如何将报头和有效载荷分离有效载荷如何向上交付给对应应用层程序报文内容是否完整报文内容是否有误
联系UDP协议格式我们发现以上四个问题对应
解析固定报头长度来分开报头和有效载荷再通过目的端口号将有效载荷交付给应用层服务16位UDP长度对应着报文的长度当长度不符时直接丢包而16位UDP检验和检测是否在传输过程中出现0变成1、1变成0
通过UDP协议报头的设计那么我们就解决了网络传输的4个问题而在这里我们也能理解报头协议是一个结构化字段。
struct UdpHeader
{uint32_t src_port:16;uint32_t dest_port:16;uint32_t length:16;uint32_t check_sum:16;
}又因为传输层是实现在操作系统中也就是操作系统需要维护获得的UDP报文需要对UDP报文实现“先描述再组织” 如图为发送端如何形成UDP报文结构示意 首先应用层需要存在报头的结构化对象和管理报文信息的sk_buff结构化对象当用户准备通过sendto进行发送时通过bind函数将对应端口号写入进报头信息中另外有效载荷写入sk_buff指向的缓冲区中这时有tail执行有效载荷的尾_data指向有效载荷的头部_data-_tail即为有效载荷的长度传输层首先将_data向左移动8个字节给UDP的报头放入接着完善报头中的报文长度信息和check_num通过二进制位运算最终就形成了一段UDP报文 因为系统中可能存在多份待发送的UDP报文所以我们需要通过数据结构来管理这些报文。最终UDP协议就实现了 当接收端接收到UDP报文时 先分开报头和有效载荷通过分离前8个字节数据然后读取报头中的报文长度就能找到有效载荷通过对有效载荷进行二进制位运算判断前后的check_num和为0不为0就丢弃报文。获取到报文后就可以通过对应端口找到接受端的应用层服务 最终我们就完成了UDP通信 1.2.UDP的特点
UDP传输的过程类似于寄信为什么这么说呢这和UDP的特点有关 无连接: 知道对端的IP和端口号就直接进行传输, 不需要建立连接;不可靠: 没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层 返回任何错误信息; 面向数据报: 不能够灵活的控制读写数据的次数和数量;应用层交给UDP多长的报文, UDP原样发送, 既不会拆分, 也不会合并; 我们在传输报文时只要知道对端的地址就可以进行传输不需要事先通知对方才能够寄信。另外当我们写信出去后我们就无法获得这封信的状态除非他被对方收到并且返回收到信息给你。最终信的寄和读都是一次的行为跟你信里面写了多少内容无关是一个整体。 值得注意的是UDP协议允许报文最长为16位也就是64k的大小当我们传输的内容大于64k时我们就需要多次将数据分为64kb的报文接着不断传输。
2.TCP协议
TCP协议的学习任务十分繁重建议慢慢品。
2.1.TCP协议格式 如图TCP协议格式明显复杂于UDP协议接下来我们学习一下TCP协议中的重要字段。
ps如果发现这些重要字段的学习过于抽象可以大概记忆一下概念结合下面的TCP机制学习 选项 表示TCP协议标准报头携带的额外信息用于增强TCP协议的功能和灵活性大小为0-40字节与4位首部长度有关。 4位首部长度4位TCP报头长度 4位首部长度对应着0000-1111即【0,15】并且1单位对应着4个字节所以最终4为首部长度对应着【0,60】字节。 在实际应用中4位首部长度是用来分离协议报头和有效载荷的
首先当应用层获取到报文时截取前20字节的标准报头访问到4位首部地长度获取到4位首部长度如果4位首部长度为20表示选项为空那么后续数据就是有效载荷。如果大于20字节说明需要截取大于部分之后为有效载荷 6位标志位 URG: 紧急指针是否有效紧急任务报文 ACK: 确认号是否有效确认报文 PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走询问报文段 RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段 SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段 FIN: 通知对方, 本端要关闭了我们称携带FIN标识的为结束报文段 一个标志位对应着一个比特位而6位标志位是为了定义报文的类型因为服务器一般只有一个而获取服务的客户端有无数个那么服务器在通信时就需要标识不同的报文类型 16位窗口大小 窗口大小字段用于告诉接收端发送端目前还有多少数据没有收到确认还可以发送多少数据。用于实现流量控制 32位序号和32位确认序号 32位序号用来保证数据的按序到达32位确认序号用来保证发送端发送的哪些数据已经被接受到了该序号之前的数据全部收到。 16位紧急指针 表示紧急数据在有效载荷中的偏移量。但是大小只有一字节 需要结合6位标志位中的URG标志 2.2.不同类型报文和TCP通信
我们知道在TCP协议字段中存在6位的标志位而这6位的标志位对应着6种类型的报文分别在TCP通信的不同场景中发挥着各自的作用。 因为一个服务器会处理大量客户端的报文请求所以我们也需要学习不同类型的报文请求在这部分内容中我们主要讲解URG、RSH、RST标志位对应字段的报文其他标志位都是比较常见的我们会在TCP整个模块或多或少会有提及…… 处理紧急任务报文---URG
在接收端报文的维护是通过链表式的队列也就是处理任务时需要遵守先进先出原则当出现一些紧急任务时比如客户端发现传输的大文件数据传错了这时取消传输就需要将这个任务及时的关闭那么就需要将该报文插入特定的标志位当操作系统读取到报文的这个标志位时就允许这个任务插队 询问报文---RSH
我们在流量控制的学习中知道每次报文发送时会携带本端的缓冲区的窗口大小在极端场景下可能某一次回复的报文中窗口大小为0也就是当时本端的缓冲区不支持传输读取速度过慢。那么这时另一端就暂时不再发送报文 那么另一端知道什么时候再次发送报文呢所以这时发送端可以定一段时间发送一个询问报文并且带上RSH字段来告诉接收端尽快进行数据的读取 除了发送询问报文这个方式当接收端发现缓冲区的大小达到某一个标准时会给发送端发送新的窗口大小的报文。 链接重置报文---RST
网络通信过程中我们无法避免报文在网络中丢失在TCP三次握手中对于客户端建立连接是在给服务器发送ACK报文时完成对于服务端则是接收到这个ACK才完成链接。而这次ACK报文的传输出现丢包时会导致服务端无法完成链接。 另外当出现网络链接认知不一致时某一端以为完成链接了另一端却认为没有完成就需要发送RST标志的报文来实现重新链接。例如当我们使用某个APP时突然网络断开了并且客户端断链了这时网络恢复后客户端就可以通过发送RST标志的报文来实现重新链接。 2.3.确认应答机制 在网络传输中因为距离变长的问题可能会出现信息传递无法到达另一端的问题所以TCP协议为了保证发送端和接收端的高精度的信息交互实现了确认应答机制。这种应答机制双方都遵守 当发送端发送一段报文后如果接收端收到就向发送端发送ACK确认报文 这样就实现了如果接收到应答对于发送方就能保证上一条信息对方已经收到 因为网络传输存在时延并且TCP发送报文可以是并行同时发送大量报文发送端发送报文的顺序和接收端接收到报文的顺序很可能出现不同所以我们需要保证数据的按序到达 这时我们结合报文中报头字段32位序号将信息进行排序。然而对于每一个发送的报文我们需要通过确认应答机制获取对应的ACK报文这时通过报头字段的32位确认信号。 这时就衍生了一个问题为什么需要分开两个字段不能将32位序号和32位确认序号合并 为了让TCP协议的应答机制更加的合理化于是在TCP报头协议字段就分开了32位序号和32位确认序号。 32位序号保证了数据在应用层按序到达。32位确认序号既实现了确认发送端的哪些数据已经被收到又可以让双向通信时应答报文既可以作为ACK报文又可以作为数据的报文。 在这种场景中同一时间这两种序号都需要被使用完成自己各自的功能也就是不能够将他们合并成一个模块实现不同的功能 讲到这里还是有点抽象我们来看一下这个场景 假设客户端发送数字1-10服务端回应对应的单词a-j而这时服务端发送的报头信息32位序号为j表示当前发送的最后一个为j32位确认信息为11表示接收到数字1-10。注意这里我们只是大概模拟过程可能是错误的 2.4.流量控制 如图为客户端和服务器通过TCP协议进行通信的示意我们知道TCP协议在传输层分别维护了发送缓冲区和接收缓冲区而缓冲区的大小是有限的当发送方发送数据过多时会导致接收方的接受缓冲区收满了 这时我们有两种解决方法 将后续的数据包全部丢弃显然是不合理的浪费资源进行流量控制控制发送方的发送速度并且允许随时查询接受缓冲区的接收能力。 那么如何进行流量控制
我们在确认应答机制中知道TCP协议中发送方发送一条消息也是一段报文后如果接收方接收到一定要回复一条ACK报文。并且TCP协议字段中维护了一个16位窗口大小内置了当前发送方目前接收缓冲区还有多少数据没有得到确认即发送端告诉接收端我发送端发送ACK还可以接收多少数据当接收端接收到发送端的ACK报文时一方面知道上一条自己发送的信息成功被接受了另一方面获取了对端的接受缓冲区的接收能力那么接下来就能够进行发送策略修改注意以上的发送端也可以是接收端 但是我们还会存在疑惑上层我们使用TCP协议时并没有感受到流量控制和对应的策略的修改。 TCP协议是在传输层实现的而传输层协议的实现是在操作系统中的。回到上图我们知道TCP协议数据传输是以字节流的形式并且在上层我们无法控制一次发多少数据并且应用层也不关心如何发送这是因为操作系统需要在传输层的发送缓冲区控制发送策略。 也就是流量控制是通过操作系统中的TCP模块来实现的是操作系统完成的 2.5.超时重传机制
我们知道TCP通信是基于“确认应答机制”的那么发送端发送一个报文理论上就一定会获得一个ACK报文。 但是网络并不能保证百分百传输所以没获得ACK报文就对应着两种情况 发送的报文丢失了接收端没收到。接收端收到了但是发送的ACK报文丢失了 为了解决这种问题我们发出数据报文后在接收到ACK报文之前发送端需要暂时保留上一层发出的数据报文这些数据就维护在滑动窗口中 那么为了解决第一种情况我们就需要通过超时重传机制在我们发送数据报文后没有收到ACK报文时发送端会间隔一段时间重新发送数据报文。 但是如果是接收端接收到而ACK报文丢失那么这时接收端就获得了两个相同的数据报文就会造成数据冗余。那么这个问题如何解决呢这时就可以结合TCP的32位序号字段如果序号相同表示数据冗余缓冲区就丢包
那么这个超时策略怎么设置呢 最理想的情况下, 找到一个最小的时间, 保证 确认应答一定能在这个时间内返回.但是这个时间的长短, 随着网络环境的不同, 是有差异的.如果超时时间设的太长, 会影响整体的重传效率;如果超时时间设的太短, 有可能会频繁发送重复的包; TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间. Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时 时间都是500ms的整数倍.如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传.如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增.累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接. 2.6.TCP链接管理机制|三次握手|四次挥手 说到管理我们条件反射地回答对数据结构的“增删查改”在TCP通信中因为一个服务器会接收到大量用户的服务请求这就无法避免服务器对这些客户端进行管理而管理我们就需要定义结构化字段……
如图为TCP通信的流程图。当我们在上层获取到连接时先是定义结构体然后抽象成链表形式最终在服务端我们就能够实现对客户端请求进行增删查改和访问……
struct client_links
{int start_seq; // 客户端的排序字段std::string src_ip;std::string src_port;std::string dest_ip;std::string dest_port;uint64_t timestamp;int status; // 状态栏 CLOSED、CLOSED_WAIT、ESTABLISHEDstruct client_links* next;
}
因为定义了数据结构这也体现了客户端、服务端在进行TCP通信时是有成本的需要消耗空间来维护数据结构和消耗时间对数据结构进行操作。这也是TCP比UDP复杂的体现 这里我们也能看出为什么TCP建立连接需要进行三次握手 首先TCP在维护链接服务器需要开辟空间来维护客户端的请求字段所以如果只进行一次链接就可能有单机客户端恶意发送大量的SYN请求SYN Flood造成服务器过载如果只进行两次握手即是客户端发送SYN服务端发送SYNACK本质上还是客户端只发出一次报文那么还是存在这种情况…… 那么进行三次握手的原因为 以最小成本验证全双工通信防止出现接收到单机客户端的大量的异常SYN请求。客户端建立好链接之后服务器收到ACK才建立连接分配内存给结构体字段。本质上可以看做4次握手其中服务器ACK和SYN形成了捎带应答报文SYNACK因此减少了一次握手 ps三次握手是TCP建立连接时客户端与服务器之间相互发送SYN和ACK报文以确认双方都已准备好且可以接收数据的一个过程。 我们已经知道了三次握手的原因那么为什么TCP断开连接需要四次挥手呢 tcp通信是全双工的所以当我们关闭tcp通信时需要客户端、服务端之间互相发送关闭连接的请求报文来确认双方都完成了断开连接。所以当客户端发送FIN结束链接的报文相应的服务端也需要发送FIN报文又因为TCP的确认应答机制所以需要两次ACK报文最终显现出4次挥手来结束链接。 那么这时我们会有一个问题为什么不能将中间的服务端发送ACK和FIN捎带应答呢 这是因为当服务器发送ACK时表示许可客户端发送关闭请求但是可能出现服务端传给客户端的数据还没有传输完毕当数据传输完毕时服务端发送FIN报文当客户端接收到FIN报文后向服务端发送ACK报文后就关闭了链接。 在某些场景下也是可以实现3次挥手的但是主流的还是四次挥手。 ps四次挥手是TCP断开连接时客户端和服务器之间通过互相发送FIN和ACk报文来确认双方都断开连接的过程。 如图客户端发送FIN报文后处于FIN_WAIT1和FIN_WAIT2状态接收到服务端发送的FIN报文后状态变为TIME_WAIT一段时间后状态变为CLOSED表示客户端关闭链接。 FIN_WAIT状态我们可以理解这是为了等待服务端的FIN报文。那为什么出现TIME_WAIT状态这是因为网络中可能存在尚未达到客户端的报文中所以我们需要一个等待时间等待网络中的滞留报文消散 而服务端接收到客户端的FIN报文后装处于CLOSE_WAIT状态接着发送FIN给客户端这时处于LAST_ACK状态当接收到客户端的ACK报文后然后变为CLOSED表示服务端关闭链接。 2.7.滑动窗口 滑动窗口是TCP为了并发发送大量数据并且暂时不需要发送ACK报文字段的一种提高通信效率的解决方案。 滑动窗口的原理 发送前四个段的时候, 不需要等待任何ACK, 直接发送;收到第一个ACK后, 滑动窗口向后移动, 继续发送第五个段的数据;依次类推; 操作系统内核为了维护这个滑动窗口, 需要开辟发送缓冲区 来记录当前还有哪些数据没有应答; 只有确认应答过的数据, 才能从缓冲区删掉;窗口越大, 则网络的吞吐率就越高; 那什么是滑动窗口呢
我们知道tcp的数据是一段字节流char而滑动窗口本质上是在数据流中发送数据的一段范围也就是两个指针维护的一块空间。
// 本质上就可以看成双指针维护的字节流中的小数组
int left 最新返回ACK报文确认序号;
int right left 返回ACK报文的接收端缓冲区可接收数据大小;
// 滑动窗口的大小
right - left关于这两个下标的的值大家结合一下上图和TCP协议字段进行思考我就不赘述了…… 因为滑动窗口的大小跟这两个指针指向的下标有关也就是可变的实际上滑动窗口的大小由接收端的接收能力决定联系上流量控制并且在滑动窗口原理处我们知道接收到第一个ACK后才会继续移动。这时我们就能够通过这个ACK报文获取当前接收端缓冲区的接收能力进而控制滑动窗口的大小通过修改right 这时我们知道通信时滑动窗口的大小是通过ACK报文进行调整的那么刚建立连接时滑动窗口的大小是多少呢这时我们联系三次握手是不是也有报文的收发那么不就是和通信时一样不同的是通过三次握手时接收端的报文获取到窗口大小信息来确定初始滑动窗口大小……
那么滑动窗口大小的改变是不是就是在进行流量控制呢 滑动窗口中报文丢失问题
滑动窗口是发送端维护的那么在滑动并传输报文的过程中报文传输丢包了我们如何解决报文重传 通过超时重传机制或者快重传机制来实现数据重传 也就是当数据丢失时确认序号下标对应的只会是丢失数据前的数据。这时就需要进行数据重传因为此时最左段的ACK没有被发送端获得此时滑动窗口不会移动。那么假如为3000的数据缺失而2000、4000、5000未缺失这时确认序号下标对应均为2000并且与最左段丢失不同此时2000的ACK报文已获得因此滑动窗口会移动到3000处。也就是中间、右端报文缺失本质上都是最左段报文缺失 讲到这里我们也要注意我们一定需要处理报文丢失问题不然滑动窗口不会再次移动也就是处于阻塞状态这体现了TCP协议保证传输的可靠性。
2.8.拥塞控制
网络传输过程中可能会出现网络崩溃的现象而在TCP传输的角度体现在发送的报文出现大面积丢包。这时TCP协议会认为网络处于拥塞状态不同于以往丢包的处理策略进行超时重传和快重传。但是如果在网络拥塞的状态下再次进行报文传输就会加剧拥塞所以需要进行拥塞控制。 那么什么是拥塞窗口
拥塞窗口跟滑动窗口类似都是由两个指针维护的一段范围但是在TCP刚建立通信时由拥塞窗口主导来发送数据当发送端发现数据正常发送网络状态良好就会更换成滑动窗口发送的逻辑 发送开始的时候, 定义拥塞窗口大小为1; 每次收到一个ACK应答, 拥塞窗口加1; 每次发送数据包的时候, 将拥塞窗口和接收端主机反馈的窗口大小做比较, 取较小的值作为实际发送的窗口; // 拥塞窗口大小
int left 确认信号的最大值
int right left min(拥塞窗口大小, 接收端窗口大小);这里对拥塞窗口和接收端窗口大小取最小值保证接收端能够正常的接收适量的数据 拥塞控制算法
这时我们会发现如果拥塞窗口不断以指数级变大最终就会导致发送数据过多进而违背了拥塞控制的初衷。 为了不增长的那么快, 因此不能使拥塞窗口单纯的加倍.此处引入一个叫做慢启动的阈值当拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长 如图为拥塞控制算法当网络正常时拥塞窗口从1开始指数级增长至慢启动阈值接着线性增长。当网络拥塞时回到1重新开始拥塞控制算法这时更新 慢启动阈值 拥塞窗口 / 2。 而拥塞窗口大小一直变化是因为需要不断的探测网络状态来判定网络传输的上限值。因为TCP在进行传输时不仅要考虑接受缓冲区的窗口大小还要衡量当前网络许可的报文发送量因为网络中存在无数的主机。 2.9.延迟应答|捎带应答
延迟应答
在确认应答机制中我们知道回复上一条报文的ACK报文中会存储接收端的接收能力那么如果我们ACK报文发送稍微延迟一下在这段时间差中应用层可能会对缓冲区进行字节流的读取那么延迟后的ACK报文中存储的接收能力就会稍微提高这时就能够运行发送端发送更多数据增加吞吐量。 如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小.假设接收端缓冲区为1M. 一次收到了500K的数据; 如果立刻应答, 返回的窗口就是500K;但实际上可能处理端处理的速度很快, 10ms之内就把500K数据从缓冲区消费掉了;在这种情况下, 接收端处理还远没有达到自己的极限, 即使窗口再放大一些, 也能处理过来;如果接收端稍微等一会再应答, 比如等待200ms再应答, 那么这个时候返回的窗口大小就是1M; 另外 窗口越大, 网络吞吐量就越大, 传输效率就越高. 传输时保证网络不拥塞的情况下尽量提高传输 效率。同样如果所有的数据报文都采用延时应答因为滑动窗口的移动需要获得窗口左端对应的ACK报文那么这时也会导致网络传输效率的降低所以我们需要有策略的使用延迟应答。 数量限制: 每隔N个包就应答一次;时间限制: 超过最大延迟时间就应答一次; 具体的数量和超时时间, 依操作系统不同也有差异; 一般N取2, 超时时间取200ms; 捎带应答
很多情况下, 客户端服务器在应用层也是 一发一收 的. 意味着客户端给服务器说了 你好, 服务器也会给客户端回一个 Hello。又因为报文的收发需要遵守确认应答机制那么这个时候ACK就可以搭顺风车, 和服务器回应的 Hello 一起回给客户端 3.TCP的其他知识
3.1.面向字节流
我们在之前的学习中了解到传输层进行报文数据发送时是以TCP报文字段的32位序号作为发送多少数据的基准底层原理就是TCP的传输是面向字节流的。 如图我们可以把序号看成字符数组的下标这就体现了TCP面向字节的的特点并且实际上在应用层结构化数据第一步就是需要通过序列化将数据转化为字符串 而在TCP通信的本质就是发送端的传输层中的发送缓冲区将数据拷贝到接收端的接收缓冲区又因为操作系统进行数据发送时对发多少、发什么我们在上层无法得知也不关心这时就会出现可能接收端缓冲区中存在着若干个报文的数据这些数据以字符串的形式存储并且接收端的接收缓冲区也有自己的接收策略那么这时的字符串数据就会一段一段地不断被接受像水流一样被应用层读取并解析成数据 这就是TCP面向字节流的本质序号就对应着字节下标当读取数据时读到哪里就用读到哪个下标来体现 3.2.粘包问题
我们在3.1.中提及TCP是面向字节流的传输层协议这导致了应用层在读取从传输层传来的数据是一段一段的可能是残缺的报文、一个完整的报文、多个报文那么这时就会出现数据报文的“粘包问题”。
那么我们如何解决粘包问题呢 采用定长的报文格式。因为我们定义相同长度为length的报文我们只需要在应用层读取传输层字符流数据时读取短于length的字符流时先存储直至读取到length长度后作为一个报文。当读取到超过length长度的报文就先截取n个length长度的报文剩下的字符流就相当于读取短语length的字符流。在字符流中设置特殊分割字符。我们在字符流数据中读取到一些我们设置的特殊字符时就可以作为报文的分界。设置报头自描述字段。例如应用层HTTP协议中content-length字段表示有效载荷的长度那么我们也可以在处理传输层的字符流数据时也添加这些字段 一般来说第一种方法就可以解决单独解决粘包问题而第二种和第三种需要一起结合才能解决粘包问题。下面即为特殊字符和自描述字段来解决粘包问题…… std::string Encode(const std::string message){// message - len\nmessage\nstd::string len std::to_string(message.size());std::string package len line_seq message line_seq;return package;}// 循环式处理 字符型字符流数据bool Decode(std::string package, std::string *message){// len\nmessage\n - message// 但是无法保证获取的字节流是完整的报文// 1.出现一段残缺的报文 2.收到一段完整的报文和下一段报文拼接 3.恰好收到一段完整的报文auto left package.find(line_seq);if (left std::string::npos) // 未获取message中的长度return false;// 打包对应的len字段std::string len package.substr(0, left);// 总长度为len字段的长度 len字段对应的message长度 两个换行符int length_package len.size() std::stoi(len) 2 * line_seq.size();if (length_package package.size()) // package为情况1return false;// 获取message并传到外部*message package.substr(left line_seq.size(), std::stoi(len));// 删除完整报文将剩余部分返回给外部重新进行Encodepackage.erase(0, length_package);return true;}
3.3.TCP异常问题
进程终止 当服务器、客户端在正常通信时客户端突然出现异常导致进程终止。这时进程终止后服务器、客户端之间会自动进行四次挥手。 机器重启 机器重启时可以视为进程终止那么也是自动的进行四次挥手。 机器掉电/网线断开 接收端认为连接还在一旦接收端有写入操作接收端发现连接已经不在了就会进行reset. 定期询问对方是否还在使没有写入操作 . 如果对方不在 , TCP自己也内置了一个保活定时器 3.4.全连接队列 Linux内核协议栈为一个tcp连接管理三次握手期间使用两个队列: 半链接队列用来保存处于SYN_SENT和SYN_RECV状态的请求全连接队列accpetd队列用来保存处于established状态但是应用层没有调用accept取走的请求 而全连接队列的长度会受到listen的第二个参数的影响. 全连接队列满了的时候, 就无法继续让当前连接的状态进入established状态了. 值得一提的是这两个队列都是不进行实际上TCP客户端和服务端通信的 对于全连接队列我们可以把他看成去饭店吃饭当饭店繁忙TCP维护的实际通信数目过多那么为了提高效率防止其他链接到来时因繁忙就无法建立而下一刻就有其他链接进行四次挥手断开的场景我们就可以在饭店外放一些椅子供客人等待全连接队列如果有正在通信的客户端选择断开那么全连接队列的队头出队进行通信…… 这样子我们可以实现高效的处理服务器高负载的TCP通信场景另外我们也不可设置过长的全连接队列因为队列的维护也是需要资源的并且也不符合正常的生活体验。
全连接、半连接队列始终在三次挥手过程中并不进行实际的TCP客户端、服务端通信。
3.5.TCP总结
TCP传输控制协议是一个面向连接的、可靠的、基于字节流的传输层协议。而TCP协议的实现和TCP协议的模块都是围绕着可靠性、性能来进行的。 在2.TCP协议的学习中我们也深刻的体会到了传输控制协议中“控制” 体现在数据传输的控制、链接的控制、性能优化的控制……
4.TCP与UDP
4.1.UDP如何实现可靠传输
我们知道TCP是可靠的传输协议、而UDP因为其简单可靠性较弱为了让UDP协议实现可靠性传输我们可以添加
确认应答机制确认报文在传输过程中是否缺失超时重传机制确保报文都能被接收端接收到引入报文序列号确保报文发送时有序的
简而言之实现UDP的可靠传输本质上就是添加实现TCP可靠传输的模块。但是如果直接把UDP做成TCP显然是不合理的所以UDP实现可靠运输需要结合实际场景……
4.2.TCP与UDP比较
基本概念
TCP 全称是传输控制协议。 面向连接的协议即在数据传输前需要建立连接。 提供可靠的、按顺序的、无差错的数据传输。 主要用于需要高可靠性的数据传输如网页浏览、文件传输、电子邮件等。
UDP 全称是用户数据报协议。 无连接的协议即在数据传输前不需要建立连接。 提供不可靠的数据传输数据包可能会丢失、重复或乱序。 主要用于需要快速传输且对可靠性要求不高的场景如视频流、在线游戏、DNS查询等。
连接与数据传输
TCP 连接建立通过三次握手建立连接。 客户端发送SYN包。 服务器回应SYN-ACK包。 客户端回应ACK包连接建立。 数据传输数据按顺序传输使用序列号和确认机制确保数据包的到达和顺序。 连接终止通过四次挥手断开连接。 一方发送FIN包表示终止连接。 对方回应ACK包。 对方发送FIN包表示同意断开连接。 一方回应ACK包连接断开。
UDP 无连接无需建立连接直接发送数据。 数据传输数据以独立的报文形式发送不保证顺序和可靠性。
可靠性
TCP 可靠性通过确认应答ACK、重传机制、序列号等保证数据的可靠传输。 流量控制使用滑动窗口机制控制发送数据的速度防止网络拥塞。 拥塞控制使用慢启动、拥塞避免、快重传和快恢复等算法控制网络拥塞。
UDP 不可靠性没有确认应答和重传机制数据包可能会丢失或乱序。 无流量控制和拥塞控制没有内置的流量控制和拥塞控制机制。
头部开销
TCP 头部较大TCP头部通常是20字节包含源端口、目的端口、序列号、确认号、数据偏移、保留位、控制位、窗口大小、校验和、紧急指针等字段。
UDP 头部较小UDP头部通常是8字节包含源端口、目的端口、长度和校验和等字段。
使用场景
TCP 网页浏览需要可靠传输的HTTP/HTTPS协议。 文件传输如FTP、SFTP。 电子邮件如SMTP、IMAP、POP3。
UDP 视频流如视频会议、直播。 在线游戏需要快速传输的实时数据。 DNS查询快速的域名解析。 VoIP实时语音通信。