SOCK_STREAM和SOCK_DGRAM到底有什么区别?

上一节《套接字有哪些类型》提到,Stream Sockets就是SOCK_STREAM,它基于TCP协议;Datagram Sockets就是SOCK_DGRAM,它基于UDP协议。
这给大家造成一种印象,Stream Sockets就是可靠连接,Datagram Sockets就是不可靠通信,实际情况真的如此吗?

另外,不管哪种数据传输方式,都得通过整个Internet网络的物理线路将数据传出去,从这个层面理解,所有的socket都是有物理连接的,为什么还有无连接的socket呢?

从字面上理解,Stream Sockets好像有一条管道,它连接发送端和接收端,数据包通过这条管道来传输。当然,两台计算机在通信之前必须先搭建好管道。
Datagram Sockets像是无头苍蝇乱撞,数据包从发送端到接收端没有固定的路线,爱怎么走就怎么走,只要能到达就行。每个数据包都比较自私,不和别人分享自己的线路,但是大家都能殊途同归,到达接收端。

上图是一个简化的互联网模型,H1->H6表示计算机,A->E表示路由器,发送端发送的数据必须经过路由器的转发才能达到接收端。

假设H1要发送若干个数据包给H6,那么有多条路径可以选择,比如:

  • 路径1:H1–>A–>C–>E–>H6
  • 路径2:H1–>A–>B–>E–>H6
  • 路径3:H1–>B–>D–>E–>H6
  • 路径4:H1–>A–>B–>C–>E–>H6
  • 路径5:H1–>A–>C–>B–>D–>E–>H6

数据包的传输路径是由路由器根据算法出来的,算法会考虑很多因素,比如网络的拥堵状况、下一个路由器是否忙碌等。

Datagram Sockets

对于Datagram Sockets,每个数据包可以选择不同的路径,比如第一个数据包选择路径4,第二个数据包选择路径1,第三个数据包选择路径2…当然,它们也可以选择相同的路径,那也只不过是巧合而已。
每个数据包之间都是独立的,各走各的路,谁也不影响谁,除了迷路的或者发生意外的数据包,最后都能到达H6.但是,到达的顺序是不确定的,比如:

  • 第一个数据包选择了一条比较长的路径(比如路径5),第三个数据包选择了一条比较短的路径(比如路径1),虽然一个数据包很早就出发了,但是走的路比较远,最终还是晚于第三个数据包达到。
  • 第一个数据包选择了比较短的路径(比如路径1),第三个数据包选择了一条比较长的路径(比如路径5),按理说第一个数据包应该先到达,但是非常不幸,第一个数据包走的路比较拥堵,这条路上的数据量非常大,路由器处理的很慢,所以它还是晚于第三个数据包达到了。

还有一些意外情况会发生,比如:

  • 第一个数据包选择了路径1,但是路由器C突然断电了,那他就到不了H6了
  • 第三个数据包选择了路径2,虽然路不远,但是太拥堵,以至于他等待的时间太长,路由器把它丢弃了。

总之,对于Datagram Sockets,数据包在传输过程中会发生各种不测,也会发生各种奇迹。H1只负责把数据包发出,至于她什么时候到达,先到达还是后到达,有没有成功到达,H1都不管了;H6也没有选择的权利,只能被动接收,收到什么算什么,爱用不用。

Datagram Sockets遵循的是【尽最大努力交付】的原则,就是尽力而为,实在做不了也没办法。Datagram Sockets提供的没有质量保证的服务。

Stream Sockets

Stream Sockets在正式通信之前要先确定一条路径,没有特殊情况的话,以后就固定地使用这条路径来传递数据包了。当然,路径被破坏的话,比如某个路由器断电了,那么就会重新建立路径。

这条路径是由路由器维护的,路径上所有的路由器都要存储该路径地信息(实际上只需要存储上游和下游两个路由器地位置就行),所以路由器是有开销的。
H1和H6通信完毕后,要断开连接,销毁路径,这个时候路由器也会把之前存储的路径信息擦除。

在很多网络通信教材中,这条预先建立好的路径被称为“虚电路”,就是一条虚拟的通信电路。

为了保障数据包准确、顺序地到达,发送端在发送数据包后,必须得到接收端地确认才发送下一个数据包;如果数据包发出去了,一段时间以后仍没有得到接收端地回应,那么发送端会重新再发送一次,直到得到接收端的回应。这样一来,发送端发送的所有数据包都能到达接收端,并且是按照顺序到达的。

发送端发送一个数据包,如何得到接收端的确认呢?很简单,为每个数据包分配一个ID,接收端接收到数据包以后,再给发送端返回一个数据包,告诉发送端我接收到了ID为xxxxx的数据包。

Stream Sockets会比Datagram Sockets多出很多数据包,因为发送端每发送一个数据包,接收端就会返回一个数据包。此外,建立连接和断开连接的过程也会传递很多数据包。

不但是数量多了,每个数据包也变大了:除了源端口和目的端口,Stream Sockets还包括序号、确认信号、数据偏移、控制标志(通常说的URG、ACK、PSH、RST、SYN、FIN)、窗口、检验和紧急指针、选项等信息;Datagram Sockets则只包含长度、校验和信息。

Stream Sockets的数据包比Datagram Sockets大很多,这意味着更大的负载和更大的带宽。许多即时聊天软件采用UDP协议(Datagram Sockets),与此有莫大的关系。

总结:

  • Datagram Sockets传输效率高,但是不可靠,有丢失数据包、捣乱数据的风险
  • Stream Sockets非常可靠,万无一失,但是传输效率低,耗费资源多。

两种套接字的特点决定了他们的应用场景,有些服务对可靠性要求比较高,必须数据包能完整无误地送达,那就得选择Stream Sockets(TCP服务),比如HTTP、FTP等;而另一些服务,并不需要那么高的可靠性,效率和实时才是它们所关心的,那就可以选择Datagram Sockets(UDP服务),比如DNS、即时聊天工具等。