天天看点

Socket高可靠性数据传输

在计算机网络中,TCP/IP保证了数据的可靠性传输,但是该可靠性传输时建立在链路可用的情况下的,也就是说在链路可用的情况下,该协议可用保证数据可靠的传输到对端。

socket就是在TCP/IP协议(当然还包含其他协议)之上的更高层次的系统API,socket底层使用了TCP/IP来保证了链路在可用的情况下,数据可靠的传输到对端,那么应用层在使用socket的时候读写数据的时候就一定能保证数据可靠的传输了吗?答案是:否,为什么会这样?下面将通过分析socket的实现机制来进行相应的说明。

我们通过对linux系统中的socket的数据流传输机制的分析来说明为什么直接使用socket机制来传输数据是不可靠的。socket的使用相信大家都不会陌生,服务端监听端口,客户端指定端口连接到服务器端,然后就可以进行数据的传输了,但是却很少关系我们在调用socket的send方法之后,我们的数据是怎么样被发送到对端的?

在了解数据传输之前,需要去了解一下linux的用户态和内核态的概念。

socket在调用send方法之后,用户空间的数据会被拷贝到内核空间的socket的缓冲区(可以同socket的OPTION设置)(如果使用DMA可以减少该过程)(内核缓冲区是内核为socket分配的内存,用于缓存需要发送的数据),而且可以通过设置TCP_NODLAY来设置是否即时发送内核缓冲区的数据到对端(默认是关闭的,至于为什么关闭这个网上有很多文章说明,最主要是提高数据有效载荷)。如果在使用socket进行数据传输的时候没有设置该选项,那么就会出现一个问题,应用层多个业务多次send之后,那几次调用发送的数据可能仅仅是被存放在内核的缓冲区,而没有被立刻发送到对端,但是应用层的却认为发送成功,然后使用了应用层的缓冲区,在这个时候如果出现网络连接断开,那么内核中已经被缓存的数据将丢失,而且应用层无法知道哪些数据发送失败了,这种情况就需要应用层自己设计一个可靠的传输机制,对于每一个发送出去的request包都对于一个应用层的一个response包,对没有收到response的包,默认是失败了,在断线重连之后重发。

上述的断线重连消息重发会存在服务器端重复处理同一个请求多次的问题,这个问题可以通过在协议中设置一个消息ID和type标记,type用于标记该消息是否是重发消息,ID是消息的唯一标记,通过这两个标识符,服务器端首先检查type是否为重发消息,如果为重发消息就去已处理的消息ID的容器中去找,如果找到那么就返回客户端“该消息已被处理”,如果没有找到那么就重新处理消息,并将消息ID加入消息容器中。

下面推荐一篇讲述socket数据传输机制的文档,里面有讲述数据从用户态到内核态的过程。

https://www.ibm.com/developerworks/linux/library/j-zerocopy/