赞
踩
TCP的结构如下
序列号seq:占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生。给字节编上序号后,就给每一个报文段派一个序号,序列号seq就是这个报文段中的第一个字节的数据编号
确认号ack:占4个字节,期待收到对方下一个报文段的第一个数据字节的序号,序列号表示报文段携带数据的第一个字节的编号,而确认号指的是期望接受到下一个字节的编号,因此当前报文段最后一个字节的编号+1即为确认号
确认ACK:占1位,仅当ACK=1时,确认号字段才会生效,ACK=0时,确认号无效
同步SYN:连接建立时用于同步序号,当SYN=1,ACK=0时表示:这是一个连接请求报文段,若同意连接,则在响应报文段中使得SYN=1,ACK=1。因此SYN=1表示这是一个连接请求,或连接接受报文。SYN这个标志位只有在TCP建立连接时才会被置为1,握手完成就会被置为0
三次握手:
1.Client将标志位SYN置为1,随机产生一个值seq=j,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认
2.Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=j+1,随机产生一个值seq=K,并将该数据包发送给Client已确认连接请求,Server进入SYN_RCVD状态
3.Client收到确认后,检查ack是否为j+1,如果正确则将标志位ACK置为1,ack= k+1,并将该数据包发送给Server,Server检查ack是否为K+1,Ack是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client和Server之间可以开始传输数据
四次挥手
由于TCP是全双工的,因此每个方向都要单独关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只能意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍能够发送数据,直到这一方向也发送了FIN,首先进行关闭的一方主动关闭,另一方则执行被动关闭
1.数据传输结束后,客户端的应用进程发出释放报文段,并停止发送数据,客户端进入FIN_WAIT_1状态,此时客户端依然可以接受服务器发来的数据
2.服务器接收到FIN后,发送一个ACK给客户端,确认序号为收到的序号+1,服务器进入CLOSE_WAIT状态。客户端收到后进入FIN_WAIT_2状态。
3.当服务器没有数据要发送时,服务器发送一个FIN报文,此时服务器进入LAST_ACK状态,等待客户端的确认
4.客户端收到服务器FIN报文后,给服务器发送一个ACK报文,确认序列号为收到的序号+1。此时客户端进入TIME_WAIT状态,等待2MSL(MSL:报文最大的生存时间),然后关闭连接
为什么一定要等待2MSL
因为去向ACK消息最大存活时间(MSL)+来向FIN消息的最大存活时间(MSL)这恰恰就是2MSL,等待2MSL事件,Client就可以放心的释放TCP所占用的资源、端口号,此时可以使用该端口号连接任何服务器,如果不等,释放的端口可能会重连刚断开的服务器端口,这样依然存活在网络里的老的TCP报文可能与新TCP连接报文冲突,造成数据冲突,为了避免这种情况,所以要等待2MSL时间
TCP如何保证可靠性
1.序列号,确认应答,超时重传
数据到达接收方,接收方需要发出一个确认应答,表示已经收到该数据段,并且确认序号会说明他下一次需要接受的序列号,如果发送方迟迟未收到确认应答,那么可能是发送的数据丢失,也可能是确认应答丢失,这时发送方在等待期间会进行重传,这个时间一般是2PRR(报文段往返时间)+一个偏差值
2.窗口控制高速重发控制/快速重传
TCP会利用窗口控制来提高传输速度,意思是在一个窗口大小内,不用一定等到应答才能发送下一段数据,窗口需等待确认而可以继续发送数据的最大值。如果不使用窗口控制,每一个没收到确认应答的数据都要重发,使用窗口控制,如果数据段1001-2000丢失,后面数据每次传输,确认应答都会不停的发送序号为1001的应答,表示我要接受1001开始的数据,发送端如果收到3次相同的应答,就会立刻进行重发
3.拥塞控制
如果把窗口定的很大,发送端会发送大量的数据,可能会造成网络拥塞
解决方法慢启动:定义拥塞窗口,一开始该窗口大小设为1,每收到一个确认应答后,将拥塞窗口大小2
拥塞避免:设置慢启动阈值,一般开始设为65536,当窗口大小达到这个阈值时,拥塞窗口指数不再指数上升,而是加法增加,以此来避免拥塞。将报文段的超时重传堪称拥塞,一旦发生超时重传,我们需要先将阈值设置为当前窗口大小的一半,并且将窗口大小设为初值1,然后会进入到慢启动过程
TCP粘包问题
原因:
TCP是一个基于字节流的传输服务(UDP基于报文的),"流"意味着TCP所传输的数据是没有边界的。所以可能会出现两个数据包黏在一起的情况
解决:
1.发送定长包,如果每个消息的大小都一样,那么在接受对等方只要累计接收数据,直到数据等于一个定长的数值就将它作为一个消息
2.包头加上包体长度。包头是定长的4字节,说明了包体的长度,接收对等方先接受包头长度,依据报头长度来接收包体。
3.在数据包之间设置边界,如添加特殊符号\r\n标记。FTP协议正是这么做的。但问题在于如果数据正文中也含有\r\n,则会误判为消息的边界
4.使用更加复杂的应用层协议
TCP流量控制
所谓流量控制就是让发送方的发送频率不要太快,让接收方来得及接收,如果接受方来不及接受发送方发送的数据,那么就会有分组丢失。在TCP中利用可变长的滑动窗口机制就可以很方便的在TCP连接上实现对发送方的流量控制,主要的方式是接收方返回的ACK中会包含自己的接收窗口大小,以控制发送方此次发送的数据量大小(窗口大小)
如果接受方滑动窗口满了,发送方会怎么做
基于TCP流量控制中的滑动窗口协议,我们知道接收方返回给发送方的ACK包中会包含自己的接收窗口大小,若接收方窗口已满,此时接收方返回给发送方的接收窗口大小为0,此时发送方会等待接收方发送的窗口大小直到变为非0为止,然后,接收方回应的ACK包是存在丢失的可能的,为了防止双方一直等待而出现死锁情况,此时就需要坚持计时器来辅助发送方周期性地向接收方查询,以便发现窗口是否变大,当发现窗口大小为非0时,发送方继续发送数据
三次握手的时候每次握手信息对方没有收到会怎么样
1.若第一次握手服务器未接受到客户端请求建立连接的数据包时,服务器不会进行任何相应的动作,而客户端由于在一段时间内没有收到服务器发来的确认报文,因此会等待一段时间后重新发送SYN同步报文,若仍然没有回应,则重复上述过程直到发送次数超过最大重传次数限制后,建立连接的系统调用会返回-1
2.若第二次握手客户端未接受到服务器回应的ACK报文时,客户端会采取第一次握手失败的动作,这里不再重复,而服务器此时将阻塞在accept()系统调用处等待client再次发送ACK报文
3.若第三次握手服务器未接受到客户端发送过来的ACK报文,同样会采取类似于客户端的超时重传机制,若重传次数超过限制后仍然没有回应,则accept()系统调用返回-1,服务器端连接建立失败,但此时客户端任务自己已经连接成功了,因此开始向服务器端发送数据,但是服务器端的accept()系统调用已返回,此时没有在监听状态,因此服务器端接收到来自客户端发送来的数据时会发送RST报文给客户端,消除客户端单方面建立连接的状态。
第二次握手传回了ACK,为什么还要传回SYN
ACK是为了告诉客户端发送来的数据已经接收无误,而传回SYN是为了告诉客户端,服务端接收到的消息确实是客户端发送的消息
有很多close-wait怎么解决
1.首先检查是不是自己代码的问题(看是否服务端程序忘记关闭连接),如果是,则修改代码
2.调整系统参数,包括句柄相关参数和TCP/IP的参数,一般一个CLOSE_WAIT会维持至少两个小时的时间,我们可以通过调整参数来缩短这个时间
TIME_WAIT状态会导致什么问题
我们考虑高并发短连接的业务场景,在高并发短连接的TCP服务器上,当服务器处理完请求后主动请求关闭连接,这样服务器上会有大量的连接处于TIME_WAIT状态,服务器维护每一个连接需要一个socket,也就是每个连接会占用一个文件描述符,而文件描述符的使用是有上限的,如果持续高并发,会导致一些正常的连接失败
TIME_WAIT状态如何避免
首先服务器可以设置SO_REUSEADDR套接字选(端口复用)来通知内核,如果端口忙,但TCP连接位于TIME_WAIT状态时可以重用端口,在一个非常有用的场景就是,如果你的服务器程序停止后想立即重启,而新的套接字依旧希望使用同一端口,此时SO_REUSEADDR选项就可以避免TIME_WAIT状态
也可以使用长连接的方式减少TCP的连接与断开,在场连接的业务中,往往不需要考虑TIME_WAIT的状态,但其实在长连接的业务中并发量一般不会太高
TCP如何实时监测断线情况
TCP正常的断开,通信双方都是知道的,但是非正常的断开,比如直接拔掉了网线,就只能靠如下两种方法,实现短时间内的检测
1.心跳包机制
心跳包机制是网游设计中常用的机制。从用户层面,自己发包去判断对方连接状态,可以根据情况,很灵活的使用。比如,20S发送一个最小的数据包。如果发送没有回应,就判断对方掉线了,断开后立即关闭socket
2.利用tcp_keepalive机制
利用TCP的机制,通过设置系统参数,从系统层面,监测tcp的连接状态。在配置socket属性时,使用 keepalive option,一旦有此配置,这些长时间无数据的链接会根据tcp的keepalive内核属性,在大于(tcp_keepalive_time)所对应的时间(单位为秒)之后,断开这些链接。
关于keep alive无论windows,还是linux,keepalive就三个参数:
keepalive_probes: 探测次数
keepalive_time: 探测的超时
keepalive_intvl: 探测间隔
对于一个已经建立的tcp连接,如果在keepalive_time时间内双方没有任何的数据包传输,则开启keepalive功能的一端将发送 eepalive数据包,若没有收到应答,则每隔keepalive_intvl时间再发送该数据包,发送keepalive_probes次。一直没有收到应答,则发送rst包关闭连接。若收到应答,则将计时器清零。
TCP最大连接数限制
Client最大TCP连接数client在每次发起TCP连接请求时,如果自己并不指定端口的话,系统会随机选择一个本地端口,该端口是独占的,不能和其他TCP连接共享。TCP端口的数据类型是unsigned short,因此本地端口个数最大只有65536,除了端口0不能使用外,其他端口在空闲时都可以正常使用,这样可用的端口最多有65535个
Server最大TCP连接数server通常固定在某个本地端口上监听,等待client的连接请求,不考虑地址重用的情况下,即使server段有多个ip,本地监听端口也是独占的,因此server端TCP连接4元组中只有客户端的IP地址和端口号是可变的,因此最大TCP连接为客户端IP数 x 客户端port数,在IPV4,不考虑IP地址分类的情况下,最大TCP连接数为2的32次方 x 2的16次方,也就是server端单机最大TCP连接数约为2的48次方
然而上面给出的只是理论上的单机最大连接数,在实际情况下,收到明文规定(一些IP地址和端口具有特殊意义,没有对外开放),急起资源,操作系统等限制,特别是server端,其最大并发TCP连接数远不能达到理论上限。对server端,通过增加内存,修改最大文件描述符个数等参数,单机最大并发TCP连接数超过10万是没有问题的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。