初探 WebSocket

1. Socket

要使用websocket,首先要明白socket。

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。
Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原意那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。

简单来说,socket让我们开发着无须了解网络通信的底层原理,就可以使我们轻松地建立双工通信通道。

2. 基本工作模型

d000baa1cd11728b45647b06cafcc3cec3fd2c4c.jpg

3. 连接池模型

上图只是简单的描述一对C/S通信的流程,而实际情况中,我们往往需要面对的是一个S(server)对应的对个C(client),只考虑单进程,单线程的情况下,如何协调它们之间的通信呢?下面介绍连接池模型。

顾名思义,我们需要划分一块区域,用于存放客户端的连接资源。当有新连接加入时,我们将其放入连接池(pool);对于已经连接的socket,我们监听它们的输入,并给出输出响应。由于预先存储了所有相连的socket,我们也可以主动推送数据,主动断开连接等。

在php中,完成连接池模型的必须api都是有提供的(不知道是因为有这个模型,才准备了这些api,还是恰恰相反),用伪代码可以更清晰地描述这一模型。

init_pool; //初始化连接池
listen; //开始监听
where(true){
    watch(pool); //此处监听连接池。阻塞,直到得到新状态的通知
    for (socket in pool){
        if(is_master(socket)){
            //如果是服务器端的新状态,代表有新连接
            add_pool(socket); //加入连接池
        }else{
            //客户端状态更新,服务端应给出响应
            work(socket);
        }
    }
}

4. 数据传输

socket在数据传输时可能会遇到的问题:

  1. 通信双方的意外断线
  2. 大数据的可靠传输

对于问题1,我们应该预防网络情况不佳导致的数据丢失,具体实现效果包括断线重连,心跳检测,数据包可靠性检测(特征值检测)。
至于问题2,socket的发送和接收都是有数量限制的,一次性发送大数据,应该拆分为多次发送小数据。为此,我们必须设计一套可靠的通信协议,来协调分片传输。常用方法有:结束标记法、关键字分割法、长度声明法。

5. WebSocket

WebSocket协议支持(在受控环境中运行不受信任的代码的)客户端与(选择加入该代码的通信的)远程主机之间进行全双工通信。用于此的安全模型是Web浏览器常用的基于原始的安全模式。 协议包括一个开放的握手以及随后的TCP层上的消息帧。 该技术的目标是为基于浏览器的、需要和服务器进行双向通信的(服务器不能依赖于打开多个HTTP连接(例如,使用XMLHttpRequest或长轮询))应用程序提供一种通信机制。

WebSocket(后简称WS)可以看作是socket的升级版,帮助建立现代浏览器与服务器的连接。
与它的长辈socket相比,它有如下几点不同:

  1. 握手。WS连接如果要想成功建立,需要经历“握手”。WS初次连接服务器时,将发送“握手”请求,服务器需要解析握手信息并按一定规则返回特定信息,才算握手成功,否则连接将被前端关闭。握手的意义是C/S双方互相通知对方:我准备好了。
  2. 数据帧。WS的通信数据是按照数据帧打包(有关数据帧的格式本文不再赘述)的。数据帧格式相当于一种数据传输协议,它规定了数据该被怎么发送,怎么接收。参看第4节的第二个问题,我们应该为数据传输定制协议,而WS已经事先定义了一种,就是数据帧。
  3. 关闭握手。没错WS的关闭也需要握手(什么都是商量着来的),通过这种信息沟通,双方可以确认关闭动作并得知关闭原因等详细数据。

6.小结

最近尝试搭建一个聊天室,需要WebSocket技术,所以恶补了相关知识。虽然最后聊天室成功的跑了起来,期间碰到的问题之多还是让我感叹不已。Socket网络编程是一门大学问,远远不是一两篇博文就可以讲得清楚的,但希望通过这一篇没有代码的博文,能让读者对整套系统有一个大概的认识,写代码的时候能少走一点弯路。

最后推荐一个开源的Websocket库 nekudo/php-websocket ,作者思路清晰,项目功能完善源码却不晦涩,小弟佩服佩服。