我所不知道的TCP Socket编程(六)-高级内容简介

 六:高级内容简介:

     

     接下来介绍的几个较高级的内容,便于我们更好的立理解Socket连接中消息传输的诸多场景(较少的一部分,其它的高级内容用到的同学自己研究吧,如套接字选项、连接复用、网络架构、线程池、混合模式等):

     

     6.1 缓冲

     

     有下面几个问题:

     1)在一次调用中应该读/写多少数据?

     2)如果write成功返回,是否意味着连接的另一端已经接收了数据?

     3)是否应该将一个大数据量的write分割成多个小数据量进行多次写入?这样会造成怎样的影像?

     

     我们会一一作出回答;

     

     6.1.1 写缓冲

     

     TCP连接上调用write进行写操作时究竟发生了什么?

     1)当你调用write并返回时,就算无异常,也并不意味着数据已经通过网络顺利发送并被客户端套接字接收到;

     write返回时,只是表明你已经将数据提交给了Ruby的IO系统和底层的操作系统内核;

     2)在应用程序和实际网络硬件之间至少存在一个缓冲层;

     在write返回时,数据交到了操作系统内核手中,他可以立即发送,也可以出于效率考虑暂时不发送,将其同别的数据进行合并;

     TCP套接字默认将sync设置为true,这就跳过了Ruby的内部缓冲,否则就又要多处一个缓冲层了;

     

     为何需要缓冲区:

     所有的IO缓冲都是出于性能的考虑;

     1)缓冲使得write调用可以立即返回,幕后再由内核将所有未执行的写操作汇总,在发送时分组及优化,在实现最佳性能的同时避免网络过载;

     2)在网络层面上,发送大量小分组会引发客观的开销,因此内核会将多个小数据量的写操作合并成较大数据量的写操作;

     

     6.1.2 该写入多少数据

     

     那么,是否应该将一个大数据量的write分割成多个小数据量进行多次写入?

     

     幸好我们有缓冲去,通常不需要考虑这个问题,获得最佳性能的方法是一口气写入所有的数据,让内核决定如何对数据进行结合;

     如果你要做一个相当大数据量的write,比如文件或大数据写入,那最好将这些数据进行分割,避免全部载入内存中,显然,我们唯一需要做的就是优化我们的应用程序;

     

     6.1.3 读缓冲

     

     读操作同样会被缓冲;

     

     如果调用read从TCP连接中读取数据并传给它一个最大读取长度,Ruby实际上可能会接收大于你指定长度的数据;

     此时,“多处的”数据会被存储在Ruby内部的读缓冲区中,在下次调用read时,Ruby会先看看自己内部缓冲区中有没有未读数据,然后再通过操作系统内核请求更多的数据;

     

     6.1.4 该读取多少数据

     

     因为TCP提供的是数据流,无法得知发送方到底发送了多少数据,所以在决定读取长度时,只能猜;

     

     一个较大的读取长度可以确保总是可以得到所有可用的数据,但同时,系统会为我们分配内存,如果用不着那么多,就会造成资源浪费;

     一个较小的读取长度,就需要多次才能读取完全部的数据,这会导致每次系统调用引发的大量开销;

     

     所以,需要根据应用程序所要接收的数据大小进行调优;

     在各类使用套接字的Ruby项目中,多采用readpartial(1024*16),即16KB作为各自的读取长度;

     

     你总可以通过调优服务器来适应当下的数据量以获得最佳性能,犹豫时,16KB是个不错的选择。

     


     6.2 消息划分


     现在又一个问题:如何将服务器与客户端之间交换的消息进行格式化?

     

     目前位置,我们可以使用EOF(由close触发)来表明消息的终止,但是如果想在同一个TCP连接上发送多个消息,就需要确定一致的方式来划分消息的起始位置;

     事实上,有无数种方法可以用于在消息间进行划分;一些很复杂,一些很简单;取决于你想如何格式化消息;

     

     协议与消息:

     消息和协议并不是一回事,比如,http协议既定义了消息边界(连续的新行),又定义了用于消息内容(涉及请求行、头部等)的协议;

     协议定义了应该如何格式化消息;

     

     6.2.1 使用新行

     

     使用新行:

     Unix系统中是\n,Windows系统中则是\r\n;

     

     现实中实用新行划分消息的协议是HTTP;

     

     6.2.2 使用内容长度

     

     指定内容长度(content length):

     使用这种方法,消息发送需要先计算出消息的长度;

     1)使用pack将其转换为固定宽度的整数,后面跟上消息主体一并发送;

     2)消息接收方首先读取(read)这个长度,然后unpack,获得这个长度值;

     3)这样就知道了消息的大小,然后接收方严格读取长度值所指定的字节数,获取完成的消息;

     

     客户端用pack将消息长度转换成一个与处理器要求相一致的整数,这点很重要,因为他确保了任何给定的整数都会被转化为同样数量的字节;

     

     6.3 超时

     

     超时其实就是忍耐;你愿意在套接字连接上等待多长时间呢?套接字读取呢?套接字写入呢?比如5s;

     

     6.4 DNS查询

     

     超时可以让你很好的控制代码,但是有些地方就没法那么随心所欲了;

     

     # ./code/snippets/client_easy_way.rb
     require 'socket'
     socket = TCPSocket.new('google.com',80)

     

     我们知道Ruby在构造函数内部调用了connect;

     我们传入了主机名而非IP地址,Ruby需要查询DNS将主机名解析成可以连接的IP地址;

     一个缓慢的DNS服务器会阻塞整个Ruby进程;

     

     6.5 SSL套接字

     

     SSL使用公钥加密提供了一套用于在套接字上进行安全的数据交换机制;

     SSL套接字并没有取代TCP套接字,而是将不安全的套接字“升级”到安全的SSL套接字;(如果你愿意,可以在TCP套接字之上再添加一个安全层)

     

     注意,一个套接字可以升级SSL,但是一个套接字不能同时进行SSL和非SSL通信;使用SSL时,端到端的通讯必须全部都使用SSL完成,否则无法保证安全;

     

     对于那些既需要在SSL上又需要在不安全的TCP上运行的服务,需要使用两个端口(两个套接字);

     HTTP就是一个常见的例子:不安全的HTTP默认使用端口80,而HTTPS(运行在SSL之上的HTTP)通讯默认是在端口443上;

     

     所有的TCP套接字都可以转换成SSL套接字;(在Ruby中这通常使用标准库中的openssl实现)

     会有一个TCP套接字到SSL套接字的包装器,这个过程需要一个SSL证书,在典型的产品设置中,不会让你去生成自签名证书(只适用于开发/测试);通常需要从一个可信任机构购买的证书;该机构会提供给你用于安全通信的cert和key;

     

     总结:

     

     到目前为止,我最初想要学习的目标达到了,这个系列的文章就留给大家;毕竟本文只是作为了解TCP Socket的基础性文章,所以一些Ruby语言下高级使用场景,还需要大家自己去学习和实践,祝大家在技术的道路上不畏艰险,勇往直前。






  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值