SOCK_STREAM与SOCK_DGRAM套接字

一、流格式套接字(SOCK_STREAM)

流格式套接字(Stream Sockets)也叫“面向连接的套接字”,在代码中使用 SOCK_STREAM 表示。
SOCK_STREAM 是一种可靠的、双向的通信数据流,数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送。

流格式套接字有自己的纠错机制,在此我们就不讨论了。

SOCK_STREAM 有以下几个特征:

  1. 数据在传输过程中不会消失;
  2. 数据是按照顺序传输的;
  3. 数据的发送和接收不是同步的(有的教程也称“不存在数据边界”)。

可以将 SOCK_STREAM 比喻成一条传送带,只要传送带本身没有问题(不会断网),就能保证数据不丢失;同时,较晚传送的数据不会先到达,较早传送的数据不会晚到达,这就保证了数据是按照顺序传递的。

传送带

为什么流格式套接字可以达到高质量的数据传输呢?这是因为它使用了 TCP 协议(The Transmission Control Protocol,传输控制协议),TCP 协议会控制你的数据按照顺序到达并且没有错误。

你也许见过 TCP,是因为你经常听说“TCP/IP”。TCP 用来确保数据的正确性,IP(Internet Protocol,网络协议)用来控制数据如何从源头到达目的地,也就是常说的“路由”。

那么,“数据的发送和接收不同步”该如何理解呢?

假设传送带传送的是水果,接收者需要凑齐 100 个后才能装袋,但是传送带可能把这 100 个水果分批传送,比如第一批传送 20 个,第二批传送 50 个,第三批传送 30 个。接收者不需要和传送带保持同步,只要根据自己的节奏来装袋即可,不用管传送带传送了几批,也不用每到一批就装袋一次,可以等到凑够了 100 个水果再装袋。

流格式套接字的内部有一个缓冲区(也就是字符数组),通过 socket 传输的数据将保存到这个缓冲区。接收端在收到数据后并不一定立即读取,只要数据不超过缓冲区的容量,接收端有可能在缓冲区被填满以后一次性地读取,也可能分成好几次读取。

也就是说,不管数据分几次传送过来,接收端只需要根据自己的要求读取,不用非得在数据到达时立即读取。传送端有自己的节奏,接收端也有自己的节奏,它们是不一致的。

流格式套接字有什么实际的应用场景吗?浏览器所使用的 http 协议就基于面向连接的套接字,因为必须要确保数据准确无误,否则加载的 HTML 将无法解析。

二、数据报格式套接字(SOCK_DGRAM)

数据报格式套接字(Datagram Sockets)也叫“无连接的套接字”,在代码中使用 SOCK_DGRAM 表示。
计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。

因为数据报套接字所做的校验工作少,所以在传输效率方面比流格式套接字要高。

SOCK_DGRAM 有以下几个特征:
1.强调快速传输而非传输顺序;
2.传输的数据可能丢失也可能损毁;
3.限制每次传输的数据大小;
4.数据的发送和接收是同步的(有的教程也称“存在数据边界”)。

众所周知,速度是快递行业的生命。用摩托车发往同一地点的两件包裹无需保证顺序,只要以最快的速度交给客户就行。这种方式存在损坏或丢失的风险,而且包裹大小有一定限制。因此,想要传递大量包裹,就得分配发送。

另外,用两辆摩托车分别发送两件包裹,那么接收者也需要分两次接收,所以“数据的发送和接收是同步的”;换句话说,接收次数应该和发送次数相同。

总之,数据报套接字是一种不可靠的、不按顺序传递的、以追求速度为目的的套接字。

数据报套接字也使用 IP 协议作路由,但是它不使用 TCP 协议,而是使用 UDP 协议(User Datagram Protocol,用户数据报协议)。

QQ 视频聊天和语音聊天就使用 SOCK_DGRAM 来传输数据,因为首先要保证通信的效率,尽量减小延迟,而数据的正确性是次要的,即使丢失很小的一部分数据,视频和音频也可以正常解析,最多出现噪点或杂音,不会对通信质量有实质的影响。

其实SOCK_DGRAM 没有想象中的糟糕,不会频繁的丢失数据,数据错误只是小概率事件。

三、面向连接和无连接的套接字到底有什么区别?

流格式套接字(Stream Sockets)就是“面向连接的套接字”,它基于 TCP 协议;数据报格式套接字(Datagram Sockets)就是“无连接的套接字”,它基于 UDP 协议。

这给大家造成一种印象,面向连接就是可靠的通信,无连接就是不可靠的通信,实际情况是这样吗?

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

从字面上理解,面向连接好像有一条管道,它连接发送端和接收端,数据包都通过这条管道来传输。当然,两台计算机在通信之前必须先搭建好管道。

无连接好像没头苍蝇乱撞,数据包从发送端到接收端并没有固定的线路,爱怎么走就怎么走,只要能到达就行。每个数据包都比较自私,不和别人分享自己的线路,但是,大家最终都能殊途同归,到达接收端。
无连接套接字

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

假设 H1 要发送若干个数据包给 H6,那么有多条路径可以选择,比如:
路径①:H1 → A → C → E → H6
路径②:H1 → A → B → E → H6
路径③:H1 → A → B → D → E → H6
路径④:H1 → A → B → C → E → H6
路径⑤:H1→ A → C --> B → D → E→ H6

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

无连接的套接字

对于无连接的套接字,每个数据包可以选择不同的路径,比如第一个数据包选择路径④,第二个数据包选择路径①,第三个数据包选择路径②等等。当然,它们也可以选择相同的路径,那也只不过是巧合而已。

每个数据包之间都是独立的,各走各的路,谁也不影响谁,除了迷路的或者发生意外的数据包,最后都能到达 H6。但是,到达的顺序是不确定的,比如:
1.第一个数据包选择了一条比较长的路径(比如路径⑤),第三个数据包选择了一条比较短的路径(比如路径①),虽然第一个数据包很早就出发了,但是走的路比较远,最终还是晚于第三个数据包达到。
2.第一个数据包选择了一条比较短的路径(比如路径①),第三个数据包选择了一条比较长的路径(比如路径⑤),按理说第一个数据包应该先到达,但是非常不幸,第一个数据包走的路比较拥堵,这条路上的数据量非常大,路由器处理得很慢,所以它还是晚于第三个数据包达到了。

还有一些意外情况会发生,比如:
1.第一个数据包选择了路径①,但是路由器C突然断电了,那它就到不了 H6 了。
2.第三个数据包选择了路径②,虽然路不远,但是太拥堵,以至于它等待的时间太长,路由器把它丢弃了。

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

无连接套接字遵循的是「尽最大努力交付」的原则,就是尽力而为,实在做不到了也没办法。无连接套接字提供的没有质量保证的服务。

面向连接的套接字

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

有连接套接字.gif

这条路径是由路由器维护的,路径上的所有路由器都要存储该路径的信息(实际上只需要存储上游和下游的两个路由器的位置就行),所以路由器是有开销的。

H1 和 H6 通信完毕后,要断开连接,销毁路径,这个时候路由器也会把之前存储的路径信息擦除。

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

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

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

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

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

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

两种套接字各有优缺点:
1.无连接套接字传输效率高,但是不可靠,有丢失数据包、捣乱数据的风险;
2.有连接套接字非常可靠,万无一失,但是传输效率低,耗费资源多。

参考文章

  1. 套接字有哪些类型?socket有哪些类型?
  2. 套接字有哪些类型?socket有哪些类型?
  3. 面向连接和无连接的套接字到底有什么区别?
  • 7
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值