详解Socket接⼝
要学Internet上的TCP/IP⽹络编程,必须深刻理解Socket接⼝。之所以另起⼀篇⽂章来讨论Socket,是因为它是⽹络通信架构的基础,重要性不⾔⽽喻。所谓socket通常也称作“套接字”,⽤于描述IP地址和端⼝,是⼀个通信链的句柄。应⽤程序通常通过“套接字”向⽹络发出请求或者应答⽹络请求。socket的英⽂原义是“孔”或“插座”。在这⾥作为通信机制,取后者意思。socket⾮常类似于电话插座。以⼀个国家级电话⽹为例。电话的通话双⽅相当于相互通信的2个进程,区号是它的⽹络地址;区内⼀个单位的交换机相当于⼀台主机,主机分配给每个⽤户的局内号码相当于socket号(JAVA程序的socket ID 由操作系统分配)。任何⽤户在通话之前,⾸先要占有⼀部电话机,相当于申请⼀个socket;同时要知道对⽅的号码,相当于对⽅有⼀个固定的socket。然后向对⽅拨号呼叫,相当于发出连接请求(假如对⽅不在同⼀区内,还要拨对⽅区号,相当于给出⽹络地址)。对⽅假如在场并空闲(相当于通信的另⼀主机开机且可以接受连接请求),拿起电话话筒,双⽅就可以正式通话,相当于连接成功。双⽅通话的过程,是⼀⽅向电话机发出信号和对⽅从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。通话结束后,⼀⽅挂起电话机相当于关闭socket,撤消连接。
在电话系统中,⼀般⽤户只能感受到本地电话机和对⽅电话号码的存在,建⽴通话的过程,话⾳传输的过程以及整个电话系统的技术细节对他都是透明的,这也与socket机制⾮常相似。socket利⽤⽹间⽹通信设施实现进程通信,但它对通信设施的细节毫不关⼼,只要通信设施能提供⾜够的通信能⼒,它就满⾜了。
⾄此,我们对socket进⾏了直观的描述。抽象出来,socket实质上提供了进程通信的端点。进程通信之前,双⽅⾸先必须各⾃创建⼀个端点,否则是没有办法建⽴联系并相互通信的。正如打电话之前,双⽅必须各⾃拥有⼀台电话机⼀样。在⽹间⽹内部,每⼀个socket⽤⼀个结构体相关描述:(协议,本地地址,本地端⼝) ⼀个完整的socket有⼀个本地唯⼀的socket号
最重要的是,socket 是⾯向客户/服务器模型⽽设计的,针对客户和服务器程序提供不同的socket 系统调⽤。客户随机申请⼀个socket (相当于⼀个想打电话的⼈可以在任何⼀台⼊⽹电话上拨号呼叫),系统为之分配⼀个socket号;服务器拥有全局公认的 socket ,任何客户都可以向它发出连接请求和信息请求(相当于⼀个被呼叫的电话拥有⼀个呼叫⽅知道的电话号码)。
socket利⽤客户/服务器模式巧妙地解决了进程之间建⽴通信连接的问题。服务器socket 半相关为全局所公认⾮常重要。⼀般来说,编好程序 > 访问,调⽤,数据填充,逻辑组织 > socket组件 > 操作系统的通信API > 到达⽹线出⼝。这样就可以实现与外部的连接。
在⽹络上查socket相关资料的时候,经常会把socket和电话插座拿来对⽐,不仅仅是因为socket的英⽂原义是“插座”,更重要的是,电话机发送信号和对⽅从电话机接收信号的过程,相当于socket发送和接收数据的过程。对于通话双⽅来说,通信设施的细节不重要,重要的是两端都有电话机,也就是都⽀持socket这样的连接⽅式。
webSocket机制
websocket是⼀个基于TCP连接的全双⼯通信⽅式,服务端和客户端可以相互推送数据。
我们可以看看websocket是如何保持客户端和服务端通信的。
这⾥需要注意⼀点,websocket在连接的时候有⼀个握⼿阶段,但是这和TCP的三次握⼿⼜是不⼀样的。TCP的三次握⼿是为了保证连接可靠,当TCP三次握⼿成功的时候,websocket的握⼿阶段才真正开始。TCP三次握⼿传送的是TCP报⽂,⽽websocket的握⼿传送的是HTTP报⽂,这个是不太⼀样的地⽅。
握⼿开始的时候,我们需要现发送⼀个HTTP 1.1的请求头部:
GET /chat HTTP/1.1
Host: ample
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: example
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
服务端返回的成功握⼿请求头部如下:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
Upgrade:WebSocket表⽰这是⼀个特殊的 HTTP 请求,请求的⽬的就是要将客户端和服务器端的通讯协议从 HTTP 协议升级到WebSocket 协议。
⼀旦连接成功后,就可以在全双⼯的模式下在客户端和服务端之间来回传送WebSocket消息。这就意味着,在同⼀时间、任何⽅向,都可以双向发送基于⽂本的消息。每个消息已0×00字节开头,以0xff结尾(这样就可以解决TCP协议中的黏包问题,在TCP协议中,会存在两个缓冲区来存放发送的数据或者接收的数据,如果没有明显的分隔符,服务端⽆法正确识别命令),中间数据的编码是UTF-8。
关于如何使⽤WebSocket也不赘述,主要还是说说WebSocket带来了什么。
WebSocket的优势
传输速度收到的影响很多,我们可以从多个⾓度对HTTP和WebSocket进⾏⽐较。
websocket和socket从纯粹的字节数⾓度考虑
HTTP:每⼀次数据传输都需要有⼀个HTTP头部,头部的⼤⼩不⼀,可能只有⼏百B,也可能有⼏千B。
WebSocket只有在进⾏连接的时候需要发送⼀个HTTP请求,之后就再也不需要发送纷繁的HTTP头部信息,光从字节数上就减少了很多。⽽在关闭WebSocket的过程中,也不需要像建⽴握⼿的时候那么繁杂,只需要传送⼀个特定的字节码0×8的关闭帧就⾏,服务端收到之后,需要响应⼀个关闭帧到客户端。
从请求数的⾓度考虑
正常情况下,如果我们要请求多个数据,就多发多次HTTP请求,整个过程包括建⽴连接,关闭连接,特别是建⽴连接的时间在整个传输时间中还占据了⽐较⼤的⽐重。HTTP长连接的劣势也在上⾯有描述过。
WebSocket可以⼀直保持连接,通过Socket通道传输数据,节省掉了建⽴连接需要耗费的时间。
从服务器并发数的⾓度考虑
服务端要同时维持⼤量连接处于打开状态,就需要能以低性能开销接收⾼并发数据的架构。此类架构通常是围绕线程或所谓的⾮阻塞 IO ⽽设计的。这就与传统服务器围绕 HTTP 请求/响应循环的设计不同。这个时候,我们就会想到nodejs,使⽤事件机制和异步IO对请求进⾏处理,提⾼了服务器的并发能⼒,并且减少了线程切换带来的开销。
Java曾引⼊⼀个新的I/O API,其被称为⾮阻塞式的I/O。这⼀API使⽤⼀个选择器来避免每次有新的HTTP连接在服务器端建⽴时都要绑定⼀个线程的做法,当有数据到来时,就会有⼀个事件被接收,接着某个线程就被分配来处理该请求。因此,这种做法被称为每个请求⼀个线程(thread-per-request)模式。其允许web服务器,⽐如说WebSphere和Jetty等,使⽤固定数量的线程来容纳并处理越来越多的⽤
户连接。在相同硬件配置的情况下,在这⼀模式下运⾏的web服务器的伸缩性要⽐运⾏在每个连接⼀个线程(thread-per-connection)模型下的好得多。
每个连接⼀个线程模式通常会有⼀个更好的响应时间,因为所有的线程都已启动、准备好且是等待中,但在连接的数⽬过⾼时,其会停⽌提供服务。在每个请求⼀个线程模式中,线程被⽤来为到达的请求提供服务,连接则是通过⼀个NIO选择器来处理。响应时间可能会较慢⼀些,但线程会回收再⽤,因此该⽅案在⼤容量连接⽅⾯有着更好的伸缩性。
⽽WebSocket对于服务端的优势就在于Socket减少了数据传输和处理的成本,使得这些异步的IO机制能够充分地扬长避短。
换句话说,WebSocket带来的并发能⼒提升,不仅仅因为传输机制本⾝,服务端⼀样需要做调整来适应新的机制,这样才能充分发挥WebSocket的优势。
Socket.io
Socket.IO是⼀个JavaScript端的框架,提供了⼀个简单类似WebSocket的API,实现异步接收和发送服务端数据。Socket.io⽀持WebSocket,Flash Sockets,长轮询,流,持久帧(iframe)和JSONP轮询。具体使⽤哪个⽅案取决与浏览器的兼容性,尽可能使⽤最优⽅案解决。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论