TCP/IP 五层协议和 OSI 的七层协议对应关系如下:

传输控制协议 (Transmission Control Protocol,TCP):提供⾯向连接的、可靠的数据传输服务,数据传输的基本单位是报⽂段(segment)⽤户数据报协议 (User Datagram Protocol,UDP):提供⽆连接的、尽最⼤努⼒的数据传输服务,但不保证数据传输的可靠性,数据传输的基本单位是⽤户数据报。从上图中可以看出, TCP/IP 模型⽐ OSI 模型更加简洁,它把 应⽤层/表示层/会话层 全部整合为了 应⽤层 。
在每⼀层都⼯作着不同的设备,⽐如我们常⽤的交换机就⼯作在数据链路层的,⼀般的路由器是⼯作在⽹络层的。

在每⼀层实现的协议也各不同,即每⼀层的服务也不同,下图列出了每层主要的传输协议:

同样, TCP/IP 五层协议的通信⽅式也是对等通信:

TCP 和 UDP都是传输层协议,他们都属于TCP/IP协议族:
UDP的全称是⽤户数据报协议,在⽹络中它与TCP协议⼀样⽤于处理数据包,是⼀种⽆连接的协议。在OSI模型中,在传输层,处于IP协议的上⼀层。UDP有 不提供数据包分组、组装和不能对数据包进⾏排序的缺点,也就是说,当报⽂发送之后,是⽆法得知其是否安全完整到达的。
它的特点如下:

UDP在传输数据之前不需要先建⽴连接,远地主机的运输层在接收到UDP报⽂后,不需要确认,提供不可靠交付。总结就以下四点:
TCP 的可靠传输机制是基于连续 ARQ 协议和滑动窗⼝协议的。
TCP 协议在发送⽅维持了⼀个发送窗⼝,发送窗⼝以前的报⽂段是已经发送并确认了的报⽂段,发送窗⼝中包含了已经发送但未确认的报⽂段和允许发送但还未发送的报⽂段,发送窗⼝以后的报⽂段是缓存中还不允许发送的报⽂段。当发送⽅向接收⽅发 送报⽂时,会依次发送窗⼝内的所有报⽂段,并且设置⼀个定时器,这个定时器可以理解为是最早发送但未收到确认的报⽂段。 如果在定时器的时间内收到某⼀个报⽂段的确认回答,则滑动窗⼝,将窗⼝的⾸部向后滑动到确认报⽂段的后⼀个位置,此时如 果还有已发送但 没有确认的报⽂段,则重新设置定时器,如果没有了则关闭定时器。如果定时器超时,则重新发送所有已经发送 但还未收到确认的报⽂段,并将超时的间隔设置为以前的两倍。当发送⽅收到接收⽅的三个冗余的 确认应答后,这是⼀种指示, 说明该报⽂段以后的报⽂段很有可能发⽣丢失了,那么发送⽅会启⽤快速重传的机制,就是当前定时器结束前,发送所有的已发 送但确认的报⽂段。接收⽅使⽤的是累计确认的机制,对于所有按序到达的报⽂段,接收⽅返回⼀个报⽂段的肯定回答。如果收到了⼀个乱序的报⽂ 段,那么接⽅会直接丢弃,并返回⼀个最近的按序到达的报⽂段的肯定回答。使⽤累计确认保证了返回的确认号之前的报⽂段都 已经按序到达了,所以发送窗⼝可以移动到已确认报⽂段的后⾯。发送窗⼝的⼤⼩是变化的,它是由接收窗⼝剩余⼤⼩和⽹络中拥塞程度来决定的,TCP 就是通过控制发送窗⼝的⻓度来控制报⽂段的发送速率。
但是 TCP 协议并不完全和滑动窗⼝协议相同,因为许多的 TCP 实现会将失序的报⽂段给缓存起来,并且发⽣重传时,只会重传⼀个报⽂段,因此 TCP 协议的可靠传输机制更像是窗⼝滑动协议和选择重传协议的⼀个混合体。
由于TCP的下层⽹络(⽹络层)可能出现丢失、重复或失序的情况,TCP协议提供可靠数据传输服务。为保证数据传输的正确性,TCP会重传其认为已丢失(包括报⽂中的⽐特错误)的包。TCP使⽤两套独⽴的机制来完成重传,⼀是基于时间,⼆是基于确认信息。
TCP在发送⼀个数据之后,就开启⼀个定时器,若是在这个时间内没有收到发送数据的ACK确认报⽂,则对该报⽂进⾏重传,在达到⼀定次数还没有成功时放弃并发送⼀个复位信号。
TCP的拥塞控制机制主要是以下四种机制:
(1)慢启动(慢开始)
(2)拥塞避免

(3)快速重传
(4)快速恢复

⼀般来说,流量控制就是为了让发送⽅发送数据的速度不要太快,要让接收⽅来得及接收。TCP采⽤⼤⼩可变的滑动窗⼝进⾏流量控制,窗⼝⼤⼩的单位是字节。这⾥说的窗⼝⼤⼩其实就是每次传输的数据⼤⼩。
(1)三次握手

三次握⼿(Three-way Handshake)其实就是指建⽴⼀个TCP连接时,需要客户端和服务器总共发送3个包。进⾏三次握⼿的主要作⽤就是为了确认双⽅的接收能⼒和发送能⼒是否正常、指定⾃⼰的初始化序列号为后⾯的可靠性传送做准备。实质上其实就是连接服务器指定端⼝,建⽴TCP连接,并同步连接双⽅的序列号和确认号,交换TCP窗⼝⼤⼩信息。
刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态。
⾸部的同步位SYN=1,初始序号seq=x,SYN=1的报⽂段不能携带数据,但要消耗掉⼀个序号。
在确认报⽂段中SYN=1,ACK=1,确认号ack=x+1,初始序号seq=y
确认报⽂段ACK=1,确认号ack=y+1,序号seq=x+1(初始为seq=x,第⼆个报⽂段所以要+1),ACK报⽂段可以携带数据,不携带数据则不消耗序号。
TCP 三次握⼿的建⽴连接的过程就是相互确认初始序号的过程,告诉对⽅,什么样序号的报⽂段能够被正确接收。 第三次握⼿的作⽤是客户端对服务器端的初始序号的确认。如果只使⽤两次握⼿,那么服务器就没有办法知道⾃⼰的序号是否 已被确认。同时这样也是为了防⽌失效的请求报⽂段被服务器接收,⽽出现错误的情况。
(2)四次挥手

刚开始双⽅都处于 ESTABLISHED 状态,假如是客户端先发起关闭请求。四次挥⼿的过程如下:
即发出连接释放报⽂段(FIN=1,序号seq=u),并停⽌再发送数据,主动关闭TCP连接,进⼊FIN_WAIT1(终⽌等待1)状态,等待服务端的确认
即服务端收到连接释放报⽂段后即发出确认报⽂段(ACK=1,确认号ack=u+1,序号seq=v),服务端进⼊CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进⼊FIN_WAIT2(终⽌等待2)状态,等待服务端发出的连接释放报⽂段。
即服务端没有要向客户端发出的数据,服务端发出连接释放报⽂段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进⼊LAST_ACK(最后确认)状态,等待客户端的确认。
即客户端收到服务端的连接释放报⽂段后,对此发出确认报⽂段(ACK=1,seq=u+1,ack=w+1),客户端进⼊TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进⼊CLOSED状态。
那为什么需要四次挥⼿呢?
因为当服务端收到客户端的SYN连接请求报⽂后,可以直接发送SYN+ACK报⽂。其中ACK报⽂是⽤来应答的,SYN报⽂是⽤来同步的。但是关闭连接时,当服务端收到FIN报⽂时,很可能并不会⽴即关闭SOCKET,所以只能先回复⼀个ACK报⽂,告诉客户端,“你发的FIN报⽂我收到了”。只有等到我服务端所有的报⽂都发送完了,我才能发送FIN报⽂,因此不能⼀起发送,故需要四次挥⼿。
简单来说就是以下四步:
TCP 使⽤四次挥⼿的原因是因为 TCP 的连接是全双⼯的,所以需要双⽅分别释放到对⽅的连接,单独⼀⽅的连接释放,只代表不能再向对⽅发送数据,连接处于的是半释放的状态。
最后⼀次挥⼿中,客户端会等待⼀段时间再关闭的原因,是为了防⽌发送给服务器的确认报⽂段丢失或者出错,从⽽导致服务器 端不能正常关闭。
这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,
我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我
给定一个nxmbool数组:[[true,true,false],[false,true,true],[false,true,true]]有什么简单的方法可以返回“该列中有多少个true?”结果应该是[1,3,2] 最佳答案 使用转置得到一个数组,其中每个子数组代表一列,然后将每一列映射到其中的true数:arr.transpose.map{|subarr|subarr.count(true)}这是一个带有inject的版本,应该在1.8.6上运行,没有任何依赖:arr.transpose.map{|subarr|subarr.in
给定两个大小相等的数组,如何找到不考虑位置的匹配元素的数量?例如:[0,0,5]和[0,5,5]将返回2的匹配项,因为有一个0和一个5共同;[1,0,0,3]和[0,0,1,4]将返回3的匹配项,因为0有两场,1有一场;[1,2,2,3]和[1,2,3,4]将返回3的匹配项。我尝试了很多想法,但它们都变得相当粗糙和令人费解。我猜想有一些不错的Ruby习惯用法,或者可能是一个正则表达式,可以很好地回答这个解决方案。 最佳答案 您可以使用count完成它:a.count{|e|index=b.index(e)andb.delete_at
是否可以在不实际下载文件的情况下检查文件是否存在?我有这么大的(~40mb)文件,例如:http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm这与ruby不严格相关,但如果发件人可以设置内容长度就好了。RestClient.get"http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm",headers:{"Content-Length"=>100} 最佳答案