这部分知识我主要是给大家介绍TCP/IP协议这里的关键协议
因为不同的应用程序解决的业务场景是错综复杂的,比如遇到一些特例情况就需要自定义协议.因此,很难有一个通用的协议满足所有的业务需求.
这里说一下,业务是一个公司的命脉,在公司中业务远比技术要重要!!!
比如:点外卖
我们查看外卖列表
请求:外卖列表
你当前的位置,你的身份信息(根据你的身份信息进行食品的推荐)
响应:一个列表
列表中要有商家信息(名称,图片,距离,位置,简介)
再比如,我们要指定搜索某个店铺的名字
请求:店铺的名字/id
响应:一个列表,列表中要有店铺的信息(名字,图片,价格,简介,口味......)
比如我们使用最简单最朴素的文本格式进行传输:
请求:用户id;地址\n
响应:商家名字;商家图片地址;商家简介;商家地址\n
标签化的组织形式,使用标签来表示键值对,以及树形结构
如:
(结束标签)
这两个标签必须要成对出现,中间的内容可以使字符串也可以是数字,嵌套的放在标签的里面
如:
HTML就是XML的特殊形式
在2010年之前XML很流行,但之后发现它很啰嗦,所以就发明了json,出自js这个语言.
如:
请求:
{
userId:123456,
position:"北京市.......",
}
响应:
{
name:".......",
show:"很好吃",
}
这种格式明显比XML简单,所以json就逐渐取代了XML.
上述的XML和json是采用了文本的组织形式,有点是好观察,但是缺点是有很多多余的标签占据了过多的IO.效率上不去.
因此发明了protobuffer(谷歌)
这是一种二进制的数据传输形式.
特点:效率高,缺点:肉眼观察不了.
以及最为经典的传输数据组织形式:HTTP.(后面会讲)
传输层虽然是操作系统已经实现好了的,但程序员要网络编程调用的socket api实际上就是属于传输层的部分.还记得五元组吗?只关注起点和终点的特征在这里鲜明的体现着.
比如,mysql数据库的默认端口号就是3306.
端口号起到的作用是在一台计算机上明确的区分不同的应用程序.
端口号是传输层的概念,TCP和UDP中都包含原端口和目的端口.
但是如果我们字节写程序并且设定端口号,需要从1024开始选取.
因为0->1023的端口号,被称为"知名端口号/具名端口号",这些端口号已经分配给了一些知名并且广泛使用的应用程序了
之前我们讲过也写过,UDP的性质是:无连接、不可靠传输、面向字节报、全双工.
这张是广大教科书上的UDO报文结构:
但实际上不应该长这个样子,这样只是为了排版方便.
实际上应该是这样:
报头和载荷的关系,就是相当于车厢和车头一样:载荷中装有完整的应用层数据报.
因为这是在传输层,所以说系统会接收到应用层的数据报,然后对它进行再封装,加上UDP报头然后传给网络层.
我们之前说过:进行一次网络通信,就要涉及到一次五元组:
原IP、目的IP、原端口、目的端口、协议类型
在UDP报头中原端口和目的端口各占两个字节,所以说范围才是0->65535.
而UDP报文长度是两个字节,换算单位就是64KB(其实也就是货车的"载重量",其实很小)
在我们如今的网络通信中,一个照片就几个MB,那我们要如何解决这个问题呢?
在应用层拆分成多个报进行传输.
不用UDP改用TCP进行数据的传输.(一般都是使用第二个)
还有一个就是校验和:其目的在与检验传输数据的是否正确.
在网络上,数据传输的过程中,时常发生一种叫做"比特翻转"的错误,也就是:1->0;0->1.
为了检验这种错误,就引入了校验和进行检验:就是针对要传输的数据,进行数学计算,得到一个比较短的结果,在传输之前和传输之后分别计算一次,如果数据内容一定,那么校验和也就一定.
1)定长(不管数有多大,长度也是一定的)
2)冲突概率很小(哪怕只变动一bit位,MD5变化都会很大,解决了上述CRC的问题,准确率高)
3)不可逆(只能通过原始数据计算MD5,但很难通过MD5计算于是数据)
1)校验和
2)hash值
3)加密领域
TCP的性质是:有连接、可靠传输、面向字节流、全双工.在前面的代码编写中都曾验证过.
剩下的之后再说.
TCP是一个复杂的协议,其中有很多个机制,这里我们主要讨论其中的10个核心机制.
我们都知道TCP的性质其中有一条:可靠传输.这个可靠的意思不是一定能把消息传过去,是让你知道传没传过去.就是如果传输失败后会告知你传输失败.
而应答机制就是实现我们可靠传输的最为核心的一个机制
在这个情景中,"不可以!!!我在用"就是应答报文,也叫作ack(acknowledge的前三个字母)TCP实现可靠传输就是通过这个应答报文实现的.
如果出现以上两种情况,那么在互联网的沟通中,我们的交流就出现了语义的错乱了,此时语句就出现歧义了.
新郎要把新娘迎回就需要组织一大串的车队,而这些车队有虽然在出发是按顺序出发的,但是速度有快又慢,而且走的路段也不同,那么到达新娘家的顺序也就可能和出发时的顺序相差很大了.
所以说,网络中传输信息"后发先至"的情况是客观存在的,不可避免,在信息传输中就需要考虑如何解决这种歧义.
这里我们给传输信息取的名字1、2就被称为序号,而我们为了回答信息而取的应答1、应答2就是确认序号.
任何一条数据(包含应答报文),都是有序号的;确认序号,则是只有应答报文有.(普通报文包含应答报文无意义)
而这一条报文是不是应答报文取决于这个标志位:
而这个sck标志位为1则是应答报文,而如果为0就不是应答报文.
如果A向B发送一串信息,假如说有1000个字节,那么每个字节从1到1000进行编号,而报文中只写1就可以了,如果之后A又发送了一串1000个字节的消息,则这条详细从1001开始编号,并且报文中只需填写1001就可以了!!!
知道第一个字节序号+TCP报文长度,就可以知道每个字节的序号了!!!
如果应答报文传输的应答报文是1001,则可以说明序号<1000的数据B已经收到了,同样,A向B传输1001-2000的数据,如果B向A传输的应答报文是2001,则可以说明B已经收到了1001-2000的数据了.
A应当从1001继续发送数据.(B向A索要1001的数据)
在确认应答的时候,我们只是讨论了顺利传输的情况.但是如果丢包了呢?
发的数据丢了
返回的ack丢了
这两种情况都会被认定为丢包,如果过判断系统丢包了,就会引发TCP的重传机制.
就是重发一遍,但如果不是传输数据丢了,而是在路上还没到呢?TCP就引入了一个时间阈值,如果超过这个时间,就会重传.
咱们再来考虑一下第二个图:
1-1000这个数据主机B收到了两回,那如果这个数据是个转账数据呢?
TCP存在一个"接收缓冲区"(操作系统内核中的一段内存),当B的网卡读到数据之后,会先将数据存入"接收缓冲区",再从接收缓冲区中获取读到的数据.(就是类似"阻塞队列"之类的数据结构),然后在这个"接收缓冲区"中依照序号大小排序,并查重,删除相同内容后再进行B的读取,能够保证应用程序的read操作不读取到相同的内容.
相当于是接亲车队开到新娘家门口先进行等待,等所有人都到了后,排序回出发顺序再开进新娘家.
比如说结婚,不是举行完婚礼算是结婚,而是领证后算是结婚.
双方各自要记录双方信息,彼此之间要相互认同.
假如两个人(甲和乙)在打游戏,现在要求连麦,为了确认双方的耳机和麦克风都正常:
此时确认的内容:
甲:无
乙:已知甲的麦克风和乙的耳机都是正常的.
此时确认的内容:
甲:已知乙的麦克风和甲的耳机是正常的.由于之前甲的信息被回复,因此根据甲和乙之间默认的约定,甲也得知了甲的麦克风和乙的耳机都是正常的.
乙:已知甲的麦克风和乙的耳机都是正常的.
此时确认的内容:
甲:已知甲的麦克风和耳机都是正常的,并且乙的麦克风和耳机也都是正常的
乙:已知甲的麦克风和乙的耳机都是正常的.
此时确认的内容:
甲:已知甲的麦克风和耳机都是正常的,并且乙的麦克风和耳机也都是正常的.
乙:已知甲的麦克风和乙的耳机都是正常的,因为刚才乙的问题被回应,所以根据默认的约定,乙得知甲的耳机和乙的麦克风都是正常的.
这是双方都知道了双方的耳机和麦克风都是正常的,都具备着发送以及接收的能力,此时连接建立!!!
这里我们发现,其实图三和图二双方获取的内容都是一样的,所以可以把这两次信息交互合并为一次,那么:
双方建立对对方的认同.(保存对方的信息)
检验收信方和发信方的发送和接收能力都是否正常.
在握手的过程中,双方来协商一些重要的参数.
这里的SYN全称是synchronize,是"同步"的意思.这里指同步报文段.
就是这个,同步标志位.等于上面的"喂喂喂,听得到吗"属于同步报文段(SYN),而"听得到,那你听得到我说话吗?"既是同步报文段(SYN),也是应答报文(ACK),最后的"听得到"是应答报文(ACK).
还有
LISTEN
服务器的状态,表示服务器已经准备就绪了,随时可以建立连接,相当于手机开机,信号良好,随时有人可以打电话.
ESTABUSHED
指客户端和服务器都有了,连接建立完成,接下来就可以正常通信了,相当于电话打过去对方接通了,现在可以随时说话了!!!
接下来
这部分是描述了TCP和socket api之间的关系.(此处不关注)因为此处是C语言版本的.
这里再举个例子:(一对情侣要分手)
答案:不完全可以!!!
因为三次握手中间的两次可以合并是因为它俩是同一时机执行的,具体来说三次握手的这三次交互,是纯内核中完成(应用程序感知不到,也干预不了)服务器内核在收到syn之后,就会立即发送ack,之后也会立即发送syn.
而四次挥手则不是:
第一个fin的发起,不是由内核控制的,而是由应用程序,调用socket的close()方法(或者是进程退出),才会出发fin,而ack则是由内核控制的,在收到fin之后会立即发送ack.而第二个fin是由服务器的应用程序执行到了对应的close()方法,才会出发fin,内核和应用程序的close()之间会隔着一个时间(时间的长短由你的代码控制)
程序随着循环的结束而结束,而循环的结束随着这个break的触发而结束,而break由hasNext()判断为false就触发了,hasNext()判断为false是因为读到了EOF(文件结束标志),而EOF是因为内核收到了对方发来的fin数据报从而调用了socket.close方法.
循环结束后,就调用到了下面的那个close()方法,这时就是服务器给客户端发送了fin,就是最后的两次交互.
在上述代码中,相当于是循环一结束就立即close发起了fin,此时ack和fin之间的时间间隔就比较短.此时很有可能系统就把这两个包裹合并成了一个,但是如果间隔时间长了,比如在close之前干了别的事了,
就比如这样:
在结束之前睡了个觉,此时就无法合并成一个了.
CLOSE_WAIT(等待关闭)
出现在被动发起断开连接的一方
这里要注意:建立连接一定是客户端主动发起请求,但是断开连接可能是客户端主动发起请求,也可能是服务器主动发起请求.
这个状态就是等待关闭,也就是等待调用close()方法关闭socket.
TIME_WAIT
出现在主动发起断开连接的一方,假设是客户端主动发起断开连接,当客户端进入TIME_WAIT状态时,相当于四次挥手已经完成了,只差一步给服务器返回ack了.
此时这里的TIME_WAIT要保持一会儿当前的TCP连接不要立即就释放.(连接还没有断开)
另外,在三次握手和四次挥手的过程中也是存在超时重传的.
如果是最后一个ack丢包了,站在服务器的视角来看,服务器不知道是因为ack丢了,还是自己发的fin丢了,所以统一视为fin丢了,重新进行重传fin操作.
既然服务器可能进行fin的重传,客户端就需要能够针对这个重传的fin进行ack响应.很明显,如果客户端最后一个ack传完后就直接断开连接,这样ack就无法进行了,因此使用TIME_WAIT保留一段时间,是为了能够处理最后一个ack丢包的情况.能够在收到重传的fin后,进行ack的响应.
TIME_WAIT具体保持的时间是2MSL(指的是互联网上两个节点之间,数据传输消耗的最大时间为什么事2MSL? 其实就是传ack+回传fin的时间).
如果2MSL后,客户端还是没有接收到重传的fin,那么就认为上个ack正常到达了!!!
确认应答、超时重传、连接管理都是给TCP的可靠性提供支持的.但是引入了可靠性就会牺牲传输效率.因此UDP没有可靠性所以说它的传输效率更高.但是TCP也在效率上面做了一些补救措施,其中滑动窗口就是其中一个,它降低了确认应答、等待ack消耗的时间.
我们要注意,可靠性与效率本身是矛盾的.(我们的TCP肯定是可靠性是最优先的)
等待
数据传输(数据拷贝)
大多数的时间都是花在等上面.
这是我们确认应答的机制实现的功能,我们发现在每次发送报文的时候都需要等待ack花费了大量的时间.
本质上就是不等待的批量发送一组数据,然后使用一份时间来等待着一组数据的多个ack.
就比如说,我们现在想吃肉夹馍,煎饼.我们可以先去店铺点两份食物,然后一起等待出餐,这样比分批去点两份食物更加节省时间.
我们把不需要等待,最大能够发送的数据的最大的量称为"窗口大小".
滑动窗口不是发完四条然后再一起等待,而是向上图一样,发24条然后等待回应,收到一条回应然后窗口向后"滑动"一条.(如上面发了1001-5000的数据,然后等待接收2001的ack,在接收到2001的ack后发送5001-6000的数据报,以此类推).
也就是说,前提是接收ack比发数据报要慢.而且发数据报和接收ack是相互独立的,而且发送下一条数据报的条件是接收到窗口第一条数据报的ack.就像向前滑动了一个窗口一样,所以被称为"滑动窗口".
但如果再上述情况下,丢包了怎么办呢?
根据ack的机制,我们发现这种情况不需要过多处理!!!
因为我们发现,根据应答序号的定义,如上图,如果1001的ack丢包了,只要我们的2001ack能够顺利到达,就说明1-1000以及1001-2000的数据也就顺利到达了,也就不需要考虑之前的1001ack丢包的情况了.
如上图,1-1000的数据和1001的ack都顺利到达了,但是1001-2000的数据却丢包了,则是B给A的ack仍然是1001.(此时和A发送的数据报是什么序号的没什么关系了),大概的意思就是B主机在向A主机索要1001开头的数据报,当主机A再向主机B发送2001-3000、3001-4000、4001-5000、5001-6000、6001-7000主机B在传给主机A的ack全部都是1001,意思依然是告诉主机A 1001开头的数据报丢包了,让它重新传,此时主机A重传1001-2000的数据,主机B确认收到后才开始检验2001到7000的数据,发现也收到后,才会发送7001的ack代表之前的都收到了.
(以上重点理解)
上述重传数据的形式,起了个名字,叫做"快速重传".
这种快速重传可以说是超时重传在滑动窗口模式下的一个变形.
如果当前传输数据密集,则按照"快速重传"的方式进行重传;
如果当前传输数据稀疏,则按照之前的"超时重传"的方式进行重传.
这是一种干预发送的窗口大小的机制,窗口越大,传输效率就越高(一份时间等的ack就越多),但是窗口不能无限大.
会遇上这几种情况:
完全不等ack,会影响TCP的可靠性.
窗口太大,会消过多的系统资源.
发送速度太快,接收方处理不过来.
也就是说,发送方的处理速度,不能超过接收方的处理能力.
这里我们利用查看缓冲区的剩余大小来衡量接收方的处理能力.
这里的16位不是说窗口大小最大是64KB,在选项中有一个"窗口扩展因子"能够进一步扩大窗口大小.(就是说窗口扩展因子里写的是几,就将数据向右移几位,如64KB<<2=> 256KB)
由于接收缓冲区剩余空间都是在不断变化的,所以说,每次返回的ack的窗口大小都是在不断变化的,发送方也是在动态调整的.
当窗口大小为零的时候,发送方A就会暂时暂停发送数据,但会定期发送一个窗口探测报文这个报文不携带业务数据,只是为了触发ack探查窗口大小.
流量控制和拥塞控制共同决定发送方的窗口大小是多少.
拥塞控制描述的是传输过程中,中间节点的处理能力.
也就是说前面考虑A的发送速率只是考虑了B的处理能力,而没有考虑中间节点的的处理能力.
网络信息传输是一个"木桶效应",就是信息传输的效率取决于处理能力最差的交换机或者路由器.
那么我们在网络上通信的工程中,如何量化中间交换机或路由器的信息处理能力呢?
制作TCP的人采用了"实验"的方式来逐渐找到一个合适的窗口大小,来逐渐找到一个合适的发送速率.
拥塞窗口就是尝试以多大的窗口大小进行发送.
初始阶段由于窗口大小不是特别的大,所以以指数的形式进行增长.当增长速率达到阈值之后,指数增长就变成了线性增长(增长的前提是不丢包),接下来当传输过程中一但丢包了,说明此时发送的速率接近了极限了,此时就把窗口大小一下缩成很小(初始的窗口大小)的值(接下来继续重复刚才指数增长到线性增长的过程).
随着时间的推移逐渐到达了动态平衡的过程.
拥塞窗口和流量控制的窗口,共同决定了发送方实际的发送窗口.(拥塞控制和流量控制的较小值)
这个也是提升效率的机制,作用就是让滑动窗口的窗口大一点,这样就可以让传输的速度快一点.
在接收方能够处理得了的前提下,尽可能的把窗口大小放大一点.(类似电脑的超频)
实际上,采用延时应答的方式,就是在滑动窗口下,ack不再是每一条数据都返回了,比如如上图是隔一条数据返回一次ack.
实际上的剩余空间的处理大小,既取决于发送方的发送,又取决于接收方的处理.
这也是提高效率的一种方式.在延时应答的基础上,引入了捎带应答.
一问一答形式是服务器客户端的经典表现形式:
本来这两个报是有时间差的,但因为延时应答,让这两个报成为了同一时机,就合并了.(注意:三次握手的那个合并本身就是相同时机,三次握手是一定会合并的,而这个捎带应答是可能会合并,但和四次挥手很像)
面向字节流引入了一个麻烦事:粘包问题
因为接收缓冲区,其实就是把收到的多个数据都放在了一起,应用程序read的时候,读到哪里才算是一个完整的应用层数据报呢?
如果这里有三个TCP数据报:
aaaaaaa
bbbbbbb
ccccccc
因为TCP是按照字节流的读法,一次可以读一个字节,也可以读N个字节,所以说上述字节流,可能读到的是8个字节:aaaaaaab、bbbbbbbc、ccccccc读到的根本就不是传输者想要表达的意思.
所以说,在TCP层次,没有在socket api中告诉我们应该读几个字节全凭借程序员自己的代码.
使用换行符分割数据报:
aaaaaaa\n
bbbbbbb\n
ccccccc\n
2.约定号每个报的长度.
出现了这几种情况TCP都无法继续正常传输了!!!
程序崩溃了
电脑关机了(依照电脑的正常步骤进行的关机)
3. 电脑掉电了
4. 网线断开了
发送方发送完数据后仍然在等待ack,但等不到,进入超时重传功能,但超过超时重传指定的次数,开始尝试复原TCP连接(传输复位报文段),这个重试也会失败.只能由发送方单方面宣布断开连接.
接收方发现一段时间间没有来数据了,接收方会先等,然后接收方会周期性的给发送方发送一个消息(这个消息叫做"心跳包"),确认一下对方是否还在工作.
讲到这里,TCP协议也就告一段落了.要想深入了解TCP,请去官方参考TCP文档:RFC 9293: Transmission Control Protocol (TCP) (ietf.org)
TCP的优势在于,可靠传输,绝大多数场合都需要考虑可靠传输.
而UDP的优势在于,效率更高,如果默写场景对于性能要求更加苛刻(比如一个机房内,服务器与服务器之间的通信,网络带宽相对充裕,结构相对简单,丢包的可能性比较小)而且UDP有一个天然的优势,就是天然适应广播,就是在一个局域网中的各设备之间的信息交互.
IP地址中有一个特殊的IP,叫做"广播IP".通过UDP往广播IP上发送此时在此局域网中的所有设备都能接收到炬.(就比如说在家中的电视投屏功能,手机先要在局域网中进行广播寻找电视,需要到的电视会在手机的屏幕上出现一个电视列表,选中列表然后手机会通过局域网传输当前视频的地址)
这两个协议在日常生活中用到的很多,但并不是只有这两个协议.
就比如说,王者,使用的是TCP还是UDP呢:它即需要可靠性又需要高效率.当然有的传输层协议专门为了游戏而打造(因为TCP与UDP太极端了),所以研发了专门应对游戏环境的KCP
网络层要完成了两种功能:1.地址管理2.路径选择
涉及到的东西:TCP/IP协议栈(总而言之就是TCP+IP)
上图是IP协议的IPv4版本(v4版本的意思)
这个就是当前IP协议的版本:只有4和6两个版本(本博客只介绍IPv4)
描述了IP报头有多长(IP报头是可变长度)选项中有一个功能能够改变报头报文长度,但此处是4个字节
这里说是8位,但实际上只有4位是有效位,这四位只有一位可以是1,其余都得是0.
4位就表示了IP协议的四种状态/四种工作模式.(类似于一个怪的四种模式,开了这个就不能开那个)
在实际的工作中就可以切换IP的模式来达到最优的效果.这都是站在技术角度上的瓶颈,但一般来说性能瓶颈都是业务角度带来的.
描述了一个IP数据报的长度.(头+载荷),这个长度减去IP报头长度,就能够得到一个完整的TCP/UDP数据报长度.
确实有这个限制,但是IP自身支持对包的拆分和组装功能(就比如一张床,整件比较大运不过来,我就把它拆成零件运过来)
发送方:把100KB的数据,交给传输层进行封装,传输层交给网络层进行封装,网络层把这个100KB的数据包拆成64KB+36KB再将这两份数据交给数据链路层,由以太网封装成两个数据帧并发送.
接收方:数据链路层根据这两个数据帧进行分用等到两个IP数据包然后交给网络层,网络层针对这两个IP数据包进行解析,并把里面的载荷拼接成一个,交给传输层.
16位标识:同一个数据拆成的数据包标识是相同的.(名牌)
3位标志:结束标志.
13位片偏移:便是了多个包的先后顺序
一个数据包在网络上上能够传输的最大时间.(英文是TTL,这个时间单位不是秒,而是次数,一个包被构造出来,都有一个初始的TTL值,如32或者64,每经过一个交换机或者路由器,TTL就会-1,如果一直减到0了,就认为这个包永远也到不了,就遗弃了)
描述了当时的载荷部分内容是符合当前哪个协议的(TCP/UDP)
此处只需要针对首部进行校验,载荷部分(TCP/UDP数据报部分)已经有校验和了
然后就是IP协议中最重要的部分了:IP地址
那么,32位IP地址,世界上只能由42亿9千万的IP地址,但是只要和计算机有关系的都需要使用一个IP地址(空调,冰箱,服务器,计算机等),很显然已经不够我们现在使用的了,那么我们该如何解决这个问题呢?
在NAT的背景下,就把IP地址分成两个大类:
内网IP(私有IP) 分为这三种: '10.*' '172.16.*-172.31.*' '192.168.*'
外网IP(公网IP) 剩下的都是公网IP
NAT要求,公网IP必须是唯一的.
私网IP可以在可以在不同的局域网中重复出现.
如果某个私网中的设备想要访问公网中的设备,就需要对应的NAT设备(路由器),把IP地址进行映射,从而完成网络访问.
反之,公网的设备,无法直接访问私网的设备.不同局域网的私网的设备没法直接相互的访问.
使用cmd的ipconfig代码可以查看内网ip:
我们的笔记本电脑中的以192.168开头的都指的是局域网中的私有IP(内网IP)内网IP在不同局域网中是可以重复的,但在局域网内部是可以重复的.
要想进行主机与主机之间的通信,需要使用外网IP进行通信:
每台主机都有一个外网ip我们可以使用:iP地址查询--手机号码查询归属地 | 邮政编码查询 | iP地址归属地查询 | 身份证号码验证在线查询网 (ip138.com)
这个网址来查询我们这台主机的ip地址.
比如说我们外网IP是:114.252.000.000
我的内网IP是:192.168.1.180
现在我需要去访问目的IP为1.2.3.4的主机.
这是站在服务器的视角看到的我的主机的IP.
如果这是有复数个主机接入运营商路由器,这些主机的内网IP都会被替换成运营商路由器的外网IP114.252.000.000(当然,每个主机有自己的端口号进行区分).
此时,只要是这个电脑经过运营商路由器发给了服务器,服务器看到的源IP都是一样的.但如果是多个电脑同时访问同一个服务器,服务器的响应就会根据发来数据的源端口来进行区分.
因此,服务器能拿到的只是路由器的IP不能拿到我的内网ip,如果我的主机不主动和服务器进行联系,服务器也不知道我的端口.
NAT之间能够有效的解决IP地址不够用的情况,但是也带来了网络环境更加复杂的问题.
使用16个字节128位表示IP地址.就算给地球上每粒沙子一个外网IP都够用.
但是现在的IPv6的普及度还是极低的.
但是IPv6在我国普及度已经达到80%左右了!!!(中美贸易战是契机,虽然都已经普及了,但都还没有开启:随时严阵以待,切换IPv6)
把一个地址分成了两部分,一部分是网络号,一部分是主机号:(比如有这么一个地址)
前半部分是网络号,后半部分是主机号.
网络号是:192.168.0
主机号是:10
这是光猫:
这是路由器:
两个绿圈是两个局域网,而路由器的功能就是将两个局域网的信息可以互相沟通.
这是子网掩码,子网掩码是一个32位的整数左侧都是1右侧都是0,1的部分就描述了IP有多少位是网络号.
第一个咱们都已经见过了,就是主机号是0然后就是子网掩码了,也就是网络号了.
第二个主机地址都是1了也就是说变成了192.168.0.255,就成为了广播地址,使用UDP往这个地址发送整个局域网都能收到.
第三个我们在网络编程中也是常见的,127开头的都是本机环回.(常用测试)
另外,主机号为1,通常是"网关地址"(不绝对可以配置)网关就是局域网的出入口,也就是路由器的LAN口或者WAN口.
就是路径规划,相当于使用地图导航一下,由于网络环境比较复杂,任何一个节点都无法感知到网络的全貌的,如果进行一个比较长的的路径转发,就比较麻烦.需要"一边走一边问".
路径选择就相当于是一个你要去哪,就边走便查.(每个路由器都会保存一定的周围设备的信息(路由表),每次一个IP数据报经过路由器,都需要匹配路由表,看看接下来咋走,如果路由表上有匹配项就按照它走,如果没有就朝着那个方向走,方向错不了)
还是之前那个TTL-1,如果减到0就丢弃这份数据.根据六度空间理论32左右的TTL就可以将信息发送给世界上任何一台主机上了!!!
这一层主要考虑两个节点之间的传输(通过光纤和网线进行连接两个设备)
这里的典型协议最为知名的就是"以太网".(比如我们的网线,也被称为"以太网线") 具体就是遵守以太网的网线
载荷中包含完整的IP数据报
帧头中包含目的地址、源地址、类型
此处不是使用IP地址了,使用的是mac地址(也被称为物理地址,注意和IP地址完全独立,另一套地址体系,6个字节)
mac地址:6个字节构成,48位有效位.不是动态分配的,而是网卡出厂时就已经设置好了
有线网卡的mac地址:
无线网卡的mac地址:
既然已经有了IP地址为什么还需要mac地址呢?IP地址/mac地址可不可以自己就搞定一切呢?
答案是可以!!!主要是负责网络层和数据链路层的是两拨人,所以每一层所遵循的协议也不一样,逐步演化为了两种地址相互配合的局面.
IP地址来描述整个传输过程的起点和终点,mac地址用来描述相邻两个节点的起点和终点.
就比如:我想要从北京到上海.
通过网络层的数据规划,我们选取了从北京 -> 天津 -> 上海这条路线:
北京 -> 天津
源IP:北京
目的IP:上海
源mac:北京
目的mac:天津
中间选取了乘坐长途汽车.
天津 -> 上海
源IP:北京
目的IP:上海
源mac:天津
目的mac:上海
中间选取了乘坐飞机.
IP描述的是全程的起终点,而mac描述的是当前任务的起终点.
类型:
0800指的是普通的以太网数据帧,载荷部分就是一个完整的IP数据报.
0806载荷部分是一个ARP报文
8035载荷部分是一个RARP报文
通过RAP协议可以在交换机/路由器中建立出一个表,这相当于一个hash表,能够建立出IP和mac之间的映射关系.(相当于是知道mac就可以知道IP,知道IP也可以知道mac)
MTU是一个数据链路层的数据帧,能够承载数据的最大长度
载荷具体多长,和使用的物理介质、数据链路层使用的协议有很大的关系,比如以太网数据帧MTU 1500(字节)
正是这个MTU引起了IP协议的分包组包
DNS也被称为"域名解析系统",是当前互联网的基石.也就是网址,类似"www.baidu.com"这样的.就是IP地址太过拗口,也不好记,所以开发了DNS.
打开cmd ,ping一个网址,比如说"www.baidu.com"
就可以使域名和IP地址对应上.
之前使用的是hosts文件(需要手动设置IP地址和域名的键值对,类似hash表),现在就是使用DNS服务器(自动联网查询服务器中的键值对,这个服务器在你的电脑中)
当前我们要求,域名要保证唯一,域名分为一级域名(.com)、二级域名(baidu)、三级域名(www.)有一级不重复就可以.
手动设置DNS可以百度搜一下,一搜一大堆.