Skip to main content

Using Websocket

WebSocket is a network communication protocol that provides the capability for full-duplex communication over a single TCP connection. Unlike the traditional HTTP request-response model, WebSocket allows the server to actively push data to the client, achieving true bidirectional communication.

Key Features:

  • Full-duplex communication: The client and server can send and receive data simultaneously.
  • Low latency: After the connection is established, the data transmission overhead is minimal.
  • Real-time: The server can actively push data without requiring client polling.
  • Persistent connection: Maintains the connection state after a single handshake, avoiding repeated connection establishment.

The WebSocket protocol uses URIs starting with ws:// or wss:// (secure connection). It establishes a connection through the standard HTTP handshake process and then upgrades to the WebSocket protocol for communication.

This article describes how to implement Websocket message sending/receiving using CloudRun (container-based and function-based), and how to access these services from the client side.

Using Function CloudRun​

Create a directory websocket-demo locally, create a new index.js file in the directory, and write the following code:

exports.main = function (event, context) {
console.log({ event, context });
if (context.ws) {
const ws = context.ws;
ws.on("close", (msg) => {
console.log("close: ", msg);
ws.send("bye!");
});
ws.on("message", (msg) => {
console.log("message: ", msg);
ws.send(`echo: ${msg?.data}`);
});
}
};

exports.main.handleUpgrade = async function (upgradeContext) {
console.log(upgradeContext, "upgradeContext");
if (upgradeContext.httpContext.url === "/reject-upgrade") {
return {
allowWebSocket: false,
statusCode: 403,
body: JSON.stringify({ code: "code", message: "message" }),
contentType: "appliaction/json; charset=utf-8",
};
}
return { allowWebSocket: true };
};

This code implements the following functionalities:

  • The main function handles message interactions after the WebSocket connection is established:
    • Listen for the close event and send a farewell message when the connection is closed.
    • Listen for the message event and reply with the current time and message content when a message is received.
  • The handleUpgrade function controls the establishment of WebSocket connections:
    • When accessing /reject-upgrade, reject the connection.
    • Other paths allow the establishment of WebSocket connections.

Deployment and Debugging​

It is recommended to use CloudBase CLI for deployment.

After installing the CLI and logging in, navigate to the project root directory and execute the following command:

# Deploying a Function CloudRun Instance
tcb cloudrunfuntion deploy

After the instance is deployed, go to the CloudBase console.

In the left navigation bar, select [CloudBase]. On the [Service List] page, locate the recently deployed instance and click to enter the [Service Details] page. The default domain name can be found on the details page. Replace the https:// at the beginning of the default domain with ws:// or wss://, and perform WebSocket connection testing in tools such as Postman.

Using Container CloudRun​

You can use the code sample: sample code repository.

After cloning the code repository, navigate to the project root directory. The directory structure is as follows:

├── Dockerfile
├── app
│   └── server.js
└── package.json

Here, app/server.js is a service implemented using express and express-ws, and CloudBase will build the container based on the Dockerfile.

For more information about Container CloudRun, refer to the Service Development Guide.

Deployment and Debugging​

It is recommended to use CloudBase CLI for deployment.

After installing the CLI and logging in, navigate to the project root directory and execute the following command:

# Deploying a Container CloudRun Instance
tcb cloudrun deploy

After the instance is deployed, go to the CloudBase console.

In the left navigation bar, select [CloudBase]. On the [Service List] page, locate the newly deployed instance and click to enter the [Service Details] page. On the details page, find the default domain name. We assume the default domain name for the created service is: https://demo.ap-shanghai.run.tcloudbase.com. Replace the https:// prefix with ws:// or wss://, for example: wss://demo.ap-shanghai.run.tcloudbase.com, and perform WebSocket connection testing in tools like Postman.

Accessing the websocket Service​

WeChat Mini Program​

wx.cloud.init({
env: "xxxx-yyy", // Replace with your Environment ID
});
const { socketTask } = await wx.cloud.connectContainer({
service: "websocket-demo", // Replace with your service name
path: "/", // Defaults to the root directory if left blank
});

socketTask.onMessage(function (res) {
console.log("[WEBSOCKET]", res.data);
});
socketTask.onOpen(function (res) {
console.log("[WEBSOCKET]", "Connected successfully!");
socketTask.send({
data: "This is a Mini Program message",
});
});
socketTask.onClose(function (res) {
console.log("[WEBSOCKET] Connection closed!");
});

Updated information can be found in the WeChat Cloud Run documentation.

Browser​

const ws = new WebSocket("wss://demo.ap-shanghai.run.tcloudbase.com"); // Replace with your service address
ws.onopen = function () {
console.log("Connection established successfully");
ws.send("WeChat Cloud Run test message"); // Send a message
};
ws.onmessage = function (evt) {
console.log(evt.data);
};
ws.onclose = function () {
console.log("Connection closed");
};

Node.js​

In Node.js, you can use the ws module to implement a WebSocket client.

import WebSocket from "ws";

function run() {
const ws = new WebSocket("wss://demo.ap-shanghai.run.tcloudbase.com"); // Replace with your service address

ws.on("close", (code, reason) => {
console.log("close:", code, `${reason}`);
});
ws.on("error", (err) => {
console.error("error: ", err);
});
ws.on("upgrade", () => {
console.log("upgrade");
});
ws.on("ping", () => {
console.log("recv ping message");
});
ws.on("pong", () => {
console.log("recv pong message");
setTimeout(() => {
ws.ping();
}, 1000);
});
ws.on("unexpected-response", (ws, req, res) => {
// Non-upgrade responses and 3xx redirect responses are considered unexpected-response
console.log("recv unexpected-response message");
});

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

ws.on("open", () => {
ws.ping();
ws.send("string data");
ws.send(Buffer.from("buffer data"));
});
}

run();