TCP设计思想
TCP是传输层的可靠传输协议,而传输层以下的网络层不提供可靠传输,因此TCP协议必须做一些措施,出错重发,超时重发,以及在接收方来不及接收时告诉发送方降低速度。TCP还要满足高性能要求,所以非常复杂。
连接管理
TCP面向连接,在收发数据之前三次握手建立连接,结束后四次挥手断开连接。
连接是对两端的socket建立的,是一个有实体概念的东西。操作系统在用socket创建连接后,为了描述和组织连接管理,肯定是会有开销的。
一个端口可以被多个进程绑定吗?
套接字的目的就是为了唯一标识一个主机上的进程,一个端口必然只能被一个进程占用。但是一个端口上能建立多个TCP连接,因为一个连接由两个socket组成,监听socket只有一个但客户端socket可不止一个。
一个程序还可以使用多个端口,不同的端口可以连接不同的服务器程序,以提供不同的服务
三次握手过程
- 客户端调用connect主动请求连接,发送SYN报文段。
- 服务端监听到连接请求,调用accept阻塞起来进入SYN收到状态,并发送确认报文确认收到SYN。
- 客户端收到确认报文,connect从阻塞状态返回并建立连接,发送对确认报文的确认
- 服务器收到确认确认报文,accept函数返回,返回一个套接字标识和客户端建立的连接
四次挥手过程
- **主动关闭方**close文件描述符,发送FIN报文段,进入FIN_WAIT1
- **被动方**read返回0值,发送对FIN的确认报文,进入CLOSE_WAIT
- 主动方收到ACK,进入FIN_WAIT2
- 过一段时间,被动方关闭连接,发送FIN,进入LAST_ACK
- 主动方收到FIN,发送对FIN的ACK,进入TIME_WAIT,并在2ML后关闭连接
- 被动方收到ACK,关闭连接
问题1:为什么是3次握手?
连接必须由客户端主动发起,若是服务器收到客户请求立即建立连接,但是给客户端的ACK丢失了,那么服务器认为建立了连接,但是客户端不认为,造成服务器的闲置连接问题。同样若是第三个报文丢失了,只是客户端认为建立了连接,并不会浪费服务器资源。总之要保证服务器的安全性。
问题2:为什么是4次挥手?
不能一方说关闭,另一方就必须立刻关闭,必须双方都处理完自己的数据才能关闭。
问题3:TIME_WAIT状态的作用?
- 保证在网络中的迟到报文消散完毕,这些报文可能会对在socket上新建立的TCP连接有影响
- 保证最后一个ACK可靠到达,假如最后一个ACK丢失对端不能关闭连接,在2MSL时间内对端有机会重发FIN并让主动关闭方收到。
在TIME_WAIT期间建立新的TCP连接?
常见错误bind: Address already in use
bind函数绑定了ip和端口形成套接字(文件描述符),一个本地地址(IP+端口)使用期间只能bind一次。但有时会需要“重用”地址。当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,就要使用端口复用。setsockopt函数可以实现。由于一个连接由两个套接字标识,所以即使重用了地址,也不会影响正常的连接。
服务器可以绑定ip为 0.0.0.0?
在我服务器有多个ip的情况下,绑定任意一个ip,有客户请求到来让路由表分配给哪个ip。
参考:https://blog.csdn.net/gzzheyi/article/details/7281517
通常不让服务端主动关闭?
如果是服务端主动关闭,大量文件描述符在time wait期间被占用。
服务端没有足够文件描述符,怎么处理超负荷的连接?
提前空出一个文件描述符,只要客户端连上就立即关闭客户端。
流量控制
接收端接受速度有限,如果发送端不加以控制,会让接收端接受缓冲区打满引发重传等一系列反应。
所以流量控制就是根据接收端的处理能力,决定发送端的发送速度。接收端将自己的接收缓冲区剩余大小写入报文中,发送方根据报文的窗口大小字段决定发送窗口大小。
拥塞控制
流量控制解决端到端的发送速度问题,拥塞控制解决网络引起的拥塞问题。
有了滑动窗口的TCP能同时网网络注入大量报文,但可能会引起拥塞,因此需要加以拥塞控制。
开始通信时用慢开始试探,到达窗口阈值加法增大,遇到网络拥塞乘法减小。
网络拥塞是根据丢包率判断的,少量丢包就采用重传即可。
拥塞控制的本质是尽可能提高传输效率但又避免给网络造成太大压力的权衡方案。
滑动窗口
确认应答机制已经保证了TCP传输可靠性,但是一来一回的传输方式效率太低。为了一次性发送多个报文段,引入滑动窗口。在调用socket时操作系统就在内核开辟了发送缓冲区和接受缓冲区,滑动窗口在自己的发送缓冲区里进行滑动。
窗口的左侧是已经确认收到的报文,窗口内是允许一次发送的最大报文数量,右侧是尚未发送的报文。窗口可以右移或者减小。右移是在收到确认后,减小是收到对端的窗口减小通知或者拥塞控制。
报文段格式
拿到一个报文格式首先考虑如何分离报头,怎样交付给上层协议。
TCP报文总长减去4位首部长度就是有效载荷长度,16位目的端口号标识应用层的进程。
6个标志位:
SYN和FIN分别在建立和释放连接时有效,区分普通的数据报文。
ACK标识确认报文的确认序号有效。
URG表明带外数据,需要被优先发送。
PSH是发送端催促接收端尽快处理缓冲区中数据。
RST是对最后一个报文丢失的补救方法。建立连接的第三个报文丢失,client却向服务器发送报文,服务器这时发送RST要求重新建立连接。
面向字节流
创建socket时,操作系统在内核创建收发缓冲区。TCP是全双工的,因为可以对连接fd调用write和read直接对缓冲区进行读写,应用层按需从有序的字节流中拿走一定长度的字节数据。
应用层必须自己规定好数据包的边界,用分隔符也好,定长结构体也好,总之避免粘包问题。
TCP异常终止
进程终止和机器重启,都会关闭文件描述符并发送FIN,和正常关闭没有区别。
机器掉电:接收端和客户端都认为对方存在,并且定时发送探测报文询问是否存在,若长时间没有应答则关闭连接。
TCP?UDP
没有优劣,只是使用场景不同
TCP用于文件传输,等需要可靠连接的场景
UDP用于对实时性要求高的场景