1. 从Socket连接说起
经过3次握手后,Socket连接的两端会创建各自的发送队列(send-q)、接收队列(recv-q),之后想要关闭连接只有两种可能:
1. 四次挥手
- 主动关闭端,确认自己已经发送完数据后,主动发送FIN包
- 被动关闭端,收到主动端的FIN包后,发送ACK包,不会再给主动端发送数据
- 被动关闭端,继续读取主动端的数据,遇到EOF后,发送FIN包给主动端,清理自己的缓冲区
- 主动关闭端,收到被动端的FIN包,响应ACK包,清理自己的缓冲区
2. 发送RST包
- 一端向另一端发送数据的时候,如果数据接收端没有连接信息(没有缓冲区),接收端会丢失数据包,并响应RST包
- 程序异常崩溃,操作系统会丢弃连接,没有四次挥手的过程
- 收到和当前连接不匹配的数据段,响应RST包
- 拒绝进一步通信,响应RST包
2. Java中的异常
1. 读取异常
- SocketTimeoutException
- 设置了SoTimeout,超时未读取到数据
- 三次握手超时,设置了connectionTimeout
- SocketException: Connection reset
- 对方响应RST包
- SocketException: socket is closed
- 本地已调用Socket.close()方法
- SocketException: Socket is not connected
- 连接未建立
2. 写入异常
- SocketException: broken pipe (Linux) 或 SocketException: Connection reset by peer (Windows)
- 对方已发送FIN包
- 对方响应RST包
- SocketException: socket is closed
- 已调用Socket.close()方法
- SocketException: Socket is not connected
- 连接未建立
3. Tomcat异常
文件上传时,浏览器和服务端建立连接,读取并发送文件的内容,Servlet容器接收数据后写入本地文件,封装为对象传递给Servlet实现类。这个过程中可能的异常有:
1. 网络异常
- 客户端崩溃,发送RST包,导致服务端 SocketException: Connection reset
- 读取超时,导致服务器 SocketTimeoutException
- 服务端响应数据给客户端时,客户端连接已经不存在(FIN或RST), SocketException: broken pipe 或 Connection reset by peer
- 客户端主动断开连接,导致服务 ClientAbortException
2. 数据异常
- 上传的格式不符合协议,导致Tomcat的协议解析错误,比如Http11Processor解析错误
- 服务端不知道怎么解析文件,FileUploadException
- 请求大小超限,SizeLimitExceededException
- Content-Type不能识别,InvalidContentTypeException
- 文件大小超限,FileSizeLimitExceededException
2. 系统资源
- 磁盘空间不足或者没有写入权限等,比如抛出 IOException: No space left on device
- 内存溢出,OutOfMemoryError
- Servlet无可用线程,
4. 服务端超时时间
如果使用的是Tomcat,可以设置连接超时时间和读入超时时间
server:tomcat:# 设置连接超时(单位:毫秒)connection-timeout: 5000# 设置读取超时(单位:毫秒)socket-timeout: 5000
如果是Spring Cloud Gateway,可以通过如下设置,配置Gateway和后端服务器的超时时间
spring:cloud:gateway:httpclient:connect-timeout: 15000 response-timeout: 25000