POSIX API 是一组标准的编程接口,用于在 Unix 和类 Unix 系统上开发可移植的应用程序。它包括系统调用、库函数、环境变量、信号处理、线程支持等。通过使用 POSIX API,开发者可以编写跨平台的代码,减少在不同系统之间的移植成本。
在网络编程中我们的服务端离不开POSIX API中的socket()、bind()、linsten()、accpet()、recv()、send()、close();客户端则需要socket()、bind()、connect()、send()、recv()、close()。通过这些API我们就可以利用TCP/IP进行网络通信。
在使用TCP/IP进行网络通信的的时候,会经历三个阶段:建立连接,传输数据,断开连接。
1、socket():
socket()
函数用于创建一个套接字,它会分配一个文件描述符(FD),并初始化一个底层的 TCP 控制块(TCP Control Block,TCB)。
2、bind():
bind()函数,通过fd将地址和端口号找到对应的TCP控制块进行绑定。
3、listen():
(1)、tcb->status=TCP_STATUS_LISTEN将对应的tcp设置成监听状态
(2)、tcb->syn_queue和tcb_accpet_queue分配半连接和全连接队列
4、进行连接:
进行三次握手:客户端发送syn请求(c_seqnum),服务端回发ack应答和syn请求(c_seqnum+1和s_seqnum),客户端再回发ack应答(s_seqnum+1),为了避免数据包被随意捕获两个生成的seqnum是随机的,ack应答的确位号实在生成的seqnum上加1。
三次握手发生时机:客户端connect()函数发起请求,服务端listen()建立半连接为客户端分配一个tcp控制块,服务端应答和请求进行二次握手,客户端connect应答三次握手完成,随后accpet()从连接队列move出建立好的连接并返回一个fd。
注意:tcp连接的生命周期在第一次连接的请求被监听到的时候开始;通过源端口目的断开,源IP和目的IP可以在半连接队列找到匹配的节点;lisnten(int fd,int backlog)第二个参数有三个版本:1.syn队列长度、2.syn+accept队列总长度,未分配fd的tcb的数量,3.accept队列的长度(accpet队列长度可以提高接入量)。
5、accept():
accpet()函数分配一个fd,并将这个fd和连接建立的tcp控制块关联。
6、send()和recv():
send()会将数据从应用程序copy到内核,对方协议栈接受完数据后,调用recv函数会把数据从内核copy到应用程序。send一次发送的数据大小和MTU单元有关,默认是1500,发送一次MTU单元可能应用程序调用了多次send(),MTU单元的接受和TCP头中的确位好和序号有关。(TCP/IP协议通过慢启动,拥塞控制,滑动窗口,延迟确认,超时重传确保数据高效传输)
7、close():
close()会回收fd,发送fin包给对端。
断开连接:发生四次挥手:主动方fin请求(fin_wait1),被动方ack应答(fin_wait2,close_wait),被动方fin请求(last_ack),主动方ack应答(time_wait)。