Nodejs_WebSocket_Examples

Nodejs_WebSocket_Examples

大部分基于JavaScriptWebSocket协议示例都是基于nodejs作为服务端,浏览器作为客户端,很少有nodejs的客户端实现。同时,由于大部分nodejs客户端的WebSocket协议和浏览器原生的WebSocket并不完全兼容,因此他们无法很好地在一起工作。

本仓库包含了一系列WebSocket协议在nodejs下的不同实现,包括纯的TypeScript语言实现以及ExpressNestjs框架的实现。

每个例子在单独的示例文件夹下,其子文件夹用以区分不同的客户端和服务器。

1. Websocket 基础

WebSocket是用于“在网络应用中保持和服务器端进程双边通讯的”,这一功能和传统的http协议仅能由客户端发起请求服务器进行响应有所区别。 在实现上,websocket协议分为 官方实现 和非官方实现

npmjs上3个最流行的websocket库分别是 wssocket.io、and websocket(其他常见的websocket库还有WebSocket-NodeµWebSockets等). 我们在这里使用了前两个,因为他们各自有其优点和不足。

ws库是最流行的websoket库,它完全支持官方协议。在浏览器中可以不用引入额外的包,直接用浏览器原生Websocket实现功能,然而,如果你要在nodejs上使用ws作为客户端,你还需要一个类似像我们这里用的 isomorphic-ws 的包装器。

socket.io虽然和官方库实现不兼容,但有有其自身的特点,在负载均衡、穿过防火墙以及自动重连方面都有其优势。使用该库时需要在服务器和客户端同时引入,注意,在客户端要引入socket.io-client y库而不是 socket.io.

2. 示例的基础功能

示例用以说明websocket的不同实现,因此不会实现更复杂的功能共,每个示例在库文件功能允许的情况下包含以下功能,示例中部分代码来自各库的官方示例:

  • 一个纯nodejs的服务器或者基于网络框架的服务器
  • 一个浏览器实现的客户端和一个nodejs(包含或者不包含框架)实心的客户端
  • 运行服务器和客户端(网页浏览器只需要在网页中打开client.html文件),客户端将自动连接服务器
  • 客户端连接后,服务器向客户端发送一个 hello 消息,包含 hello client 数据
  • 客户端收到 message消息后发送一个 response 消息,包含 response + 随机数 到服务器
  • 服务器收到 response 消息后,发送 end消息并包含其收到的消息内容。
  • 客户端收到end消息后z延时3秒断开连接
  • 服务器和客户端均需要在终端打印其收到的所有消息
  • 服务器和客户端均需要打印连接状态变化信息
  • 服务器和客户端均需要打印错误信息

3. 示例文件说明

3.1 nodejs ws example

本文件夹包括使用纯nodejs实现的WebSocket服务器和客户端示例。 其中server文件夹包括了服务器文件,安装并通过npm run start即可编译启动。server文件夹下还包括了一个运行在http服务器上的ws 服务器 server2.ts,通过 npm run start2 启动,为简化示例,两个服务器均使用 18000端口(http服务器监听3000端口).

client文件夹包括了:

  • 一个纯浏览器的客户端实现client.html,使用浏览器打开即可
  • 一个使用ws实现的客户端client.ts,运行npm run start运行
  • 一个兼容浏览器标准协议的客户端client2.ts,运行npm run start2运行

说明:

原生ws和兼容浏览器标准的WebSocket协议之间的主要区别.在原生ws中函数格式为

ws.on('message', (message)=>{
    console.log('received: %s', message);
});

在兼容浏览器标准的WebSocket协议中书写格式为:

ws.onmessage=(message)=>{
    console.log('received: %s', message);
}

除此之外,两个协议中message的类型和内容也不同,原生协议的message类型为WebSocket.data,而浏览器标准的协议中message类型为WebSocket.MessageEvent,后者的结构类似如下,而前者仅包含其中data的内容:

 MessageEvent {
  target: [WebSocket],
  type: 'message',
  data: '{"event":"end","data":"response: 0.3416359669492526"}'
 }

3.2 nodejs socketio example

本文件夹包括使用纯nodejs实现的socket.io服务器和客户端示例。 其中serverclient文件夹分别包含了服务器和客户端文件,安装并通过npm run start即可编译启动。server文件夹下还包括了一个运行在http服务器上的socket.io 服务器 server2.ts,通过 npm run start2 启动,为简化示例,两个服务器均使用 18000端口.

client文件夹同时包含了index.html浏览器客户端,需要注意:

  • 由于socket.io与标准WebSocket协议不兼容,因此浏览器需要依赖socket.io.js文件运行,也可能需要socket.io.js.map。这两个文件在socket.io官方库的client-dist中可以找到。
  • 由于版本升级,使用v2版本的socket.io文件连接v3版本服务器可能会出现400错误。
  • 此外,由于socket.io v3 开启了CORS,通过浏览器直接打开index.html无法正常运行客户端,本案例安装了http-server库作为网页服务器(默认打开index.html文件),并在服务器中配置CORS(见下文),通过npm run startweb运行http-server并访问http://localhost:8081以通过浏览器访问。

说明: 本仓库socket.io v3socket.io从版本2到3的升级过程中有很大变动,因此本仓库示例并不适用于socket.io v2版本。参考官方文档跟本仓库示例有关的几点说明如下。

  • 由于socket.io v3代码库使用TypeScript重写,因此在使用TypeSctipt时不需要再引入@types/socket.io@types/socket.io-client库,如果继续引入可能会在编译时出错。
  • 在版本3中,跨域请求CORS需要在新建服务器时明确声明,例如:
const io = require("socket.io")(httpServer, {
  cors: {
    origin: "https://localhost:8081",
    credentials: true,
    methods: ["GET", "POST"]
  }
});
  • socket.io通讯可配置自动重连机制,该机制默认打开,在服务器连接断开后会自动尝试重连服务器,如果要测试该功能,可注释掉客户端setTimeout函数内容,关闭服务器后再打开,客户端即可重新连接。
  • socket.io支持消息确认机制,本示例的response消息增加了消息确认机制,服务器在收到消息后会通过回调函数进行确认。
  • socket.io消息的内容类型可兼容stringJSON等格式,用户需要在编程时自行决定数据类型并尽心解析。

3.3 nestjs ws example

本文件夹基于ws库使用流行的nestjs框架分别实现了服务器和客户端的WebSocket服务,由于Nestjs是一个服务器框架,因此,在客户端部分,又通过nestjs内置的静态文件和mvc框架服务设计了一个html网页以便进行功能测试。本文件夹案例和 1. nodejs ws example文件夹中的案例接口设计基本相同,可以互相连接,但本案例为了丰富功能测试,在功能上进行了以下修改和补充:

  • 服务器在收到客户端的response消息后,并不会立即返回end消息,而是同样返回一个response消息,以避免客户端过早断开连接影响测试。
  • 客户端新增了一个response消息响应,将收到的消息进行打印显示
  • 客户端新增了一个terminate消息,需要手动触发以发送,服务器收到terminate消息后,发送end消息,客户端再断开连接

为了测试客户端的http相应功能,在客户端新增了两个api分别为,为了避免客户端和服务器http端口冲突,修改客户端端口为3001: –Get http://localhost:3001/ws-client/getdata 用于发送并接受response消息,通过随机数来更新数据 –Get http://localhost:3001/ws-client/sendTerminate 用于发送terminate消息以断开连接,该命令会停止客户端程序运行

在客户端根目录下新建了client.http文件,该文件可以通过vscodeREST Client插件运行,用于进行api功能调试。

经过测试,ws库可以不需要http服务器启用cors功能。

使用步骤:

  • 分别在serverclient文件夹下运行npm run start即可启动服务器和客户端,可以看到websocket通讯。
  • 在浏览器中打开http://localhost:3001可以在console中和网页中看到数据,通过刷新按钮可以刷新数据。
  • 本示例和 1. nodejs ws example中的示例可以互通互联,但由于api不同,使用时应注意区别。

3.4 nestjs socket io example

本文件夹基于socket.io库使用流行的nestjs框架分别实现了服务器和客户端的WebSocket服务, API接口和示例3完全相同,但有一些 重点注意事项:

  • 由于socket.io的3.x和2.x版本不兼容,他们无法协同工作,也就是说服务器和客户端要工作在同一个大版本下。本案例使用版本3.
  • Nestjs 内置的socket.io@nestjs/platform-io是基于socket.io版本2,与版本3的客户端不兼容。
  • Nestjs 作者答应在nestjs 8中升级 socket-io版本,目前,如果要像本示例一样使用socket.io v3,可以参考nestjs issue 5676问题中给出的临时策略, 用socket-io.adapter而不是官方的适配器。(要删除cookie项下面的name一行) .
  • 当然,将客户端降级到2.x版本可能也能工作,但本案例并未尝试此种做法。
  • 到目前为止,客户端和服务器可以正常连接工作,但是如果使用案例2中的客户端,是连接不上本案例的服务器的,这可能是由@WebSocketGateway()装饰器造成的,必须把装饰器的port参数留空,或者配置为和服务器http端口一致,才可以正常访问,如果两者不一致,即使配置了cors仍然是无法通过案例2中的客户端访问本案例中的服务器的。

参考资料

  1. HTML在线标准
  2. 官方WebSocket协议
  3. ws库
  4. socket.io 库
  5. websocket 库
  6. isomorphic-ws 库
  7. socket.io-client 库
  8. socket.io 网站版本迁移说明
  9. Nestjs issue 5676

Most WebSocket Examples in JavaScript are of nodejs in server side and web brawser in client side, and not that easy to move these client to a nodejs implementation. Besides, as most nodejs WebSocket client are not compatible with native brawser WebSocket client, few examples can work well both in nodejs and web brawser.

This repository contains a list of examples to illustrate the implementation of WebSocket protocal in nodejs, both pure TypeScript or with frameworks like Express or Nestjs.

Each example folder contains independent example, with subfolders to distinguish server and client.

1. Websocket Basics

WebSocket is “To enable web applications to maintain bidirectional communications with server-side processes”, there are official implementation and unofficial ones.

The top three popular websocket libraries on npmjs are wssocket.io、and websocket( There are also other implementations like WebSocket-Nodeor µWebSockets). We use both ws and socket.io in this repository because they each has there advantages and disadvantages.

The ws library is the most popular one and it is fully compatible with the Official WebSocket protocal, however, if you want to use ws as client in nodejs, a wrapper like isomorphic-ws is necessary, which is also used in the examples.

The socket.io library has its own features like load balance and auto-reconnection functions although it is not compatible with the Official WebSocket protocal and you need to install it both in server side and client side. In client side, you need socket.io-client library instead of socket.io.

2. Basic Example Functions

The examples are used to illustrate implementations of websocket, thus no complex functions will not be apllied here. The basic functions of each example contains following functions (unless the library has no relative function),and some of the example code are from original library examples:

  • A websocket server in pure nodejs or with a http framework
  • Two websocket client in brawser and in nodejs (with or without framework)
  • Start both server and client side(open the html file in a web brawser for the brawser client), the client will try to connect to the server
  • When the client connect to the Server, the server send an message event with hello client to the client
  • When the client receives the message event, it send an response event with response + randmon number to the server
  • When the Server receives the response event, it send an end event with the content it received.
  • When the Client receives the end event, it closes the connection after 3s delay.
  • Both server and client print every event content it received in the console.
  • Both server and client print the connect and disconnect status change in the console.
  • Both server and client print error messages in the console.

3. Examples

3.1 nodejs ws example

This example contains WebSocket server and client example implemented with nodejs. The server folder contains a normal server ,install and run npm run start to start it. There is another ws server wrapped in a httpServer in the server folder named server2.ts, run npm run start2 to start it, to make the example simple, the two servers run on the same port 18000(the http server listens port 3000).

client folder contains:

  • A pure brawser client client.html, open with a brawser to start
  • ws client with native ws library, run npm run start
  • ws client compatible with native WebSocket protocol, run npm run start2

Note:

The function format are different from ws and native WebSocket, in ws library:

ws.on('message', (message)=>{
    console.log('received: %s', message);
});

In native WebSocket compatible format:

ws.onmessage=(message)=>{
    console.log('received: %s', message);
}

Besides, the message in the two protocols are also different, in the ws library it is WebSocket.data while in the native WebSocket protocol is WebSocket.MessageEvent, the latter structure is similar as below as the former contains only the data part.

 MessageEvent {
  target: [WebSocket],
  type: 'message',
  data: '{"event":"end","data":"response: 0.3416359669492526"}'
 }

3.2 nodejs socketio example

This example contains socket.io server and client implemented with nodejs. Install and npm run start in server and client folder to start each. There is another socket.io server wrapped in a httpServer in the server folder named server2.ts, run npm run start2 to start it, to make the example simple, the two servers run on the same port 18000

There is also a index.html file which implemented a brawser client:

  • As socket.io is not compatible with native WebSocket, there is a socket.io.js file (a socket.io.js.map may also needed, both files can be found on socket.io library client-dist folder) which is replied by the html file.
  • Using the obsolete socket.io.js v2 file to connect v3 server may cause 400 error.
  • As socket.io v3 uses CORS, simply open the index.html file in a brawser cannot visit the server. We installed http-server here (which uses index.html as default page), and config the CORS option in the server (see below), run npm run startweb and visit http://localhost:8081 in a brawser to connect to the server.

NOTICE:

This repository uses socket.io v3 and not compatible with socket.io. See Socket.io Document for the detailed differences between the two version. Some import feature are as follows:

  • As socket.io v3 is rewritten by TypeScript, there is no need to import @types/socket.io or @types/socket.io-clientlibrary to use socket.io in TypeSctipt, import the above libraries in your code may cause mistake.
  • CORS needs to be explicitly announced as follows:
const io = require("socket.io")(httpServer, {
  cors: {
    origin: "https://example.com",
    methods: ["GET", "POST"]
  }
});
  • socket.io support autoreconnection mechanism which is enabled by default, if you want to test the function, just comment the setTimeout function in client, stop and start the server again, the client will reconnect to the server automatically.
  • socket.io support message acknowledgement mechanism, the response event in this example added it, the server will give an acknowledgement message through callback function when it receives a response message.
  • The content of socket.io event are highly customised ,you need to decide and encode/decode the content and avoid possible mistakes.

3.3 nestjs ws example

This example contains both WebSocket server and client implementation with popular Nestjs framework using ws library. As Nestjs is a server framework itself, a html file is added with nestjs serve Static function and MVC service. the communication interfaces in this example is similar to 1. nodejs ws example, so the servers and clients can connect with each other. However, for test purpose, the functions a modifed in this example so as to keep the client keep connected with the server until there is an order to terminate:

  • The server will not send an end message when it received a response message, instead, it sends a same response message to the client.
  • response message listener is added to the client, it will print the message it received.
  • terminate message will be send from the client trigged by a http request sendTerminate, the server will send the old end message when it received the terminate message.

The client httpServer works on port 3001, two apis were added to update data and terminate connection –Get http://localhost:3001/ws-client/getdata to send response and update the data it received. –Get http://localhost:3001/ws-client/sendTerminate 用to send terminatemessage,This will also terminate the client app, you need restart it again

client.http file is created in client folder to test http requests, which can be used by vscode plugin REST Client.

no cors configuration when use ws library.

To Use:

  • In the server and client run npm run start to start them and see the result.
  • In a brawser visit http://localhost:3001, to see and update data in console and in the page.
  • To work with 1. nodejs ws example servers and clients, you need to pay attention to the API difference.

3.4 nestjs socket io example

This example contains both WebSocket server and client implementation with popular Nestjs framework using socketio library. The program structure and API interface are exactly the same as example 3. Except for some important NOTES:

  • As socket.io version 3.x and 2.x are incompatible, they cannot work together, which means the server and client shall of the same major version.
  • Nestjs built-in socket.io library @nestjs/platform-io was written with socket.io version 2, so it is incampatible with a version 3 client.
  • The Nestjs author promised to update socket-io on nestjs 8 ,however, if you want to use socket.io v3 in nestjs, a temporary approach is introduced in nestjs issue 5676, use the socket-io.adapter in the issue (the name line under cookie shall be removed) instead of the official one. This is the way we used here.
  • You may also downgrade the client version to v2.x ,however, we did not try that way here.
  • SO FAR, the server and client works well in this example, but if you want the server in this example works together with the client in example 2, there is still a problem, perhaps due to the @WebSocketGateway() decorator mechanism, you will not visit the server if the port parameter (for example @WebSocketGateway(18000)) is different from the server http port (for example app.listen(3000)) even if cors is enabled. You shold leave the @WebSocketGateway() decorator blank and use the client to visit the same http port.

References

  1. HTML Living Standard
  2. Official WebSocket Protocal in MDN
  3. ws library
  4. socket.io library
  5. websocket library
  6. isomorphic-ws library
  7. socket.io-client library
  8. socket.io migrating guide
  9. nestjs issue 5676
作者
魏智勇(John)
加入讨论

此站点使用 Akismet 来减少垃圾评论。了解我们如何处理您的评论数据

魏智勇(John)

站长,80后,创业者,擅长工业自动化与信息化技术,熟悉各种PLC,组态软件,熟悉计算机技术,熟悉LabVIEW、C,C#,JavaScript程序设计技术。