前言
本文主要介绍 Posix API与网络协议栈
对于POSIX的介绍.在《UNIX环境高级编程》中的描述是:
POSIX是一个由 IEEE(电气和电子工程师学会 )制订的标准族。 POSIX的意思是计算机环境
的可移植操作系统界面(Portable Operating System Interface for Computer Environment)。
这边可以理解是为 POSIX 是为Unix操作系统开发制定的一套标准
Posix API包括
1.socket 2.bind 3.listen 4.accept 5.recv 6.send 7.close 8.connect //作为客户端连接服务端时使用
本专栏知识点是通过零声教育的线上课学习,进行梳理总结写下文章,对c/c++linux课程感兴趣的读者,可以点击链接 C/C++后台高级服务器课程介绍 详细查看课程的服务。
提示:以下是本篇文章正文内容,下面案例可供参考
一、TCP三个阶段
1.建立连接
三次握手
三次握手是发送在哪个函数里面: 当客户端调用connect函数连接 服务端
当客户端第一个次发送SYC到达服务端时, 服务端会构建一个包(结构体: 结构体中包含客户端的信息) 加入队列(半连接队列)中, 当服务端收到客户端的ACK包时,同样也会构建一个队列(全连接队列), 会从半连接队列中找是否有这个客户端的包, 如果有就放到全连接队列中, 这个连接(握手)的节点 叫做 TCP控制块(tcb)
半连接队列:syn queue
全连接队列: accept queue
如何通过网络数据包, 查找到半连接队列的节点(TCB)
五元组:(sip(源IP), dip(目的IP), sport(源端口), dport(目的端口), proto(协议))
端口只有65535个, 如何做到百万级别的连接
一个连接底层就是一个tcb,tcb的唯一性就是五元组, 一个连接对应一个五元,只要五元不冲突就可以依次增加
测试百万连接并发,要开100个端口
从五元组的角度来说 源IP只有1个,目标IP也只有一个,源端口(客户端端口不可被控制只有65535个),那么三个客户端并发请求最多只能支撑3*65535 个连接,于是只能通过增大服务端的端口来使五元组能扩展的更多(如果有足够多的客户端机器(源端口),那么一个目的端口也可以实现百万连接)
POSIX API 实现
1.listen,
在socket fd创建时, 底层会有一个tcb的存在,listen 把tcb设置成listen(被监听 可连接)的状态。(后续全连接队列, 半连接队列创建的前提)
2.accept
当连接队列为空时, 等待阻塞
当连接进入全连接队列后, accept 函数产生client fd
clientfd = accept(listenfd, addr, addrlen);
3.connect
fd查找到对应的tcb,通过tcb 对 地址发送syn包
connect(fd, serveraddr, size)
4.bind
socket(插座), 一个socket由两部分组成 fd 跟 tcb, 当创建socket 时会产生一个 fd,使用bind 把 fd 跟tcb进行绑定
不会对tcb进行状态改变,此时tcb还是处于closed状态
bind(ip, port)
2.传输数据
send(): 把数据 copy 到内核(tcb)中,什么时候发送数据到对端,发送多少数据由网络协议栈决定 。通过对端的ACK(确认值)回传,确定对端有没有收到数据。
recv(): 通过接收过来的包组成五元组 获取到fd,查到对应的tcb 放到recv buff里,应用层调用recv函数读数据,每次读取多少数据由应用程序决定
最大传输单元(Maximum Transmission Unit, MTU): 数据链路层传输的帧大小限制
TCP 最大段大小(Max Segment Size,MSS): TCP 为了避免被发送方分片,会主动把数据分割成小段再交给网络层,最大的分段大小称之为 MSS(MSS = MTU - IP header头大小 - TCP 头大小)
3.断开连接
close : 将首部标志状态位 FIN 置为 1
shutdown: 单向的socket便称为半开放Socket, 要实现半开放式,需要用到shutdown()函数
四次挥手: 从下图看 SERVER B 收到 SERVER A的关闭连接请求后, 把ACK 跟 FIN 分开发送, 因为延迟确认的存在, 第二步的ACK跟第三步的FIN包 经常会合在一起发送给对端(三次挥手)
当 SERVER A 收到 SERVER B 的 FIN 以后, 只能确认SERVER B 不会再发送数据给自己了,但是自己这个时候还是可以发送数据给 SERVER B,这个时候的状态也叫 FINWAIT_1
当 SERVER A 收到 SERVER B 的 FIN 以后回传给 SERVER B 确认ACK包,表示我已经收到这个FIN包了,同时自己进入了 CLOSING 状态中
等待 SERVER A 给自己发 自己FIN包 的 ACK(确保对面知道自己要关闭连接了),如果收到以后就会进入TIME-WAT
在TIME-WAIT 状态持续 2*MSL后 关闭连接