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 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 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 352 353 354