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

有免费网站推荐吗深圳高端设计公司有哪些

有免费网站推荐吗,深圳高端设计公司有哪些,小程序模板平台哪家好,子域名查询工具目录 协议前菜 端口号 ​编辑端口号范围划分 认识知名端口号(Well-Know Port Number) netstat pidof 传输层协议 UDP协议 UDP协议端格式 UDP的特点 面向数据报 UDP的缓冲区 UDP使用注意事项 基于UDP的应用层协议 TCP协议 TCP协议概念 TCP协议段格式 标志…目录 协议前菜 端口号 ​编辑端口号范围划分  认识知名端口号(Well-Know Port Number)  netstat pidof  传输层协议 UDP协议 UDP协议端格式 UDP的特点  面向数据报 UDP的缓冲区 UDP使用注意事项 基于UDP的应用层协议 TCP协议 TCP协议概念 TCP协议段格式 标志位详解 确认应答(ACK)机制 序列号 确认序号 对于为什么有确认序号和序号 超时重传机制 连接管理机制三次握手四次挥手 服务端状态转化 客户端状态转化 为什么是三次 两次呢 四次挥手 下图是TCP状态转换的一个汇总 理解TIME_WAIT状态 为什么是TIME_WAIT的时间是2MSL 解决TIME_WAIT状态引起的bind失败的方法 理解 CLOSE_WAIT 状态  滑动窗口 流量控制  拥塞控制  延迟应答  捎带应答  面向字节流 粘包问题 关于listen的第二个参数 TCP异常情况 TCP小结 基于TCP应用层协议 TCP/UDP对比 C语言总结 在这 常见八大排序 在这作者和朋友建立的社区 非科班转码社区-CSDN社区云 期待hxd的支持哈 最后是打鸡血环节 想多了都是问题做多了都是答案 最近作者和好友建立了一个公众号 公众号介绍专注于自学编程领域。由USTC、WHU、SDU等高校学生、ACM竞赛选手、CSDN万粉博主、双非上岸BAT学长原创。分享业内资讯、硬核原创资源、职业规划等和大家一起努力、成长。 二维码在文章底部哈 协议前菜 端口号 端口号(Port)标识了一个主机上进行通信的不同的应用程序 在TCP/IP协议中, 用 源IP, 源端口号, 目的IP, 目的端口号, 协议号 这样一个五元组来标识一个通信(可以通过netstat -n查看) 端口号范围划分  0 - 1023: 知名端口号, HTTP, FTP, SSH等这些广为使用的应用层协议, 他们的端口号都是固定的。 1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的。 认识知名端口号(Well-Know Port Number)  有些服务器是非常常用的, 为了使用方便, 人们约定一些常用的服务器, 都是用以下这些固定的端口号 执行下面的命令, 可以看到知名端口号 cat /etc/services 我们自己写一个程序使用端口号时, 要避开这些知名端口号 netstat netstat是一个用来查看网络状态的重要工具 语法netstat [选项] 功能查看网络状态 常用选项 n 拒绝显示别名能显示数字的全部转化成数字 l  仅列出有在 Listen (监听) 的服務状态 p 显示建立相关链接的程序名 t  (tcp)仅显示tcp相关选项 u (udp)仅显示udp相关选项 a (all)显示所有选项默认不显示LISTEN相关netstat是一个用来查看网络状态的重要工具 netstat -nltp/ntp 常用(我们经常去启动服务时想看服务是否成功启动就带l看是否LISTEN。看链接什么的就去掉l。) pidof  在查看服务器的进程id时非常方便 语法pidof [进程名] 功能通过进程名, 查看进程id 传输层协议 1. 传输层根据传来的端口号查哈希表找到对应的进程进程和端口号是通过哈希关联的 2. linux下一切皆文件我们的网络套接字在底层也是以文件的形式存在的就是文件描述符也就是进程和文件的对应关系。换言之我们只要能找到进程我们就能找到该进程对应的网络文件文件打开是有自己的内核缓冲区的所以当网络收过来的数据在内核当中他首先通过目的端口号找到目的进程然后再根据目标进程所打开的文件找到该文件打开的内核缓冲区然后把数据填到缓冲区里这样进程就可以以文件的方式读取文件了。 根据目的端口号查hash表找到进程再根据进程结合文件描述符找到文件再把数据拷贝到文件缓冲区里最后就跟读文件一样就把数据传上去了 PS1. 添加报头的本质就是拷贝对象 2. 在网络中发送数据比如send其实并不是我们发送的是我们把数据拷贝到了TCP/UDP对应的缓冲区中怎么发送/什么时候发都是OS决定的。我们讲文件的时候其实并不是我们把数据写到了磁盘/文件里而是我们把数据写到了OS的缓冲区中比如write然后OS经过自己的策略把数据刷新到外设比如磁盘上所以write其实是拷贝函数是把应用层的数据拷贝到内核再有OS定向的用自己的策略发送到磁盘上 UDP协议 UDP协议端格式 16位UDP长度, 表示整个数据报(UDP首部UDP数据)的最大长度。 如果校验和出错, 就会直接丢弃。 UDP的特点  无连接: 知道对端的IP和端口号就直接进行传输, 不需要建立连接; 不可靠: 没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层返回任何错误信息; 面向数据报: 不能够灵活的控制读写数据的次数和数量 面向数据报 应用层交给UDP多长的报文, UDP原样发送, 既不会拆分, 也不会合并 用UDP传输100个字节的数据 如果发送端调用一次sendto, 发送100个字节, 那么接收端也必须调用对应的一次recvfrom, 接收100个字节; 而不能循环调用10次recvfrom, 每次接收10个字节 UDP的缓冲区 UDP没有真正意义上的 发送缓冲区. 调用sendto会直接交给内核, 由内核将数据传给网络层协议进行后续的传输动作 UDP具有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致; 如果缓冲区满了, 再到达的UDP数据就会被丢弃 UDP的socket既能读, 也能写, 这个概念叫做 全双工。 UDP使用注意事项 我们注意到, UDP协议首部中有一个16位的最大长度. 也就是说一个UDP能传输的数据最大长度是64K(包含UDP首部)。 然而64K在当今的互联网环境下, 是一个非常小的数字。 如果我们需要传输的数据超过64K, 就需要在应用层手动的分包, 多次发送, 并在接收端手动拼装。 基于UDP的应用层协议 NFS: 网络文件系统 TFTP: 简单文件传输协议 DHCP: 动态主机配置协议 BOOTP: 启动协议(用于无盘设备启动) DNS: 域名解析协议当然, 也包括你自己写UDP程序时自定义的应用层协议; TCP协议 TCP协议概念 TCP全称为 传输控制协议(Transmission Control Protocol). 人如其名, 要对数据的传输进行一个详细的控制 没有100%可靠的协议但是有局部100%可靠的协议虽然最新的消息我们没有应答但是之前的消息我们可以做到收到了应答就说明我刚发的消息对方100%收到了。 TCP协议段格式 源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去; 32位序号/32位确认号: 后面详细讲; 4位TCP报头长度: 表示该TCP头部有多少个32位bit(有多少个4字节); 所以TCP头部最大长度是15 * 4 606位标志位       URG紧急指针是否有效。        ACK: 确认号是否有效。        PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走。        RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段。        SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段。        FIN: 通知对方, 本端要关闭了, 我们称携带FIN标识的为结束报文段。16位窗口大小: 后面再说16位校验和: 发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP首部, 也包含TCP数据部分16位紧急指针: 标识哪部分数据是紧急数据40字节头部选项: 暂时忽略标志位详解 SYN只要报文是建立链接的请求SYN需要被设置为1。 FIN该报文是一个断开链接的请求报文。 ACK确认标记位表示该报文算术对历史报文的确认一般大部分正常通信的情况下ACK都是1 PSH接受数据的低水位线比如接受缓冲区可有100byte但是要有20才去通知上层有数据了。但是如果有PSH他就会影响本地的OS让他尽快去通知上层让其读取这就是PSH状态位的含意。以前我们都是自己阻塞去调用read的但是如果我们以后不想阻塞等待就需要有人尽快的或者使用OS默认的策略来当数据有了或者超过数据低水位线了通知你而我们要是不想要OS默认而是想立即去读我们就把PSH标记位设置此时这个数据就会被我们对于的server端收到之后放到缓冲区里更多的做一个工作通知上层来可以读取PSH催促报文是Client催Server去尽快拿接受缓冲区数据的PSH是Client发送的。这是极端的情况用于理解PSH上面那个是准确的描述 URG紧急指针标记位报文在发送的时候我们是要保证按序到达的也就是说如果有一些数据优先级更高但是序号较晚无法做到数据被有限紧急处理。 RST如果客户端只要是发送了请求就认为是连接建立成功了但是如果最后发送的ACK丢失了呢此时服务端因为没有收到ACK所以认为连接依旧没有建立成功此时客户端给服务端发送消息但是服务端看到客户端消息之后发现并没有完成三次握手要遵守协议所以就意识到客户端三次握手可能失败了就会给客户端发送ACK响应响应的时候将响应报文的**RST**reset标记位置为一代表的就是告诉客户端将你的连接进行重置。tcp报文中reset标记位被置一代表需要关闭连接需要重新连接所以叫reset PS 在数据通信前就已经有了三次握手里面就会协商双方的数据通信能力所以不怕第一次发送数据时大于对方接受能力不担心他来不及接受的问题。 PS最后一次ACK三次握手时也可以携带数据 如何做到 那tcp有些场景有可能有些数据是需要server端优先去读取那么我们就需要把tcp的标志位设置为URG那么就代表该数据可以直接忽略他的序号而被上层直接进行读取处理这样的报文就叫做我们的紧急指针报文。注意设置了之后代表的是可以被优先读取 但是又有问题那么这些数据都是在接受缓冲区的你说要紧急读取那他在哪呢 这就有了我们的16位紧急指针他就代表我们的要紧急读取的数据在缓冲区特定偏移量的位置。那多大呢注意只能有一个字节也就是说紧急发送的数据只能有一个字节。那什么情况下会这样用呢 首先%99情况用不到。其次一般用紧急指针传递的数据都是有额外含义的数据并不只是数据本身。就有点像是宏一样数字对应已经预定好的信息目的就是为了未来主机或者服务出问题的时候去获取他们的状态的所以我们把URG标记所表示的数据一般称之为带外数据就是用同一个tcp链接不走你的接受缓冲区而是被上层优先处理这就是带外数据带外数据通常用来检测已经毫无反应的机器的状态已经挂掉不想/但是跑的很慢可以查到紧急指针在机房中的应用场景。 确认应答(ACK)机制 序列号 确认序号 TCP将每个字节的数据都进行了编号. 即为序列号 每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发。 确认序号比对应的序号**大1**并且代表**前面的**序号**都**确认了。序号我们来代表一个报文所对应的序号而确认序号是用来确认特定我所收到的报文之前的序号全部收到了**比如C发送了 1 2 3 5 6 S返回的确认序号就是4** 对于为什么有确认序号和序号 因为TCP协议是全双工的我在给你发消息的同时我也可以收消息。 即服务器在确认消息的时候同时发送了新的消息给应答就要填充确认序号而TCP是保证可靠性的在服务器发消息那也需要序号那就是说S要有序号和确认序号一起返回给C。即序号和确认序号是用来保证可靠性的--确认应答。序号是给对方确认的确认序号是对方给我确认的 超时重传机制 主机A发送数据给B之后, 可能因为网络拥堵等原因, 数据无法到达主机B; 如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发; 但是, 主机A未收到B发来的确认应答, 也可能是因为ACK丢失了 因此主机B会收到很多重复数据. 那么TCP协议需要能够识别出那些包是重复的包, 并且把重复的丢弃掉. 这时候我们可以利用前面提到的序列号, 就可以很容易做到去重的效果. 那么, 如果超时的时间如何确定? 最理想的情况下, 找到一个最小的时间, 保证 确认应答一定能在这个时间内返回但是这个时间的长短, 随着网络环境的不同, 是有差异的如果超时时间设的太长, 会影响整体的重传效率; 如果超时时间设的太短, 有可能会频繁发送重复的包;TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间 Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时时间都是500ms的整数倍如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接连接管理机制三次握手四次挥手 在正常情况下, TCP要经过三次握手建立连接, 四次挥手断开连接 服务端状态转化 [CLOSED - LISTEN] 服务器端调用listen后进入LISTEN状态, 等待客户端连接。 [LISTEN - SYN_RCVD] 一旦监听到连接请求(同步报文段), 就将该连接放入内核等待队列中, 并向客户端发送SYN确认报文。 [SYN_RCVD - ESTABLISHED] 服务端一旦收到客户端的确认报文, 就进入ESTABLISHED状态, 可以进行读写数据了。 [ESTABLISHED - CLOSE_WAIT] 当客户端主动关闭连接(调用close), 服务器会收到结束报文段, 服务器返回确认报文段并进入CLOSE_WAIT。 [CLOSE_WAIT - LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据); 当服务器真正调用close关闭连接时, 会向客户端发送FIN, 此时服务器进入LAST_ACK状态, 等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)。 [LAST_ACK - CLOSED] 服务器收到了对FIN的ACK, 彻底关闭连接 客户端状态转化 [CLOSED - SYN_SENT] 客户端调用connect, 发送同步报文段。 [SYN_SENT - ESTABLISHED] connect调用成功, 则进入ESTABLISHED状态, 开始读写数据。 [ESTABLISHED - FIN_WAIT_1] 客户端主动调用close时, 向服务器发送结束报文段, 同时进入FIN_WAIT_1。 [FIN_WAIT_1 - FIN_WAIT_2] 客户端收到服务器对结束报文段的确认, 则进入FIN_WAIT_2, 开始等待服务器的结束报文段。 [FIN_WAIT_2 - TIME_WAIT] 客户端收到服务器发来的结束报文段, 进入TIME_WAIT, 并发出LAST_ACK。 [TIME_WAIT - CLOSED] 客户端要等待一个2MSL(Max Segment Life, 报文最大生存时间)的时间, 才会进入CLOSED状态。 为什么是三次 并且我们发现最后一次是没有应答的即是否被对方收到是不一定的所以三次称握手不一定成功。建立连接不一定成功前两次可以保证因为有应答 比如如果是一次握手就可以那就是说只要发送SYN就可能建立连接那么这样是非常容易被攻击的。就比如一直发送SYN这种攻击方式被称为SYN洪水服务器资源被消耗导致正常的无法正常运行 两次呢 其实和一次是类似的服务端只要发出去那也就是认为连接建立好了我们依旧发送大量SYN给服务端然后服务器收到了再发一个报文没有确认里面就建立了连接同样会被消耗大量资源要维护大量连接。 前两次不行是因为每次都是让server端认为连接已经建立好了而三次就不一样了可以把确认连接的机会交给server端因为只有三次握手最后一次握手成功了之后你的server端来结束三次握手。客户端三次握手当他最后的ACK发送出去的时候他并不知道这个报文有没有给server端收到所以第三次丢失是客户端最害怕的事情但是这并不影响服务端因为服务端只要三次握手了服务端才是最后确认ACK的人。即正常的情况下客户端给服务端发送大量的SYN我给你ACK那么你就必须得给我ACK然后我才认为连接建立好了换句话说只要我们服务端和客户端建立好了连接只要服务端有了维护连接的结构体那么客户端必须维护所以你想要用一台机器给我发送大量的SYN攻击我的服务器也会把你拉下水都会建立相同连接数所以单主机攻击是非常困难的因为客户端的资源也在不断减少但服务器的资源一般更多。三次握手客户端最后一次发送ACK就认为连接建立好了就要去建立结构维护这样服务器就把最好一次报文丢失的成本嫁接给了客户端因为客户端面对的群体比较小出上一些闲置的/非法的连接并不影响但是服务端出现问题问题就大了这也是为什么是三次握手基数次 那是不是三次握手就不会收到SYN洪水呢 其实不是三次握手只不过是以最小的成本较小的握手次数来避免直接的SYN洪水攻击但至少保证不会随随便便一台机器就可以把服务端搞挂掉。 为什么三次握手第二个理由 客户端是一发一收服务端也是这样就可以用最小的成本去验证全双工。 再往上增加次数也没有意义因为不能解决任何问题安全问题并不是主要在握手时考虑而三次刚刚好以最小的成本做出了最大的意义。 四次挥手 谁发送FIN就说明谁向另一方断开连接了此时就不能向对方发送数据了但是可以确认。 上面这就是四次挥手。 至于为什么不是两次两次FIN原因就在于需要知道对方确认收到了你的消息所以有两次ACK回应所以是四次挥手。 对于服务器发送FIN和ACK为什么不一起是因为你想和我断开连接不代表我也想和你断开连接我有可能还有数据没发完。但是巧合情况刚好服务器回应你的时候也想和你断开连接那便是三次挥手了特殊情况也就是后面说的捎带应答 看图 只要是客户端发送FIN状态就立马变为FIN_WAIT_1谁发谁变服务端收到消息回应ACK一回应立马变为CLOSE_WAIT后面类推。CLOSE_WAIT就是半关闭状态说白了就是没关可以进行发消息。主动断开连接的一方最终要变为TIME_WAIT状态他的特点就是发送最后一个ACK后理论上是可以释放连接资源但是他会等待一段时间才进入CLOSED状态只有第四次发送ACK的时候才是双方真正的关闭了IO能力才是把连接真正的关掉了前面是关闭通信能力 下图是TCP状态转换的一个汇总 较粗的虚线表示服务端的状态变化情况; 较粗的实线表示客户端的状态变化情况; CLOSED是一个假想的起始点, 不是真实状态; 理解TIME_WAIT状态 TCP协议规定,主动关闭连接的一方要处于TIME_ WAIT状态,等待两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态。我们使用Ctrl-C终止了server, 所以server是主动关闭连接的一方, 在TIME_WAIT期间仍然不能再次监听同样的server端口。MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同, 在Centos7上默认配置的值是60s。可以通过 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看msl的值。规定TIME_WAIT的时间请读者参考UNP 2.7节。为什么是TIME_WAIT的时间是2MSL MSL是TCP报文的最大生存时间, 因此TIME_WAIT持续存在2MSL的话。就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的)。同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN. 这时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以重发LAST_ACK)。解决TIME_WAIT状态引起的bind失败的方法 在server的TCP连接没有完全断开之前不允许重新监听, 某些情况下可能是不合理的 服务器需要处理非常大量的客户端的连接(每个连接的生存时间可能很短, 但是每秒都有很大数量的客户端来请求). 这个时候如果由服务器端主动关闭连接(比如某些客户端不活跃, 就需要被服务器端主动清理掉), 就会产生大量TIME_WAIT连接. 由于我们的请求量很大, 就可能导致TIME_WAIT的连接数很多, 每个连接都会占用一个通信五元组(源ip,源端口, 目的ip, 目的端口, 协议). 其中服务器的ip和端口和协议是固定的. 如果新来的客户端连接的ip和端口号和TIME_WAIT占用的链接重复了, 就会出现问题. 使用setsockopt()设置socket描述符的 选项SO_REUSEADDR为1, 表示允许创建端口号相同但IP地址不同的多个socket描述符 理解 CLOSE_WAIT 状态  //要改 对于服务器上出现大量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭 socket, 导致四次挥手没有正确完成. 这是一个 BUG. 只需要加上对应的 close 即可解决问题 滑动窗口 刚才我们讨论了确认应答策略, 对每一个发送的数据段, 都要给一个ACK确认应答. 收到ACK后再发送下一个数据段.这样做有一个比较大的缺点, 就是性能较差. 尤其是数据往返的时间较长的时候。 既然这样一发一收的方式性能较低, 那么我们一次发送多条数据, 就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了)。 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值. 上图的窗口大小就是4000个字节(四个段)。发送前四个段的时候, 不需要等待任何ACK, 直接发送。收到第一个ACK后, 滑动窗口向后移动, 继续发送第五个段的数据; 依次类推。 操作系统内核为了维护这个滑动窗口, 需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答; 只有确认应答过的数据, 才能从缓冲区删掉。 窗口越大, 则网络的吞吐率就越高。 左边是已经发送并且已经应答的区域 中间写了 后面是还没发送的数据 左边的数据收到了就会向右滑动 那么如果出现了丢包, 如何进行重传? 这里分两种情况讨论 情况一: 数据包已经抵达, ACK被丢了 这种情况下, 部分ACK丢了并不要紧, 因为可以通过后续的ACK进行确认;  情况二: 数据包就直接丢了 当某一段报文段丢失之后, 发送端会一直收到 1001 这样的ACK, 就像是在提醒发送端 我想要的是 1001一样如果发送端主机连续三次收到了同样一个 1001 这样的应答, 就会将对应的数据 1001 - 2000 重新发送。这个时候接收端收到了 1001 之后, 再次返回的ACK就是7001了(因为2001 - 7000)接收端其实之前就已经收到了, 被放到了接收端操作系统内核的接收缓冲区中。这种机制被称为 高速重发控制(也叫 快重传) 。 流量控制  接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这个时候如果发送端继续发送,就会造成丢包, 继而引起丢包重传等等一系列连锁反应。 因此TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制(Flow Control)。 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 窗口大小 字段, 通过ACK端通知发送端; 窗口大小字段越大, 说明网络的吞吐量越高; 接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端; 发送端接受到这个窗口之后, 就会减慢自己的发送速度; 如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端 接收端如何把窗口大小告诉发送端呢? 回忆我们的TCP首部中, 有一个16位窗口字段, 就是存放了窗口大小信息; 那么问题来了, 16位数字最大表示65535, 那么TCP窗口最大就是65535字节么? 实际上, TCP首部40字节选项中还包含了一个窗口扩大因子M, 实际窗口大小是 窗口字段的值左移 M 位; 拥塞控制  解决网络出问题软件问题的情况 虽然TCP有了滑动窗口这个大杀器, 能够高效可靠的发送大量的数据. 但是如果在刚开始阶段就发送大量的数据, 仍然可能引发问题。 因为网络上有很多的计算机, 可能当前的网络状态就已经比较拥堵. 在不清楚当前网络状态下, 贸然发送大量的数据,是很有可能引起雪上加霜的。 TCP引入 慢启动 机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据。 此处引入一个概念程为拥塞窗口 发送开始的时候, 定义拥塞窗口大小为1; 每次收到一个ACK应答, 拥塞窗口加1; 每次发送数据包的时候, 将拥塞窗口和接收端主机反馈的窗口大小做比较, 取较小的值作为实际发送的窗口;像上面这样的拥塞窗口增长速度, 是指数级别的. 慢启动 只是指初使时慢, 但是增长速度非常快。 为了不增长的那么快, 因此不能使拥塞窗口单纯的加倍。此处引入一个叫做慢启动的阈值。当拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长。 当TCP开始启动的时候, 慢启动阈值等于窗口最大值; 在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回1;少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞; 当TCP通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降; 拥塞控制, 归根结底是TCP协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案 延迟应答  如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小 假设接收端缓冲区为1M. 一次收到了500K的数据; 如果立刻应答, 返回的窗口就是500K; 但实际上可能处理端处理的速度很快, 10ms之内就把500K数据从缓冲区消费掉了; 在这种情况下, 接收端处理还远没有达到自己的极限, 即使窗口再放大一些, 也能处理过来; 如果接收端稍微等一会再应答, 比如等待200ms再应答, 那么这个时候返回的窗口大小就是1M;一定要记得, 窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽量提高传输效率; 那么所有的包都可以延迟应答么? 肯定也不是; 数量限制: 每隔N个包就应答一次; 时间限制: 超过最大延迟时间就应答一次;具体的数量和超时时间, 依操作系统不同也有差异; 一般N取2, 超时时间取200ms; 捎带应答  当我们接受消息返回ACK的同时给ACK带上数据也就是说回复的时候带上我想发送的数据这才是tcp通信的真相。 在延迟应答的基础上, 我们发现, 很多情况下, 客户端服务器在应用层也是 一发一收 的. 意味着客户端给服务器说了 How are you, 服务器也会给客户端回一个 Fine, thank you; 那么这个时候ACK就可以搭顺风车, 和服务器回应的 Fine, thank you 一起回给客户端。 面向字节流 创建一个TCP的socket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区; 硬件网卡接受数据的时候是可以通过硬件中断有个中断向量表。每一个中断要被处理是要有中断向量表的用中断号做下标搞的一个函数指针数组然后用中断号下标去指向特定的方法就可以了让OS知道有数据的让OS调用拷贝函数解包向上交付把数据加载到接受缓冲区tcp/内存。就像键盘输入一样然后应用程序可以read从接受缓冲区读取数据。 调用write时, 数据会先写入发送缓冲区中; 如果发送的字节数太长, 会被拆分成多个TCP的数据包发出; 如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去; 接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区; 然后应用程序可以调用read从接收缓冲区拿数据; 另一方面, TCP的一个连接, 既有发送缓冲区, 也有接收缓冲区, 那么对于这一个连接, 既可以读数据, 也可以写数据. 这个概念叫做 全双工由于缓冲区的存在, TCP程序的读和写不需要一一匹配, 例如: 写100个字节数据时, 可以调用一次write写100个字节, 也可以调用100次write, 每次写一个字节; 读100个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以一次read 100个字节, 也可以一次read一个字节, 重复100次;粘包问题 明确报文和报文的边界 首先要明确, 粘包问题中的 包 , 是指的应用层的数据包在TCP的协议头中, 没有如同UDP一样的 报文长度 这样的字段, 但是有一个序号这样的字段站在传输层的角度, TCP是一个一个报文过来的. 按照序号排好序放在缓冲区中站在应用层的角度, 看到的只是一串连续的字节数据那么应用程序看到了这么一连串的字节数据, 就不知道从哪个部分开始到哪个部分, 是一个完整的应用层数据包那么如何避免粘包问题呢? 归根结底就是一句话, 明确两个包之间的边界 对于定长的包, 保证每次都按固定大小读取即可; 例如上面的Request结构, 是固定大小的, 那么就从缓冲区从头开始按sizeof(Request)依次读取即可; 对于变长的包, 可以在包头的位置, 约定一个包总长度的字段, 从而就知道了包的结束位置; 对于变长的包, 还可以在包和包之间使用明确的分隔符(应用层协议, 是程序猿自己来定的, 只要保证分隔符不和正文冲突即可)对于UDP协议来说, 是否也存在 粘包问题 呢 对于UDP, 如果还没有上层交付数据, UDP的报文长度仍然在. 同时, UDP是一个一个把数据交付给应用层. 就有很明确的数据边界。站在应用层的角度, 使用UDP的时候, 要么收到完整的UDP报文, 要么不收. 不会出现半个的情况。关于listen的第二个参数 1. 直接telnet不accept连接可以建立也就是说accept和我们的三次握手没有关系不调用底层也会自动三次握手成功调用accept只是把底层已经三次握手建立的连接拿上来经此而已 我们此时listen第二个参数是设置的2 我们一直发送了四次发现有一个State不一样也就是说当前连接还没有完成 为什么呢  这次我们是没有去accept的但是如果将来有可能服务器非常忙来不及accept一瞬间来了几百上千的连接那么我们底层的连接就要在OS层面上进行排队。 listen的第二个参数叫做底层的全连接长度算法是n1表示在不accept的情况下你最多能维护多少个全连接如果收到更多那么tcp就不再进行三次握手而是收到这个请求已半连接的形式存在着太久了就会销毁等后续有新连接关闭退出了我们再把这个连接建立成功 2. 然后我们现在qiut关闭两个连接客户端主动断开连接而我们服务端是没有关闭他的关闭他需要先accpt再close所以我们服务端的连接状态就是CLOSE_WAIT。 所以我们知道了如果我们的服务端不关闭对应的文件描述符那么所处的状态就叫做CLOSE_WAIT状态。 然后我们再变一下此时我们accept但是还是不close经过测试我们服务端的连接状态还是CLOSE_WAIT。所以说只要不close他那么客户端断开连接之后我们服务端所处的连接状态一直都是CLOSE_WAIT  3. 我们在服务器运行切保持连接的情况下ctrlc关闭服务端这样使我们的服务端为断开连接的一方我们也发现他的状态将由ESTABLISHED变为TIME_WAIT。 只要走到了TIME_WAIT断开连接的一方就认为自己已经把四次挥手的工作做完了。  然后又过了一段时间我们去查看就发现TIME_WAIT等待的时间到了之后连接就释放掉了这也代表这连接真正关闭。 PS如果客户端服务端建立好连接了如果CLOSE_WAITE一直存在的话他也是消耗资源的如果在未来我们去netstat的时候发现服务器上挂了大量的CLOSE_WAITE那么基本上只有一种可能性那就是我们自己写的网络服务器把文件描述符获取上来了但是没有调用close因为没有关所以他的状态就没有办法主动的再进行四次挥手所以最终出现了这个问题。所以如果往后发现服务器非常卡就可以netstat查看一下是不是服务器上挂了大量的CLOSE_WAITE这样状态的连接办关闭状态等待调用close挂连接是要消耗资源的  TCP异常情况 进程终止: 进程终止会释放文件描述符, 仍然可以发送FIN. 和正常关闭没有什么区别. 机器重启: 和进程终止的情况相同。 机器掉电/网线断开: 接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset即 使没有写入操作, TCP自己也内置了一个保活定时器, 会定期询问对方是否还在. 如果对方不在, 也会把连接释放。 另外, 应用层的某些协议, 也有一些这样的检测机制. 例如HTTP长连接中, 也会定期检测对方的状态. 例如QQ, 在QQ 断线之后, 也会定期尝试重新连接。 比如说两个主机正在通信服务端突然进程终止了。在我们双方进行通信时双方一个或两个终止了网络也是文件打开文件的生命周期是随进程的所以进程终止时OS会自动关闭我们之前所打开的文件在网络这里就相当于会自动进行四次挥手因为三次握手也是网络自动做的是tcp做的tcp也属于OS所以进程终止时我们是不用担心的即连接建立了我们把机器重启或者关机是不会出现问题的就比如我们打开的有网页去关机的时候他就会有提示是否关闭重启如果是他就会关闭应用说明关机OS是需要关闭打开的应用的包括我们曾经打开的客户端或者服务器涉及网络就要进行四次挥手如果否他就是取消关机。 服务器ctrlc后先断开就发现状态变成了TIME_WAIT。   就是说在TIME_WAIT期间想要重启服务是不行的必须要等一会等netstat 查不到刚刚那个连接就可以重新启动了。因为文件描述符的声明周期是随进程的虽然我们代码里面没有close但是我们进程ctrlc了OS在终止杀掉进程的时候底层会自动实现握手过程。就好比我们之前写文件的时候文件描述符没有关但是进程退出之后OS会帮你关一样的。 现在有问题我都已经完成了四次挥手为什么还要有TIME_WAIT状态呢 是因为最后一次ACK是否被对方收到是不确定的所以就怕他丢了这样服务器就只能以异常情况关闭连接。解决问题的方式就是让主动发起连接的一方退出发送最后一个报文时先不要直接变成CLOSE先等一等要让对方尽量收到ACK如果丢了另一方就大概可能会进行超时重传FIN这个就是需要控制好TIME_WAIT的等待时间而使发送方重新发送ACK。这个时间一般就是2倍的MSL最大传送时间至少保证一来一回即一个FIN一个ACKMSL就是两端传送的最大时间  解决TIME_WAIT状态引起的bind失败的方法 TIME_WAIT状态时连接虽然名存实亡但他依旧还是存在此时bind的时候就意味着要bind的ip和端口依旧被占用所以bind失败但是这是有问题的。看WPS setsockopt() 可以用两个也可以只要上面那个就写在创建套接字后面 PSaccept并不参与三次握手而是从底层拿取连接  TCP小结 可靠性: 校验和 序列号(按序到达) 确认应答 超时重发 连接管理 流量控制 拥塞控制 提高性能: 滑动窗口 快速重传 延迟应答 捎带应答 其他: 定时器(超时重传定时器, 保活定时器, TIME_WAIT定时器等) 基于TCP应用层协议 HTTP HTTPS SSH Telnet FTP SMTP 当然, 也包括你自己写TCP程序时自定义的应用层协议 TCP/UDP对比 我们说了TCP是可靠连接, 那么是不是TCP一定就优于UDP呢? TCP和UDP之间的优点和缺点, 不能简单, 绝对的进行比较 TCP用于可靠传输的情况, 应用于文件传输, 重要状态更新等场景; UDP用于对高速传输和实时性要求较高的通信领域, 例如, 早期的QQ, 视频传输等. 另外UDP可以用于广播归根结底, TCP和UDP都是程序员的工具, 什么时机用, 具体怎么用, 还是要根据具体的需求场景去判定。 最后的最后创作不易希望读者三连支持 赠人玫瑰手有余香
http://www.w-s-a.com/news/374545/

相关文章:

  • ios开发网站app网站建设企业有哪些方面
  • 网站主页 优帮云深圳代做网站后台
  • app 与网站网站建设要做什么
  • 厦门国外网站建设公司郑州核酸点推vip服务
  • 免费网线seo外链怎么做
  • 宽带技术网网站wordpress widget hook
  • 山西省住房和城乡建设厅网站报名wordpress添加标签插件
  • 网站怎么自己做外贸网站案例
  • 做网站的优势公司网站怎么做站外链接
  • 海城网站制作建设精准营销的营销方式
  • 北京短视频拍摄公司重庆网站seo推广公司
  • 广州免费推广网站建设4399网页游戏大全
  • 网站的构架与组成建站公司兴田德润
  • php网站部署步骤邯郸哪有做网站的
  • 做设计什么设计比较好的网站南充市住房和城乡建设局考试网站
  • 郑州做系统集成的公司网站龙岩
  • 厦门SEO_厦门网站建设网络营销课程视频
  • vs 2015 网站开发开网店在线咨询
  • 前端如何优化网站性能大学学校类网站设计
  • 中国铁路建设投资公司网站熊学军中国it外包公司排名前50
  • 房产网站的建设广州推广排名
  • 湟源县网站建设wordpress删除未分类
  • 营销型网站开发推广厦门百度seo公司
  • 遵义网站开发培训上海中高风险地区名单最新
  • 禹州市门户网站建设做网站可以申请个体户么
  • 大良营销网站建设效果彩票网站搭建 做网站
  • 做网站的公司为什么人少了在中国如何推广外贸平台
  • 盘锦网站制作工业电商网站怎么配色
  • 白云企业网站建设seo排名点击软件
  • wordpress跨站脚本攻击漏洞国外注册的域名国内能用吗