随意的连接关闭:
“恋人的誓言是传不到上帝的耳边的”,同样客户端与服务器的关系也是这么不靠谱,服务器和客户端都可以随时关闭连接,所以就有可能造成报文发送一半连接就被关闭的悲剧。对于持久连接来说更是如此,普通的连接还有连接随着报文传输完成而关闭的默契,而持久连接只要一端觉得好长时间没有使用就可能关闭连接,甚至在任意时刻都可能会关闭连接,而这时如果另一端正在发送报文就会出现错误。虽然持久连接会交换保持时间和希望传输报文次数等首部,但是这是非承诺值,也就是说我大概会这么做,但是如果我没有这么做,也没有问题。
content-length的重要性:
对于普通的连接来说,判断报文结束的条件是连接的关闭。也就是说在如果连接关闭了,那就是告诉接收端这个报文结束了。如果是持久连接的话,就无法使用这种方法了,这个时候content-length就派上用场了。在持久连接上传输时,content-length说明了实体的长度,接收端通过是长度判断哪里是报文哪里结束,从而对报文进行截断。如果content-length出现错误或者content-length没有提供,那么客户端就无法判断报文的结束在哪里,传输便出现错误。所以说在使用持久连接进行报文传输时,一定要提供正确的content-length。
连接关闭导致的重试,以及方法的幂等性:
因为连接是这么的不靠谱,所以发送端要随时做好在传输过程中遇到连接突然关闭情况的准备,也就是发送失败的话需要进行重新发送。而重新发送的话就可能遇到问题:有些事务即使重新发送也不会有问题,例如get,head,put,delete,options,trace等正常情况下方法重复多次发送的话也不会出现问题,我们称之为是幂等,而post等方法发送多次的话就可能出问题,这些事务我们就称之为非幂等,举个我们经常遇到的例子,当我们在逛淘宝的时候,有时刷新页面的时候会弹出提示,说该页面不可以重复提交,这个就是因为有些数据如果重复提交的话就会遇到问题。比如你买了一件衣服,然后重复提交了一次,可能服务器最后收到的就是你要买两件衣服。
正常的关闭
应用程序可以选择关闭tcp输入和输出信道的任意一个,或者两个都关闭,如果只关闭一个信道就称之为“半关闭”。简单的http程序可以只使用完全关闭,而有些复杂的对话且使用了持久连接的话,用半关闭来防止对等端遇到错误就十分重要了。
首先关闭连接的输出信道是很安全的。当连接另一端的对等实体从其缓冲区读出所有的数据之后会收到一条通知,告诉它流结束了。关闭连接的输入信道则比较危险,除非你知道另一端不会再发送信息了。让我们看看如果先关闭输入信道会有什么严重后果:
- 客户端已经在持久连接上发送了 10条请求了,而且这十条请求的响应也已经收到,并在缓冲区保存。
- 这时客户端发送了第11条请求,而服务器突然觉得你已经用的够多了吧,就把服务器的输入信道关闭了,当你的请求在已经关闭的发送时,那么服务器就会回送一条重置信息,这条信息会重置客户端的缓冲区,而这时候的缓冲区还缓冲着你前十条请求的响应呢!!!
- 当最后客户端去缓冲区读取数据的时候,会得到一个连接被重置的错误,同时已经缓存的相应数据都丢了,尽管之前客户端已经接到了这些响应。
那么怎么正确的关闭连接呢?
http规范建议当要突然关闭连接时,应该“正常的关闭连接”,但是它也没说怎么关闭是正确的...
总而言之,要关闭一个连接,想要关闭的一端应先关闭自己的输出信道,等待另一端关闭它的输出信道,当两遍都告诉对方不会发送任何信息之后,再关闭输入信道,这样连接就完全关闭了,而且也不会出现问题。
但是可控的只能是自己的这端,我们没有办法控制另一端也会这么做,因此如果想要关闭一条连接的话,应该先关闭己端的输出信道,然后周期性的对己端的输入信道进行检查,如果在一段时间之内没有数据传输,那么就可以关闭连接。