理解TCP
可靠、有序、可以按字节分批传输的面向连接的数据传输方式的套接字。
TCP 生命周期
// 创建套接字,成功时返回文件描述符,失败时返回-1
int socket(int domain, int type, int protocol);
// 给套接字分配IP地址和端口号
int bind(int sockfd, struct sockaddr* pSockAddr, socklen_t addrLen);
// 等待连接,这时客户端才能调用connect,服务端把客户端的连接请求放到一个连接请求等待队列里
int listen(int sockfd, int backlog);
// 受理连接,成功时返回文件描述符,失败时返回-1
// 这时候会自动创建另外一个套接字来处理连接请求等待队列中待处理的客户端连接请求
int accept(int sockfd, struct sockaddr* pSockAddr, socklen_t* pAddrLen);
// 关闭连接
close(int sockfd);
TCP 连接的三次握手
创建连接为什么需要三次握手呢?正常来说应该是两次就行了吧?让我们先来看一个现实生活中的例子:
小A正在跟女神讲电话,聊得正欢的时候,手机信号出了点问题。
- 小A:你能听到我说话吗?
- 女神:能听到,你能听到我说话吗?
- 小A:能听到啊,刚才信号出了点问题,我们继续吧!
TCP建立连接的过程就跟上面的例子很像,双方需要相互确认,TCP的特点是保证传输可靠的,并且一般是会把数据包拆分成多个发送的,所以它还要解决包的乱序问题,我们来看看TCP建立连接的过程:
- 客户端发起连接请求,SYN标志置为1,表示这个操作是建立连接,并且设置一个初始序号SEQ:1000(随机数)
- 服务端收到客户端发来的建立连接的请求,同样将SYN标志置为1,ACK:1001(1000+1),并且设置一个初始序号SEQ:2000(随机数)
- 客户端回复SEQ:1001, ACK:2001(2000+1)
正因为客户端和服务端需要互相告知自己的初始序号,并且要确认双方收发数据没有问题,所以就多了一步,这就是TCP为什么需要三次握手的原因。这个序号是用来解决包的乱序问题的,而且它是随机的(以防被攻击),连接完成以后在后续的数据收发操作当中这个值会随着包的长度一直增加。
TCP 数据传输
TCP套接字的I/O缓冲
- I/O缓冲在每个TCP套接字中单独存在
- I/O缓冲在创建套接字时自动生成
- 即使关闭套接字也会继续传递输出缓冲中遗留的数据
- 关闭套接字将丢失输入缓冲区中的数据
TCP 滑动窗口
假设客户端的输入缓冲只有50字节的空间了,而服务器端传输了100字节过来,这时候数据会丢失吗?
TCP通过滑动窗口来控制数据流来避免这个问题:
- 客户端:你好,你最多可以传递50字节过来。
- 服务端:好的。
- 客户端:我腾出了20字节的空间,你现在可以传递70字节过来了。
- 服务端:好嘞!
TCP 四次挥手
小A和女神聊得太欢乐,时间很快就过去了,女神一看时间已经凌晨1点钟了,明天还要早起去公司赶文件呢。
- 女神:太晚了,我要睡了。
- 小A:等等,我还有些话没跟你说完呢。。。balabala。。。(又说了10分钟)
- 小A:好了,我说完了,你去睡吧。
- 小A:嗯,你也早点睡,晚安。