当前位置: 首页 > news >正文

数字货币网站开发中国建设银行网站会员注册

数字货币网站开发,中国建设银行网站会员注册,百度高级搜索页面的网址,海口网站网站建设目录 端口号 端口号范围 pidof UDP协议 UDP协议格式 UDP特点 UDP缓冲区 UDP的注意事项 基于UDP的应用层协议 TCP协议 TCP协议格式 序号与确认序号 窗口大小 6个标记位 紧急指针 确认应答机制 连接管理机制 三次握手 四次挥手 超时重传机制 流量控制 滑动…目录 端口号 端口号范围 pidof UDP协议 UDP协议格式 UDP特点 UDP缓冲区 UDP的注意事项 基于UDP的应用层协议 TCP协议 TCP协议格式 序号与确认序号 窗口大小 6个标记位 紧急指针 确认应答机制 连接管理机制 三次握手 四次挥手 超时重传机制 流量控制 滑动窗口 拥塞控制 延迟应答 捎带应答 TCP总结 面向字节流 粘包问题 TCP异常 基于TCP的应用层协议 listen的第二个参数 在上一篇HTTP应用层协议中我们没有过多谈及传输层协议只是简单的说了一下HTTP协议在传输层中用的就是TCP应用层需要先将数据交给传输层由传输层对数据做进一步处理后再将数据继续向下交付该过程贯穿整个网络协议栈最终才能将数据发送到网络当中。 端口号 端口号port标识一个主机上进行网络通信中唯一一个进程为什么不使用pid原来也说过 原因一并不是所有的进程都要进行网络服务的使用pid后操作系统就要筛选那个进程要进行网络服务。原因二不想让进程pid和网络的端口号强耦合在一起。         当主机从网络中获取到数据后需要自底向上进行数据的交付而这个数据应该交给哪个进程就是由该进程当中的目的端口号来决定的这些原来也是说过的。 在TCP/IP协议中用“源IP地址”“源端口号”“目的IP地址”“目的端口号”“协议号”这样一个五元组来标识一个通信。         我们使用netstat就可以查看这个五元组。         如果把本机当做一个服务器来说Local Address表示的就是源IP地址和源端口号也就是服务端Foreign Address表示的就是目的IP地址和目的端口号也就是客户端而ProtoProtocol表示的就是协议类型使用的是udp还是tcpnetstat的使用之前也说过。 端口号范围 端口号的长度是16位因此端口号的范围是0 ~ 655352的16次方 0 ~ 1023知名端口号。比如HTTPFTPSSH等这些广为使用的应用层协议它们的端口号都是固定的你不能随便绑定。1024 ~ 65535操作系统动态分配的端口号。客户端程序的端口号就是由操作系统从这个范围分配的所以我一般使用的都是8080或者8081。 这些服务器的端口号一般都是固定的 ssh服务器使用22端口。ftp服务器使用21端口。telnet服务器使用23端口。http服务器使用80端口。https服务器使用443端口。 我们平时使用的shell是什么呢         我们可以看到这个进程的父进程是1其中pid、pgid、sid都是一样的这不就是我们原来说过的守护进程吗。所以这个服务不退出我们使用shell连接的时候登录成功就加载bash进程打印终端输入指令就把指令以网络的形式发送给远端的主机执行完后把结果返回。         当我们打开/etc/services文件就可以看到端口号和绑定的服务这就是一个配置文件。         所以还是那个问题端口号是确定系统中唯一一个进程的它只能被一个进程绑定但是同一个进程就可以绑定多个端口号。 当一个报文到了的时候是如何把报文交给进程的呢当获取新连接的时候一定会获取一个新的sockfd文件描述符可以把一个连接看做一个文件收到数据就是把数据放到这个文件缓冲区中因为进程和文件是通过文件描述符表建立的映射关系所以只要找到这个进程就可以对数据读写当我们读取报文的时候一定会得到端口号在内核中就有一个哈希建立端口号和进程的映射所以拿到端口就可以把数据放到特定进程的文件缓冲区中。 pidof 这个命令可以查看进程id。 当我们想要杀掉这个进程就可以使用kill命令。         那为什么要使用xargs呢当我们通过pidof查找到进程id时使用管道后这个pid就变成了进程的标准输入但是使用kill命令时需要的是命令行参数而xargs就是把标准输入转化为命令行参数。 UDP协议 首先我们要明确的就是之前我们写的不管是聊天还是计算器都是使用了操作系统提供的系统接口完成的应用层服务所以在应用层可以使用标准的服务进行二次开发或者就像计算器自己定制协议。         任何协议都要解决的就是如何将报头和有效载荷分离向上交付如何将给有效载荷添加报头向下封装。 UDP协议格式 UDP全称为用户数据报协议User Datagram Protocol。         前8字节就叫做UDP的报头数据就是有效载荷也就是从应用层来的数据如何对数据进行分离和封装呢通过上一篇HTTP介绍我们知道HTTP是通过空行让报头和有效载荷分离的。         UDP采用的就是定长报头只要拿到报文就截取8字节剩下的就是有效载荷这就是将报头和有效载荷分离向上交付时就从报头中拿到16位目的端口号拿到端口号通过哈希映射找到对应的进程再向上交付给特定进程。所以我们在写代码的时候把端口号定义为uint16_t的类型。         想要获得整个报文就拿到报头中16位UDP长度得到了UDP长度再减去8字节报头就拿到了报文所以UDP可以将报文一个一个正确地接收只要我收到了就一定是一个完整的所以UDP是面向数据报的。         这就是简单理解一下怎样封装和解包内核中的实现还是很复杂的其中传输层的缓冲区也叫做内核缓冲区。         还有就是报头中16位UDP检验和检验规则不考虑只要知道校验成功向上交付报文失败直接丢弃对端不知道不重传也不关心。 UDP特点 特点我们原来也大致说过 无连接知道目的IP和目的端口号就可以直接进行数据传输不需要建立连接。不可靠这不是一个缺点只是一个特点为了保证可靠性一定要做更多的工作使用和维护的成本就会更高所以不可靠就对应了简单没有确认机制没有重传机制更不会给应用层返回任何错误信息。所以在允许少量丢包的情况下可以使用UDP因为它简单。面向数据报UDP不管报文多长都会原样发送不拆分也不合并所以他一定是一个完整报文这就叫做面向数据报。 UDP缓冲区 原来我们就说过我们使用的sendto、recvfrom、write、read、send、recv这类IO接口都叫做拷贝函数。         如果使用他们进行网络通信他们并没有直接把数据传到对端的机器中而是放在了内核的缓冲区中同样接收的时候也没有直接从对端的主机中接收而是从内核缓冲区中拷贝到应用层的。这里的缓冲区一般都是传输层协议提供的缓冲区。         所以什么时候发发多少出错了怎么办都是OS帮我们管理的或者说是传输层帮我们管的。 其实UDP并没有真正意义上的发送缓冲区调用sendto就直接把数据交给内核内核会立即通过网络协议栈向下封装执行后续操作。         但UDP还是要有接收缓冲区的如果应用层没有调用recvfrom时数据已经发过来了如果不接收就可能导致丢包像这种的丢包UDP还是要管的。但是如果缓冲区满了那再发过来的报文就会被直接丢弃。         而且网络传输的顺序也是不确定的是因为在路由器转发的时候选择的路径不同导致的从而导致报文乱序UDP不会解决这个问题这也是可靠性问题但UDP不关心这些问题都是TCP要关心的所以到TCP的时候再详细说明。 还有一点就是UDP是全双工的对一个文件描述符既能读又能写甚至在多线程下也没问题那就是全双工的只要保证接收和发送缓冲区不冲突就可以实现全双工不使用同一个缓冲区UDP在发送的时候并不会影响接收。 UDP的注意事项 UDP协议的报头中有16位UDP长度也就是说UDP能传输的数据最大的长度就是2^16次方也就是64KB这其中也包含了8字节报头长度。但是64KB在当前的互联网环境中是非常小的如果超过64KB需要发送端手动分装多次发送接收端手动拼装。 基于UDP的应用层协议 NFS网络文件系统。TFTP简单文件传输协议。DHCP动态主机配置协议。BOOTP启动协议用于无盘设备启动。DNS域名解析协议。 到这里UDP协议就结束了它很简单可以说它什么都不管而接下来的TCP他是什么都管。 TCP协议 TCP全称为传输控制协议Transmission Control Protocol它什么都要管要对数据传输进行详细的控制TCP协议是互联网中使用最广泛的传输层协议最主要的就是他保证了可靠性。          我们知道了TCP也是全双工的UDP也是全双工的而UDP没有真正意义上的发送缓冲区但是TCP是有的。         在应用层中使用系统调用只是把数据拷贝到了传输层的缓冲区之后发送的事就是传输层的事情了怎么发发多少发生了乱序丢包都是传输层帮我们解决的所以TCP才叫做传输控制协议。         接收端也只是从接收缓冲区中读取所以发送数据的本质就是在两端的发送和接收缓冲区中来回拷贝这就有了两对儿独立的发送和接收缓冲区所以TCP才支持全双工通信。 TCP协议格式 还是那个问题TCP如何进行解包和交付         TCP报头采用的标准的20字节其中就有4位首部长度这个首部长度就是报头选项的长度4位就是0000-1111就是0-15但是它的单位是4字节理论上它可以表示0-60字节。TCP标准报头的长度就是20字节所以真实的取值应该是[20, 60]对应的4位就应该是0101-1111也就是5-15。         正确的提取方式就是提取前20字节根据这20字节提取4位首部长度再乘4字节就是报头加选项的长度如果这个数是20字节就表示没有选项已经拿到了所有首部如果不是20就用这个长度减去20就是选项的长度到这里就已经读完了报头剩下的就是有效载荷。         再拿到报头中的16位端口号通过哈希映射找到对应的进程id最后向上交付。报头中的数据是如何拿到的也不再多说还是通过结构体位段的方式实现。 序号与确认序号 如果一个客户端发送了一批请求TCP帮我们全部接收了如果发送的报文顺序和接收的报文顺序不一样怎么办呢这就是乱序的问题而乱序也是不可靠的一种体现。         所以就有了序号我们可以保证TCP中的每一个报文一定携带了完整的报头让发送的一方给每一个报文都标上序号例如每次发送1000字节一共发送1-1000、1001-2000、2001-3000三段报文序号就是1、1001、2001。接收端发送回的响应报文中的确认序号就要设置为下一个要发送的位置例如1001、2001、3001。         所以序号和确认序号就让请求和应答一一对应而确认序号表示的其实是确认序号之前的所有报文已经全部收到了发送方下次发送就要从确认序号开始发送如果只收到了3001那么就可以保证3001之前的报文接收端全收到了尽管没有收到1001和2001所以允许确认部分丢失或者不给应答。         那么为什么要有两个序号呢原因也很简单TCP也是全双工的既可以发送也可以接收如果客户端请求了服务端就要响应在响应的同时服务端也想发送请求给客户端。客户端就要接收请求也要有确认序号确认服务端发送的信息我收到了。         这样就可以解决乱序的问题保证了报文按序到达。 窗口大小 在我们上课的时候有一个老师可能讲的非常快我还没有听懂他就往后讲了换成客户端向服务端发送信息发送的太快的场景此时服务端的接收缓冲区可能已经满了新的报文可能就要被丢弃虽然TCP有相应的解决方案但是从效率上来说这就是不合理的既然接收方的缓冲区已经满了那么发送方发送的就要慢一点。         但多慢才合理呢老师讲的很慢他说的你都听懂了他还要再讲一遍这也是不合理的。         如何保证发送的一方发送的速度是的呢那么接收方就要有一定的反馈在接收方给发送方应答的时候就要同步自己的接收能力什么决定了接受能力呢那就是自己的接收缓冲区剩余的空间大小这就涉及到了流量控制报头中的窗口大小就是剩余的接收缓冲区的空间大小。         所以在接收方封装TCP报头时填入报头中的窗口大小就是自己接收缓冲区的剩余空间的大小用于响应给发送方让发送方得知接收方的接收能力。 6个标记位 我们这里说的是标准的6个标记位不同平台下可能设置的标记位不同。         每个标记位的大小就是1个比特位0代表假1代表真那为什么要设置这么多标记位呢在客户端向服务端发送报文的时候这个报文可能是一个建立连接的报文就是connect也可能是通信报文亦或者是断开连接的报文等等。但是不同类型的报文也会用不同的处理方法服务端会受到大量的不同类型的报文想要区别这些报文就需要设置这些标记位。         简单介绍一下 SYN标记该报文是一个连接请求报文。FIN 标记该报文是一个断开连接请求的报文。ACK确认应答标记位只要一个报文具有应答的特征就会被设置为1所以大部分报文都会被设置为1。RSTreset连接重置标记客户端连接建立异常让客户达重新进行三次握手。PSHpush如果对端的窗口大小长时间为0该标记位表示督促对方尽快将数据进行向上交付。URG紧急标记位让本应该按序到达的报文紧急处理某个请求标记报头中的紧急指针有效。 紧急指针 紧急指针只有报头中的URG标记位被设置才会使用紧急指针并不是我们常用的指针它标识的是紧急数据在报文中的偏移量而它标识的位置只有一个字节的数据所以并不知道这个偏移量后多少字节是紧急数据。         如果在一个机房中网络状况也不是很好其中一台机器维护了大量的连接给这个机器发送消息也不回复可能因为这台机器上层在处理大量请求缓冲区中有大量数据堆积就感觉这个机器完全卡住了但是并不确定这台机器是否存活这就可以使用URG紧急指针的方式来询问该机器现在是什么状况这个报文也不用排队所以通常是用来做机器管理的一条命令。 下面我们就来说一下TCP是如何保证可靠性的。 确认应答机制 我们一直在说这个可靠性那么为什么会出现不可靠的情况呢         如果两个人面对面聊天双方都可以清楚的听到对方在说什么出现丢包和乱序的概率很小。如果两个人聊天的距离增加那就有可能一个人说了什么另一个人没听清那这样聊天的成本就增加了可靠性就无法保证了所以就是因为距离变长了才导致不可靠。         如果在同一个机器中两个进程直接进行通信就不会谈及TCP/IP协议但是一旦涉及到了网络那就需要涉及TCP/IP协议。         在网络中长距离传输主机A向另一台主机B发送“在吗”主机A并不知道主机B有没有收到直到主机B给主机A响应对应的应答此时主机A就收到了请求并且保证了主机B一定收到了主机A发送的“在吗”同样的主机B也不能保证刚才发送的响应主机A一定收到了。         通过这个例子就印证了在互联网中不存在100%可靠的协议因为无法保证刚发出去的信息被对方收到了所以只能保证在这次发送的信息的之前的信息对方收到了这就是保证了局部上的数据100%可靠。只要我发出去的消息有匹配的应答就能够保证刚刚发出去的消息对方一定收到了。         以上我们举的例子就是TCP协议中的确认应答机制。         当把应用层中的数据拷贝到传输层的发送缓冲区中把缓冲区想象成一个字符类型的数组那就有了一个天然的序号那就是数组的下标当我们发送数组中下标为0-99的数据到对端的接收缓冲区报头中的序号可能就是0那么响应回来的报头的确认序号就是100。 连接管理机制 三次握手 三次握手和四次挥手叫做连接管理机制。         在这之前我们先来理解一下什么是连接         在生活中一个服务器一定会被大量的客户端连接所以操作系统一定要管理这些连接要管理就是先描述再组织操作系统肯定要对每个连接使用数据结构进行描述再通过某种数据结构将这些连接组织起来组织起来后操作系统就要维护这些连接维护这些连接也是要有成本的那就要花费内存和CPU的资源。         我们现在就可以介绍一下什么是三次握手了 第一次握手客户端想要向服务端请求某种资源就要先发送带有SYN标记位的连接请求报文发送完后客户端的状态变成SYN_SENT。第二次握手服务端接收报文的时间一定在客户端发送请求之后因为网络传输是有时间的接收到报文后服务端变成状态变成SYN_RCVD此时服务端也要向客户端发送建立连接的请求报文还要发送ACK应答刚才客户端发来的请求。第三次握手客户端接收到了服务端的连接请求和刚才发送给服务端连接请求的应答报文再向服务端发送它刚才发送的请求建立连接的应答报文只要发送应答报文客户端状态变为ESTABLISHED已确认建立连接的状态服务器收到应答报文后也会变成ESTABLISHED已确认建立连接的状态。         至此三次握手完成客户端和服务端都要三次握手但是并不一定要保证三次握手必须成功如果一开始服务端就关闭了也不能保证连接建立成功或者无法保证最后一次握手的报文对方收到了因为没有对应的应答。         那么为什么要进行三次握手呢 一次行不行呢如果一个客户端向服务端发送大量的连接请求报文SYN只要服务器收到了就认为连接建立好了那么服务端就会维护大量的连接维护连接是有成本的我们把这种向服务端发送大量SYN请求来消耗服务端资源的情况就叫做SYN洪水。两次行不行呢只要服务端向客户端发送了建立连接请求报文的应答报文服务器就认为连接建立好了但是此时客户端收到了SYNACK报文就直接丢弃所以服务器照样还是要维护大量连接。为什么三次行呢三次就保证先建立连接成功的一定是客户端服务端是收到之后才建立连接如果客户端再向服务端发送大量连接请求不像上面两种情况都是服务端要维护连接三次就保证服务端中有连接时客户端先要有连接所以客户端和服务端的维护连接成本是等价的。如果服务端没有收到响应此时连接建立异常的成本就会嫁接到客户端。         这也就可以验证一下TCP全双工对客户端而言先发送建立连接请求收到了应答这就保证通信信道建立成功客户端既能发也能收对于服务端也是一样的发送建立连接请求和应答收到应答这也保证通信信道建立成功服务端也既能发也能收。 如果次数再多呢四次的情况和两次差不多建立连接异常的成本又嫁接到服务端服务端又要维护大量连接所以奇数次握手成本就在客户端偶数次握手成本就在服务端。如果是五次或者七次进行这么多次的握手没有意义要以最小的成本做到让服务端和客户端维护连接的成本相同并验证一下全双工所以三次就够了。 四次挥手 三次握手是想要建立连接而四次挥手就是要断开连接了。在建立连接时一般都是客户端主动发起建立连接的请求而断开连接时哪一端都有可能发起断开连接的请求断开连接也是两个人的事情所以两个人都要断开连接。         假如就让客户端先发起断开连接的请求这就表示客户端的应用层不会再拷贝数据到发送缓冲区了发送缓冲区的数据也不会再拷贝给服务器的接收缓冲区了。 第一次挥手客户端向服务端发送带有FIN标记位的报文后状态立即变为FIN_WAIT_1。第二次挥手服务端收到了断开连接请求的报文并向客户端发送带有ACK应答标记位的报文之后状态立即变为CLOSE_WAIT客户端收到应答后状态变为FIN_WAIT_2。第三次挥手服务端要向客户端发送断开连接请求的报文之后状态变为LAST_ACK。第四次挥手客户端收到服务端断开连接请求的报文后向服务端发送应答报文之后状态变为TIME_WAIT服务端收到应答状态变为CLOSED过一段时间后客户端的状态也变为CLOSED。         还是那个问题四次挥手断开连接就一定成功吗         如果在服务器中的机器下使用netstat -ntp命令查看系统中出现了大量的CLOSE_WAIT状态的连接这就是因为服务端虽然接收到了客户端发送的请求断开连接的报文并给客户端响应但没有给客户端发送断开连接请求的报文这就是应用层代码出现了Bug原因就是服务端没有关闭对应的文件描述符才导致服务端没有向客户端发送断开连接请求的报文。         原来我写过的代码中服务端启动时的命令行参数中带有的端口号要么是8080要么是8081为什么要有两个呢一直绑定一个端口号不就行了吗当我们自己试过之后一个服务端主动断开连接后再次绑定这个端口号就会绑定失败现在我们就知道了为什么会绑定失败。         先启动服务端再使用telnet连接服务端完成三次握手此时telnet就是客户端服务端收到了客户端的应答变为ESTABLISHED状态我设置的10秒后服务端关闭所以服务端主动关闭连接关闭客户端的文件描述符底层自动触发四次挥手发送FIN报文因为服务端关闭客户端也向服务端发送FIN报文服务端给客户端应答后主动退出的一方要维持一段时间的TIME_WAIT状态该状态下连接已经结束但是IP和端口号还被占用着 此时再次启动服务端就会发现绑定错误。         不同的系统下TIME_WAIT维持的时间是不同的这个时间取决于OS自己的设置和用户设置的配置文件。         在现实中的服务器是一直运行的像过节时的某些服务器的压力会很大如果服务器挂了就需要马上再启动但此时的服务器上一定挂满了大量的TIME_WAIT连接那就需要等一会儿但是现实情况是不允许等待的在等待的时候可能就会造成巨大的经济损失所以要保证服务器即使挂了也要有立即重启的能力所以操作系统就提供了一个接口设置套接字的属性。 int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); int Socket() {// 创建socketint listensock socket(AF_INET, SOCK_STREAM, 0);if (listensock 0){logMessage(FATAL, %d:%s, errno, strerror(errno));exit(2);}int opt 1;setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, opt, sizeof(opt));logMessage(NORMAL, sock: %d, listensock);return listensock; }这就解释完什么是CLOSE_WAIT什么是TIME_WAIT。         那么为什么要存在TIME_WAIT         通常都是客户端在进行四次挥手后变成TIME_WAIT状态双方通信的数据可能还没有到达这个等待的时间通常是2MSLMax Segment Life报文最大生存时间CS传输报文一来一回的时间TIME_WAIT状态还可以保证双方通信信道上的数据在网络中尽可能的消散主要的原因还是客户端断开后可能马上再次连接历史数据这时候就会影响再一次通信。         客户端在进行四次挥手后变成TIME_WAIT状态如果第四次挥手的报文丢了客户端在一段时间内仍然能够接收服务器重发的FIN报文并对其进行响应能够较大概率保证最后一个ACK报文被服务器收到。没有TIME_WAIT状态服务端没有收到应答可能就会重传FIN客户端已经关闭所以就只能让服务器来维护这个连接。         我们可以使用cat /proc/sys/net/ipv4/tcp_fin_timeout命令来查看MSL的值。 超时重传机制 关于重传机制前面多少也提到一点这个机制可以有效的防止丢包问题。         当客户端向服务端发送报文时客户端是无法知道这个报文服务端是否收到了除非客户端收到了应答要是服务端没有收到报文也就无法应答所以要设定一个时间间隔如果在特定的时间间隔内客户端没有收到应答那就认为刚才发送的报文就超时了证明这个报文丢失了之后就要进行重传客户端重新向服务端发送报文。         或者说客户端发送的请求没有丢包服务端的应答丢包了。         如果服务端的应答一直丢包是不是服务端就会收到大量重复的请求这就是超时重传带来的问题收到重复的数据也是不可靠的一种表现所以要根据序号对数据进行去重。         设置的超时时间不能太长导致服务端和客户端通信的效率降低也不能太短可能应答报文还在路上客户端就重传了可能会频繁发送重复报文。这个时间也要随网络资源而变化时间要和网络资源成反比网络越好差时间越短长。         而Linux中的超时时间是以500ms为一个单位每次判定超时重传的时间都是500ms的整数倍。如果已经重传了一次还是没有应答那么重传时间就设置为2 * 500ms再一次就是4 * 500ms倍数以指数形式递增超时重传的次数过多TCP认为网络或者对端出现异常强制关闭连接。 流量控制 我们要知道接收端处理数据的的能力是有限的如果发送端发的太快导致接收端的缓冲区满了这个时候如果发送端继续发送就会造成丢包问题引起丢包重传等一系列处理。         因此TCP支持根据接收端的处理能力来决定发送端的发送速度。这个机制就叫做流量控制Flow Control。 接收端将自己可以接收的缓冲区大小填入TCP 报头中的 窗口大小 通过ACK报文通知发送端。窗口大小越大接收端的接收能力越强说明网络的吞吐量越高。接收端一旦发现自己的缓冲区快满了就会将窗口大小设置成一个更小的值或者是0来通知发送端。发送端接受到这个报文的窗口大小后就会减慢自己的发送速度或停止发送。         我们知道TCP是全双工的所以两个机器都可以发送和接收流量控制也可以控制两个方向。         在两端发送数据之前一定进行了三次握手这三次通信不能携带数据但是可以交换报头中对方的窗口大小这样就可以在通信之前得知对方的接收能力。         当发送端收到的接收端报文时发现这个报文的窗口大小为0就代表接收端的缓冲区已经满了之后会有两种方式可使双方继续通信。 主动询问。发送端每隔一段时间会向接收端发送报文该报文不携带数据只为询问接收端的窗口大小发送端收到应答后获取窗口大小得知是否可以通信。等待告知。接收端上层将接收缓冲区当中的数据读走后接收端向发送端发送一个TCP报文主动将自己的窗口大小告知发送端。         16为窗口大小表示缓冲区最大就是65535字节实际上在TCP报头中的选项中包含一个窗口扩大因子通过左移这个数可以扩大窗口大小虽然扩大了窗口大小也要扩大一下缓冲区这些了解一下就可以了。 关于TCP如何保证可靠性的上面已经介绍完了TCP也为传输效率做出了努力下面我们就来看看TCP是如何提高传输效率的。 滑动窗口 上面我们说过TCP保证可靠性的确认应答发送一个报文接收到后应答之后才能再发送下一个这样做是保证了可靠性但是性能较差尤其数据在网络中运输的时间过长的情况这样做就是串行的。         所以TCP使用的是一次性发送多个数据暂时不需要确认就可以马上发送下一条虽然发送一批数据但也要控制在接收端的缓冲区范围之内多次IO的时间是重叠在一起的所以性能也会提高。         基于以上原因我们可以把发送缓冲区分为一下几个区域         滑动窗口是本质就是一次性向对端推送数据的上限由对方的接收能力决定滑动窗口既想给对方推送更多数据又想保证对方能够接受所以滑动窗口是兼顾可靠性和效率的一种策略。         上面就是滑动窗口的简单理解下面我们再来完善一下这个概念。我们在刷题的时候也会看到有些题会使用滑动窗口来解决其实也就是两个指针或者两个下标现在定义为start和end。 第一个问题滑动窗口必须向右移动吗         如果滑动窗口的第一个数据发送给对端对端应答后发现窗口大小变小了这就表示对端应用层没有读取数据接受能力变小了此时滑动窗口右端end不动左端start向右移动一位确认序号表示刚才的报文已经收到了应答。 第二个问题滑动窗口可以为0吗         可以为0对端一直不读取数据接受能力一直在变小发送方收到应答滑动窗口左端一直向右直到与右端相等就为0了。更新窗口大小就是start 收到的应答报文中的确认序号end start 收到的应答报文中的窗口大小。 第三个问题如果收到的应答报文中有丢包情况怎么办         根据确认序号的概念确认序号表示它之前的报文已经全部收到了所以即使应答报文不全也没事。如果发送中有一个报文丢了那么对端应答的确认序号就是已经收到序号的下一个表示这个序号之前的报文收到了所以这个丢失的报文没有对应的应答就要进行超时重传这个数据必须被暂时保存起来所以左端的start不滑动这个丢失的报文还在滑动窗口中。 第四个问题是一直向右移动会不会越界         不存在越界的问题因为TCP的发送缓冲区是环状的使用指针加模运算就可以解决。                  所以滑动窗口更侧重于解决传输的效率问题可以一次性发送多个报文它也配合超时重传解决了可靠性问题以效率为主可靠性为辅的一个特性。         当报文丢失后会一直响应相同报文达到3次就要重传当收到了丢失的报文那就直接响应已经收到的序号的下一个这种机制就叫做“高速重发机制”也叫做“快重传”。         既然已经有了超时重传为什么还要有快重传因为快重传是有条件的就是连续收到三个相同的应答在快速发送大量报文中如果有一个报文丢失发送端会很快收到3个相同的应答此时就会触发快重传如果只收到两个那就不触发快重传时间到了触发超时重传所以这两个是协作关系。 拥塞控制 至此我们上面考虑的都是两个主机之间传输的可靠性和效率但是不要忘了我们是通过网络在传输数据的如果触发少量丢包那重传就好了如果触发大量丢包那可能就是网络的问题或者说网络拥塞了。         上面我也说了我们一直在考虑两台主机之间的通信但这个问题就不能单单只局限与两台主机网络拥塞并不是两台主机通信导致的比如如果出现网络拥塞那个整个地区或整个网络都会出现网络拥塞的问题出现大量丢包的情况主机使用的都是TCP/IP协议出现丢包就重传此时可就是网络中所有的主机都触发重传本来就已经很拥塞了那么重传就会导致拥塞问题加重。         虽然TCP已经有了滑动窗口可以高效并可靠的发送大量数据如果网络已经很拥堵了一开始发送大量数据就会出现问题所以一旦出现网络拥塞那就会马上触发TCP的拥塞控制算法。         虽然在网络中我这一个主机不能决定网络拥塞的情况但是只要知道了网络中出现了拥塞所有的主机都要进行拥塞控制算法每个主机发送的数据量都会减少这就给了网络中例如路由器、交换机等设备“喘息”的机会给他们充足的时间来处理缓存和排队的数据。         TCP就引入了慢启动机制在刚开始通信的时候发送少量报文探一探网络拥塞情况之后再加快传输速度。为了更好的处理就有了一个新的概念——拥塞窗口。         这个拥塞窗口就是一个数字表示单台主机一次向网络中发送大量数据时可能会引发网络拥塞问题的上限。所以滑动窗口不仅要考虑窗口大小还要考虑网络拥塞。 滑动窗口的大小 min(拥塞窗口大小, 窗口大小);         刚开始的拥塞窗口的大小就是1每收到一个ACK应答报文拥塞窗口加上自己的值也就是指数级增长但是也不能一直指数级增长指数增长前期增长慢后期增长快所以拥塞控制想要尽快解决网络问题也可以尽快恢复双方通信的效率。         每当通信刚刚启动或者出现了网络拥塞就会触发慢启动机制一开始都发送少量数据后面再快速增长但也不能按指数级一直增长这时就要设置一个阈值超过这个阈值后不再按照指数方式增长改用线性方式增长。        当TCP刚开始启动的时候慢启动阈值设置为对方窗口大小的最大值。当发生了网络拥塞慢启动阈值会变成当前拥塞窗口的一半同时拥塞窗口的值被重新置为1就这样一直循环。不要忘了TCP发送数据不止看拥塞窗口还要看对方的窗口大小。         所以少量丢包触发超时重传大量丢包就认为网络拥塞当TCP通信开始后网络的吞吐量会逐渐上升发生网络拥塞网络的吞吐量会骤降总结来说拥塞控制是TCP协议尽可能快的把数据传输给对方但又要避免给网络造成太大压力才出现的方案它不仅保证了效率也保证数据的可靠性。 延迟应答 当两台主机在进行网络通信时一定会带有ACK应答报文报文中也会有窗口大小来记录接收缓冲区的接收能力换句话说只要我给对端应答的窗口大小越大那么我的接收能力就越强对端也就能通过滑动窗口一次性给我发送更多数据发送的数据多了那么单次IO的效率就更高了。         那么如何让窗口大小更大呢窗口的大小主要取决于接收缓冲区的剩余大小只要应用层将数据取走接收缓冲区就会变大给对端应答的时候就有可能应答一个更大的窗口大小所以就有了延迟应答的策略窗口越大网络的吞吐量就越大传输效率就越高可以在不拥塞的情况下尽可能提高效率。         那么怎么做呢原来可能每个报文都要应答现在可能就不是。 数量控制每N个报文就应答一次。时间控制超过最大延迟时间就应答一次时间肯定不能触发对方的超时重传机制。         具体的数量和时间根据不同的操作系统也会有不同的考虑一般N都是2时间为200ms。 捎带应答 我们平常使用时不仅仅是客户端单向向服务端发送数据服务端也要给客户端发数据在服务端发送数据的时候就会携带ACK标记位。所以捎带应答也是可以提高效率的机制不只传输带有ACK标记位的报文也携带了数据。 TCP总结 到了现在我们也知道TCP不止保证了可靠性还尽可能提高了性能那我们来总结一下         保证可靠性的机制 报头中的检验和检验失败直接丢弃序号和确认序号保证数据按序到达确认应答机制超市重传机制连接管理机制流量控制拥塞控制         提高性能的机制 滑动窗口高速重发机制延迟应答捎带应答 面向字节流 现在也可以理解什么是面向字节流了TCP在传输层有接收和发送缓冲区调用系统接口把数据拷贝到缓冲区。         如果发送的字节太长会被拆分成多个数据包如果发送的字节太少就会先在缓冲区中等待等到数据多了等合适的时机再发送。         虽然说是从对方的发送缓冲区拷贝到接收缓冲区但不要忘了传输层下还有网络层数据也是从网卡驱动程序来的之后再使用系统接口就可以从缓冲区中读数据。         TCP既可以发也可以收所以它是全双工的。         发送方想要发送数据就把数据拷贝到发送缓冲区什么时候发送应用层不用管接收方想要收数据就从接收缓冲区中拷贝到上层至于这个数据是什么时候来的也不用管这就是面向字节流。         而面向数据报可以明确知道只要收了一个完整报文对端一定发了一个完整报文。而且UDP有报文长度TCP不关心报文的长度TCP只管把接收的报文拆掉报头把数据拷贝到接受缓冲区后所以这就是为什么原来我们写的应用层代码要自定义协议。         除了网络通信我们在文件操作中使用的系统调用接口也是流式的操作系统不关心你写入的是什么等到合适的时机就刷新到磁盘。 粘包问题 在前几篇协议定制时我们就提到过如果一个数据从缓冲区中读上来后可能不是我们想要一个完整的数据或者多出了一点数据所以这就是粘包问题TCP不管收到的数据是不是一个完整的要解决粘包问题就要通过特定的方式原来写过的网络计算器就解决了粘包问题。 使用定长的报文保证每次都按固定大小读取。使用变长的报文网络计算器中解析报文的时候就会加入len和\r\n标记位HTTP根据空行读取报头再根据报头当中的Content-Length属性读取正文的长度。 TCP异常 TCP异常可能发生在双方主机的通信过程中如果发生了异常那么连接怎么办呢 进程终止当进程终止了操作系统会释放进程申请的资源其中包括文件描述符所以会关闭对应的文件描述符关闭文件描述符的操作其实就是进行4次挥手发送FIN标记位的报文。机器关机在关机时如果还有连接操作系统要询问是否关闭该进程选择关闭进程就和进程终止一样触发4次挥手要保证4次挥手完成关机的时间也会长一些所以这两种连接都会正常释放。主机断电/断网被这样处理的主机是没有机会触发4次挥手的先触发的异常才识别连接异常当我们使用浏览器时突然断网网页就会识别异常直接断开连接但是对端是意识不到的对端认为连接依旧存在一直保持连接就会有问题。当服务端网络异常恢复时客户端发来请求服务端认为要先建立连接所以向客户端发送ACKRST报文。如果客户端网络异常服务器也要定期发送报文如果一直得不到应答就会关闭连接。         还有一种情况就是我们使用QQ或者微信的时候我们可能不发消息这种连接就是长连接如果一直使用那么就回保持登录状态断开连接当要发送信息的时候再重新建立连接。 基于TCP的应用层协议 HTTP超文本传输协议。HTTPS安全数据传输协议。SSH安全外壳协议。Telnet远程终端协议。FTP文件传输协议。SMTP电子邮件传输协议。 listen的第二个参数 前几篇使用TCP时使用listen的时候就有两个参数 int listen(int sockfd, int backlog);         在理解这个参数之前我们先来说一说当服务器设置成为监听状态后就可以接收连接了当三次握手后accept就可以直接获取建立好的连接但是如果一直不调用accept呢         如果来不及accept并且底层还有大量的连接那么这些连接是否都要建立成功呢我们通过代码验证一下。         原来我们设置的backlog是20现在我们直接设置成1。 const static int g_backlog 1;void Listen(int sock) {// tcp面向连接的通信前要先建立连接设置为监听状态listenif (listen(sock, g_backlog) 0){logMessage(FATAL, listen error, %d:%s, errno, strerror(errno));exit(4);}logMessage(NORMAL, listen success ...); } #include iostream #include Sock.hppusing namespace std;int main() {Sock sock;int listensock sock.Socket();sock.Bind(listensock, 8080);sock.Listen(listensock);uint16_t clientport 0;string clientip ;while (true){sleep(1);}return 0; }     我们启动服务器使用telnet连接服务器此时的连接状态就是ESTABLISHED表示建立连接成功。         如果多开几个telnet可以看到这里开启了3个telnet前两次的状态都是ESTABLISHED但是最后一次的连接状态变成了SYN_RECV查看上面的三次握手就是收到连接请求报文但是不再应答SYNACK报文过一会儿连接就会自动被释放。         所以listen接口的第二个参数的意义就是backlog1就是TCP全连接队列的长度。 全连接队列accept队列。全连接队列用于保存处于ESTABLISHED状态但没有被上层调用accept取走的连接。半连接队列。半连接队列用于保存处于SYN_SENT和SYN_RCVD状态的连接也就是还未完成三次握手的连接。         我们设置的backlog的值是1那么全连接队列的长度就是2所以在第三次连接时这个连接的状态就是SYN_RECV放到了半连接队列中过一段时间就会自动释放。         那么为什么要维护连接队列呢 如果有大量连接时服务器中处理任务的线程已经被占满了如果没有连接队列客户端发来的请求就会直接被拒绝。如果拒绝之后服务器就有线程已经完成了任务但是无法马上获取连接直到有客户端发来请求那么线程就会有一段空闲的状态那就不能充分的利用资源。         但是这个全连接队列又不能设置的太长如果太长的话尾部的连接就迟迟得不到应答而且维护连接队列也是有成本的不如把维护连接队列的资源让给服务器让服务器更好的为客户端提供服务。         而且全连接队列的长度不仅要考虑backlog的值还要考虑别的因素。
http://www.zqtcl.cn/news/628471/

相关文章:

  • 建设银行江苏官网招聘网站网站设置首页连接分类页的视频教程
  • 通过dede访问自己做的网站高端 建站
  • wordpress自定义json温岭新站seo
  • 网站开发的五个阶段wordpress安装在本地
  • 郴州网站建设有哪些sem优化
  • 在百度怎么申请自己的网站深圳网站建设迅美
  • wordpress 企业网站教程网站开发集成软件
  • 专业的西安免费做网站wordpress手机端插件
  • 口碑好网站建设优化大师win10下载
  • 网站建设普及型小程序开发平台好的有哪些
  • 网站建设与管理专业凡科做的网站好吗
  • wordpress添加变量福州seo网站管理
  • 哔哩哔哩免费网站观看网站制作合同书
  • 自流井移动网站建设建设网站的一般步骤
  • 手机导航网站模板上海低价网站建设
  • 如何开公司注册需要多少钱东莞网站推广优化网上推广公司
  • 新闻门户网站制作教育培训网站开发
  • 网站建设公司哪个好一点最近一周的热点新闻
  • 做最优秀的自己的视频网站佛山搜索引擎优化
  • 六盘水市网站建设免费封面设计在线制作生成
  • 北京快速建站制作公司wordpress wpoptions
  • iis如何建立网站门源县住房和城乡建设局网站
  • 装修素材图片都从什么网站找铁门关网站建设
  • 网站服务器环境不支持mysql数据库免费商标图案logo
  • 以什么主题做网站好wordpress怎么设置404
  • 为什么手机进网站乱码网络营销工具的特点
  • DW怎么做网站下拉菜单网站建设外包网站
  • 手机做兼职的网站设计公司注册记账代理公司
  • 如何在vs做网站建筑工程电影网
  • 甘肃网站开发网站建设自己在家接单