• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# WebSocket Connection (C/C++)
2
3## When to Use
4
5The WebSocket module can be used to establish bidirectional connections between the server and the client.
6
7## Available APIs
8
9The following table lists the common **WebSocket** APIs. For details, see [net_websocket.h](../reference/apis-network-kit/net__websocket_8h.md).
10
11
12| API| Description|
13| -------- | -------- |
14| OH_WebSocketClient_Constructor(WebSocket_OnOpenCallback onOpen, WebSocket_OnMessageCallback onMessage, WebSocket_OnErrorCallback onError, WebSocket_OnCloseCallback onclose) | Constructor used to create a **WebSocketClient** instance. |
15| OH_WebSocketClient_AddHeader(struct WebSocket \*client, struct WebSocket_Header header) | Adds the header information to the client request. |
16| OH_WebSocketClient_Connect(struct WebSocket \*client, const char \*url, struct WebSocket_RequestOptions options) | Connects the WebSocket client to the server. |
17| OH_WebSocketClient_Send(struct WebSocket \*client, char \*data, size_t length) | Sends data from the WebSocket client to the server. |
18| OH_WebSocketClient_Close(struct WebSocket \*client, struct WebSocket_CloseOption options) | Lets the WebSocket client proactively close the connection. |
19| OH_WebSocketClient_Destroy(struct WebSocket \*client) | Releases the context and resources of the WebSocket connection. |
20
21## Development Example
22
23### How to Develop
24
25To use related APIs to establish a connection to the WebSocket server, you need to create a Native C++ project, encapsulate the APIs in the source file, and call these APIs at the ArkTS layer. You can use hilog or console.log to print the log information on the console or generate device logs.
26
27The following walks you through on how to establish a connection to the WebSocket server, send messages to the WebSocket server, and close the WebSocket connection.
28
29### Adding Dependencies
30
31**Adding Dynamic Link Libraries**
32
33Add the following library to **CMakeLists.txt**.
34
35```txt
36libace_napi.z.so
37libnet_websocket.so
38```
39
40**Including Header Files**
41
42```c
43#include "napi/native_api.h"
44#include "network/netstack/net_websocket.h"
45#include "network/netstack/net_websocket_type.h"
46```
47
48### Building the Project
49
501. Write the API call code in the source file to allow applications to receive the URL string passed from ArkTS, create a pointer to the **WebSocketClient** object, and check whether the connection to the WebSocket server is successful.
51
52```cpp
53#include "napi/native_api.h"
54#include "network/netstack/net_websocket.h"
55#include "network/netstack/net_websocket_type.h"
56#include "hilog/log.h"
57
58#include <cstring>
59
60#undef LOG_DOMAIN
61#undef LOG_TAG
62#define LOG_DOMAIN 0x3200 // Global domain, which identifies the service domain.
63#define LOG_TAG "WSDEMO"   // Global tag, which identifies the module log tag.
64
65// Global variables of the WebSocket client
66static struct WebSocket *client = nullptr;
67
68static void onOpen(struct WebSocket *client, WebSocket_OpenResult openResult)
69{
70    (void)client;
71    OH_LOG_INFO(LOG_APP, "onOpen: code: %{public}u, reason: %{public}s",
72        openResult.code, openResult.reason);
73}
74
75static void onMessage(struct WebSocket *client, char *data, uint32_t length)
76{
77    (void)client;
78    char *tmp = new char[length + 1];
79    for (uint32_t i = 0; i < length; i++) {
80        tmp[i] = data[i];
81    }
82    tmp[length] = '\0';
83    OH_LOG_INFO(LOG_APP, "onMessage: len: %{public}u, data: %{public}s",
84        length, tmp);
85}
86
87static void onError(struct WebSocket *client, WebSocket_ErrorResult errorResult)
88{
89    (void)client;
90    OH_LOG_INFO(LOG_APP, "onError: code: %{public}u, message: %{public}s",
91        errorResult.errorCode, errorResult.errorMessage);
92}
93
94static void onClose(struct WebSocket *client, WebSocket_CloseResult closeResult)
95{
96    (void)client;
97    OH_LOG_INFO(LOG_APP, "onClose: code: %{public}u, reason: %{public}s",
98        closeResult.code, closeResult.reason);
99}
100
101static napi_value ConnectWebsocket(napi_env env, napi_callback_info info)
102{
103    size_t argc = 2;
104    napi_value args[2] = {nullptr};
105    napi_value result;
106
107    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
108
109    size_t length = 0;
110    napi_status status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &length);
111    if (status != napi_ok) {
112        napi_get_boolean(env, false, &result);
113        return result;
114    }
115
116    if (client != nullptr) {
117        OH_LOG_INFO(LOG_APP, "there is already one websocket client running.");
118        napi_get_boolean(env, false, &result);
119        return result;
120    }
121    char *buf = new char[length + 1];
122    std::memset(buf, 0, length + 1);
123    napi_get_value_string_utf8(env, args[0], buf, length + 1, &length);
124	// Create a pointer to the WebSocketClient object.
125    client = OH_WebSocketClient_Constructor(onOpen, onMessage, onError, onClose);
126    if (client == nullptr) {
127        delete[] buf;
128        napi_get_boolean(env, false, &result);
129        return result;
130    }
131	// Connect to the WebSocket server identified by the URL stored in the buffer.
132    int connectRet = OH_WebSocketClient_Connect(client, buf, {});
133
134    delete[] buf;
135    napi_get_boolean(env, connectRet == 0, &result);
136    return result;
137}
138
139static napi_value SendMessage(napi_env env, napi_callback_info info)
140{
141    size_t argc = 1;
142    napi_value args[1] = {nullptr};
143    napi_value result;
144
145    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
146
147    size_t length = 0;
148    napi_status status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &length);
149    if (status != napi_ok) {
150        napi_create_int32(env, -1, &result);
151        return result;
152    }
153
154    if (client == nullptr) {
155        OH_LOG_INFO(LOG_APP, "websocket client not connected.");
156        napi_create_int32(env, WebSocket_ErrCode::WEBSOCKET_CLIENT_NULL, &result);
157        return result;
158    }
159    char *buf = new char[length + 1];
160    std::memset(buf, 0, length + 1);
161    napi_get_value_string_utf8(env, args[0], buf, length + 1, &length);
162	// Send the messages in the buffer to the server.
163    int ret = OH_WebSocketClient_Send(client, buf, length);
164
165    delete[] buf;
166    napi_create_int32(env, ret, &result);
167    return result;
168}
169
170static napi_value CloseWebsocket(napi_env env, napi_callback_info info)
171{
172    napi_value result;
173    if (client == nullptr) {
174        OH_LOG_INFO(LOG_APP, "websocket client not connected.");
175        napi_create_int32(env, -1, &result);
176        return result;
177    }
178	// Close the WebSocket connection.
179    int ret = OH_WebSocketClient_Close(client, {
180        .code = 0,
181        .reason = "Actively Close",
182    });
183	// Release the WebSocket resources.
184    OH_WebSocketClient_Destroy(client);
185    client = nullptr;
186    napi_create_int32(env, ret, &result);
187    return result;
188}
189
190```
191
192On receiving a WebSocket URL, the `ConnectWebsocket` function attempts to connect to the server identified by the URL. If the connection is successful, **true** is returned. Otherwise, **false** is returned. Before creating a pointer to the **WebSocketClient** object, define the **onOpen**, **onMessage**, **onError**, and **onClose** callbacks for the WebSocket connection. In the sample code, functions such as `OH_WebSocketClient_Send` and `OH_WebSocketClient_Close` are also called to send messages to the server and proactively close the WebSocket connection.
193
194
1952. Initialize and export the `napi_value` objects encapsulated through **NAPI**, and expose the preceding functions to JavaScript through external function APIs. In the sample code, the `ConnectWebsocket` function is exposed as the external function `Connect`, the `SendMessage` function is exposed as the external function `Send`, and the `CloseWebsocket` function is exposed as the external function `Close`.
196
197```C
198EXTERN_C_START
199static napi_value Init(napi_env env, napi_value exports) {
200    napi_property_descriptor desc[] = {
201        {"Connect", nullptr, ConnectWebsocket, nullptr, nullptr, nullptr, napi_default, nullptr },
202        {"Send", nullptr, SendMessage, nullptr, nullptr, nullptr, napi_default, nullptr },
203        {"Close", nullptr, CloseWebsocket, nullptr, nullptr, nullptr, napi_default, nullptr},
204    };
205    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
206    return exports;
207}
208EXTERN_C_END
209```
210
2113. Register the objects successfully initialized in the previous step into the Node.js file by using the `napi_module_register` function of `RegisterEntryModule`.
212
213```C
214static napi_module demoModule = {
215    .nm_version = 1,
216    .nm_flags = 0,
217    .nm_filename = nullptr,
218    .nm_register_func = Init,
219    .nm_modname = "entry",
220    .nm_priv = ((void*)0),
221    .reserved = { 0 },
222};
223
224extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
225{
226    napi_module_register(&demoModule);
227}
228```
229
2304. Define the types of the functions in the `index.d.ts` file of the project. For example, the `Connect` function takes a string parameter as the input parameter and returns a Boolean value indicating whether the WebSocket connection is successfully established.
231
232```ts
233export const Connect: (url: string) => boolean;
234export const Send: (data: string) => number;
235export const Close: () => number;
236```
237
2385. Call the encapsulated APIs in the `index.ets` file.
239
240```ts
241import testWebsocket from 'libentry.so'
242
243@Entry
244@Component
245struct Index {
246  @State wsUrl: string = ''
247  @State content: string = ''
248  @State connecting: boolean = false
249
250  build() {
251    Navigation() {
252      Column() {
253        Column() {
254          Text("WebSocket address: ")
255            .fontColor(Color.Gray)
256            .textAlign(TextAlign.Start)
257            .width('100%')
258          TextInput()
259            .width('100%')
260            .onChange((value) => {
261              this.wsUrl = value
262            })
263        }
264        .margin({
265          bottom: 16
266        })
267        .padding({
268          left: 16,
269          right: 16
270        })
271
272        Column() {
273          Text("Content: ")
274            .fontColor(Color.Gray)
275            .textAlign(TextAlign.Start)
276            .width('100%')
277          TextInput()
278            .width('100%')
279            .enabled(this.connecting)
280            .onChange((value) => {
281              this.content = value
282            })
283        }
284        .margin({
285          bottom: 16
286        })
287        .padding({
288          left: 16,
289          right: 16
290        })
291
292        Blank()
293
294        Column({ space: 12 }) {
295          Button('Connect')
296            .enabled(!this.connecting)
297            .onClick(() => {
298              let connRet = testWebsocket.Connect(this.wsUrl)
299              if (connRet) {
300                this.connecting = true;
301              }
302            })
303          Button('Send')
304            .enabled(this.connecting)
305            .onClick(() => {
306              testWebsocket.Send(this.content)
307            })
308          Button('Close')
309            .enabled(this.connecting)
310            .onClick(() => {
311              let closeResult = testWebsocket.Close()
312              if (closeResult != -1) {
313                this.connecting = false
314              }
315            })
316        }
317      }
318    }
319  }
320}
321```
322
3236. Configure the `CMakeLists.txt` file. Add the required shared library, that is, `libnet_websocket.so`, to `target_link_libraries` in the `CMakeLists.txt` file automatically generated by the project.
324
325As shown in the following figure, `entry` in `add_library` is the `modename` automatically generated by the project. If you want to change its value, ensure that it is the same as the `.nm_modname` in step 3.
326
327![netmanager-4.png](./figures/websocket-notemod.png)
328
3297. To call WebSocket C APIs, make sure that you have the `ohos.permission.INTERNET` permission. Add this permission to the `requestPermissions` item in the `module.json5` file.
330
331After the preceding steps, the entire project is set up. Then, you can connect to the device to run the project to view logs.
332
333## Test Procedure
334
3351. Connect the device and use DevEco Studio to open the project.
336
3372. Run the project. The following page is displayed.
338
339![Demo initial image](./figures/websocket-demo-1.jpg)
340
341Description of settings:
342
343- In the text box in the first line, enter a WebSocket URL starting with `ws://` or `wss://`.
344
345- Tap `Connect`. If the connection is successful, the **onOpen** callback is triggered and logs are printed.
346
347- Enter the content to be sent to the server in the `Content` text box and tap `Send`. If the server returns a message, the `onMessage` callback is triggered and logs are printed.
348
349- Tap `Close`. The WebSocket connection is released. You can enter a new WebSocket URL to establish a new connection.
350
351![Demo input](./figures/websocket-demo-2.jpg)
352
353![Demo log output](./figures/websocket-demo-log.png)
354