手写WebSocket服务器-01-简单例子

手写WebSocket服务器-01-简单例子

1.前言

最近因为工作需要手写了一个C语言版的websocket服务器,趁热写几篇文章记录下。

在我们的传统认知里,浏览器和Web应用之间的通信是单向的,只能由浏览器发起,由Web服务器转发给Web应用,Web应用被动响应。Web应用要想主动发数据给浏览器,就得通过其他的技术实现(比如Ajax等)。

WebSocket的出现带来了如下几点改变:

  • 浏览器和后端应用建立WebSocket连接后,可实现长连接、全双工的通信,虽然连接仍然只能由浏览器发起,但是连接建立后,任意一方可随时发送任意长度的消息给对方
  • 浏览器可绕过Web服务器直接跟后端应用通信,这个后端应用可以使用任意语言实现,只要他实现了WebSocket协议就行,可以简单如python、javascript,也可以是java、C/C++等
  • WebSocket协议是个外层协议,它可以包装字符串协议或者二进制协议,这意味这我们可以利用它实现任意复杂度的协议,可以是简单的json字符串协议,也可以是我们自定义的二进制协议,在此基础上我们甚至能实现加密压缩等功能

以上几个特性堪称革命性,这也是我决定学习websocket的最大动力。

2.例子

我们在这里展示一个极其简单的例子,然后在代码层面解释下都发生了什么。

2.1 运行示例

首先我们准备一个index.html文件,内容如下:

<!DOCTYPE html>
<html>
<head>
    <title>websocket test</title>
    <meta charset="utf8">
</head>
<body>

</body>

<script>
  if (!window.WebSocket){
    window.alert("您的浏览器不支持 WebSocket!");
  };

  var ws = new WebSocket('ws://192.168.237.130:8080');
  ws.onopen = function () {
    window.alert("连接服务端成功");
    ws.send('hello');
  };
  ws.onmessage = function (e) {
    window.alert("收到:" + e.data);
  };
  ws.onclose = function (e) {
    window.alert("服务端连接断开:" + e.reason);
  };
  ws.onerror = function (e) {
    window.alert("连接服务端失败");
  };
</script>

</html>

然后把这个文件放在web服务器Web应用根目录下。

然后我们把手写的websocket服务器wscsvr(websocket c server)运行起来。

然后打开浏览器,输入web服务器的地址,访问上述html文件。

然后浏览器依次输出了如下信息:

调试成功,下面我们解释这期间发生了什么。

2.2 浏览器端发生了什么?

当你在浏览器输入地址,敲下回车时,浏览器作为客户端向web服务器发出了一个http GET请求,请求其将index.html发给它。

浏览器收到应答后,将在本地执行index.html的代码,主要是如下这些:

可以看到,web浏览器首先检查了下自己是否支持websocket协议,幸运的是,绝大多数浏览器都支持,我们的测试浏览器也支持,因此没有弹窗。

然后它直接调用接口发起了一个websocket连接,地址是:“ws://192.168.237.130:8080”。

这个地址,其实就是我们手写的websocket服务器(wscsvr)地址,ip是192.168.237.130,端口是8080,协议是websocket协议。

然后浏览器这边注册了几个websocket连接回调函数,其中最重要的是onopen和onmessage。

onopen表示上述websocket连接建立成功,我们在建立成功后调用send接口向服务器端发送一个字符串“hello”。

onmessage表示从服务器端收到了消息,我们把它打印出来:

发现也是“hello”。

在这个例子中,我们通过websocket协议向对端发送了一个字符串“hello”,然后对端也给我们回了一个字符串“hello”。

下面我们解释,在服务端发生了什么。

2.3 websocket服务器发生了什么?

我们打开wscsvr的调试功能,发现服务器首先收到了如下内容:

乍一看我们还以为是个HTTP请求,实际上这确实也是个http请求,但不是个普通的http请求。

我们注意到,这个请求的后面有两行关键信息,一个是Upgrade,一个是websocket,这个请求在websocket协议中其实是一个握手协议,这个协议的作用是,浏览器告诉服务器,我虽然发的是http请求,但是后续我不想用http协议了,我想用websocket协议,请给我升级。

既然是握手,那就不能是单向的,服务器也得给他回消息,服务器回了什么呢?

服务器也给用http协议101代码回了个消息,这个消息的含义是,我同意升级为websocket协议,后续请用websocket协议收发消息。

然后我们继续看服务端打印的通信过程日志:

可以看到,服务端收发的消息中都已经包含不可见字符了,说明用的已经不是http协议了,这里其实已经是升级为http协议了。

下一篇文章,我们将从代码的层面解释这期间发生了什么。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注