Socket是应用层与传输层的一个抽象,将复杂的TCP/IP协议隐藏在Socket接口之后,只对应用层暴露简单的接口。
1.针对TCP该如何Socket编程
- 服务端和客户端初始化Socket,得到文件描述符
- 服务端调用bind,将绑定IP地址和端口
- 服务端调用listen,进行监听
- 服务端调用accept,等待客户端连接
- 客户端调用connect,向服务器端的地址和端口发送连接请求
- 服务端accept返回用于传输的socket的文件描述符
- 客户端调用write写入数据,服务端调用read读取数据
- 客户端断开连接时,会调用close,那么服务端read读取数据的时候,就会读到了EOF,黛处理完数据后,服务端调用close,表示连接关闭
这里需要主要的是,服务端调用accept时,连接成功了会返回一个已经完成连接的socket,后续用来传输数据。
所以,监听的socket和真正用来传送数据的socket,是两个socket,一个叫做监听socket,一个叫做已完成连接socket。
成功连接建立之后,双方开始通过read和write函数来读写数据,就像一个文件流里面写东西一样。
2.listen时候参数backlog的意义
Linux内核中会维护两个队列:
- 未完成连接队列(SYN队列):接收到一个SYN连接请求,处于SYN_RCVD状态
- 已完成连接队列(Accept队列):已完成TCP三次握手过程,处于ESTABLISHED状态
int listen(int socketfd,int backlog)
- 参数一socketfy为socketfd文件描述符
- 参数二backlog,这个参数在历史版本有一定的变化
在早期的Linux内核backlog是SYN队列的大小
在Linux内核2.2之后,backlog变成accept队列,也就是已完成连接建立的队列长度,所以现在通常认为backlog是accept队列。
但是上限值是内核参数somaxconn的大小,也就是锁accept队列长度=min(backlog,somaxconn)
3.accept发送在三次握手的哪一步
客户端connect成功返回是在第二次握手,服务端accept成功返回是在三次握手成功之后。
4.客户端调用close了。连接时断开的流程是什么?
- 客户端调用close,表面客户端没有数据需要发送了,则此时会向服务端发送FIN报文,进入FIN_WAIT1状态
- 服务端接收到FIN报文,TCP协议栈会为FIN包插入一个文件结束符EOF到接收缓冲区中,应用程序可以通过read调用来感知这个FIN包。这个EOF会被放在已排队等候的其他已接收的数据之后,这就意味着服务端需要处理这种异常情况,因为EOF表示该连接上再无额外的数据到达。此时,服务端进入CLOSE_WAIT 状态。
- 接着,当处理完数据后,自然会读到EOF,于是也调用close关闭它的套接字,这会使客户端会发出一个FIN包,之后处于LAST_ACK状态
- 服务端收到ACK确认包后,就进入了最后的CLOSE状态
- 客户端经过2MSL时间之后,也进入CLOSE状态