## Nội dung chính 1. Introduction 2. How to Use WebSockets ### 1. Introduction WebSoket là công nghệ hỗ trợ giao tiếp hai chiều giữa client và server bằng cách sử dụng một TCP socket để tạo một kết nối hiệu quả và ít tốn kém về tài nguyên. Mặc dù được thiết kế để chuyên sử dụng cho các ứng dụng web, lập trình viên vẫn có thể đưa chúng vào bất kì loại ứng dụng nào. Cũng có thể hiểu đơn giản: Websocket là một TCP socket được tạo ra khi web browser connect tới Websocket server. Kết nối này sẽ được giữ liên tục. Vì vậy client có thể nhận được thông tin (message chat) từ server ngay khi có người chat, mà client không cần phải hỏi server liên tục (poll). Server sẽ chủ động báo cho (notify) client khi có message chat mới. Hiện tại Websocket đã được hỗ trợ trên 74% các trình duyệt. Bạn có thể xem số liệu mới nhất http://caniuse.com/#search=websocket. **Ưu điểm** - WebSockets cung cấp khả năng giao tiếp hai chiều mạnh mẽ, có độ trễ thấp và dễ xử lý lỗi. - API cũng rất dễ sử dụng trực tiếp mà không cần bất kỳ các tầng bổ sung nào, so với Comet, thường đòi hỏi một thư viện tốt để xử lý kết nối lại, thời gian chờ timeout, các Ajax request (yêu cầu Ajax), các tin báo nhận và các dạng truyền tải tùy chọn khác nhau (Ajax long-polling và jsonp polling). - Không cần phải có nhiều kết nối như phương pháp Comet long-polling và cũng không có những nhược điểm như Comet streaming. **Nhược điểm** - Chưa hỗ trợ được tất cả các trình duyệt. - Không có phạm vi yêu cầu nào. Do WebSockets là một TCP socket chứ không phải là HTTP request, nên không dễ sử dụng các dịch vụ có phạm vi-yêu cầu. **1.1. Background** Trước đây, để tạo ứng dụng web giao cần giao tiếp hai chiều giữa client và server. Bắt buộc phải sử dụng HTTTP để request đến server mới có thể lấy được được những thông tin từ server trả về. Điều này dẫn đến nhiều vấn đề - Máy chủ buộc phải sử dụng một số TCP tiềm ẩn khác nhau để kết nối cho mỗi máy khách cho việc gửi thông tin đến. - Phía client bắt buộc phải duy trì một ánh xạ từ các kết nối gửi đi đến các kết nối đến để theo dõi kết quả trả về. Một giải pháp đơn giản là sử dụng một kết nối TCP duy nhất cho việc giao tiếp hai chiều giữa Client và Server. Và websocket cung cấp các API để làm được điều này. **1.2. Protocol Overview** Protocol có hai phần handshake from the client (Request) và handshake from the server (Response) **Request** ```PHP GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 ``` **Response** ```PHP HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: chat ``` ### ** 2. How to Use WebSockets** **2.1. Detecting WebSocket Support** Dưới đây là những trình duyệt mà websocket support http://caniuse.com/#feat=websockets Việc kiểm tra này là cần thiết trước khi sử dụng Websocket. Function dưới đây có thể dùng để kiểm tra việc này. Function này trả về true nếu Websocket có hỗ trợ và ngược lại sẽ trả vể false ```JavaScript function webSocketSupported() { return "WebSocket" in window; } ``` Chạy thử ```PHP_HTML <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Websockets support tester</title> <script type="text/javascript"> function WebSocketSupport() { if ("WebSocket" in window) { document.getElementById("ws_support").innerHTML = "<h2>Trình duyệt web của bạn hỗ trợ WebSocket</h2>"; } else { document.getElementById("ws_support").innerHTML = "<h2>Trình duyệt web của bạn không hỗ trợ không WebSocket</h2>"; } } </script> </head> <body onload="javascript: WebSocketSupport()"> <div id="ws_support"></div> </body> </html> ``` Chạy file **index.html** bằng các trình duyệt khác nhau. **2.2. Opening a WebSocket** WebSockets được khởi tạo thông qua function WebSocket() với các tham số như bên dưới. ```Javascript WebSocket( url[, protocols] ) ``` Đối số đầu tiên là URL mà WebSocket sẽ kết nối đến. Khi một WebSockets được khởi tạo, ngay lập tức nó sẽ kết nối với một URL cố nhất định. Sau đó, URL của WebSocket có thể truy cập thông qua "url" khác của ứng dụng. Dưới đây là các thuộc tính chi tiết trong URL kết nối sử dụng trong hàm Websocket - Scheme: Một scheme cần phải có “ws” or “wss”. “ws” scheme là một kết nối không an toàn về bảo mật, trong khi “wss” thì ngược lại. Các trang chạy qua giao thức HTTP nên sử dụng "ws" WebSockets. Với những trang chạy qua giao thức HTTPS nên sử dụng “wss”. Khi bạn không sử dụng "ws" hoặc "wss" sẽ xảy ra lỗi về kết nối. - Host: Nó là một tên bất kỳ hoặc là địa chỉ Server của bạn. - Port: Nếu port không được xác định cụ thể thì với "ws" port sẽ là 80. Với "wss" port sẽ là 443 - Resource Name: Có thể được bỏ qua, trong trường hợp mặc định là một dấu gạch chéo "/". Các định dạng chung của một URL WebSockets, cũng như một URL thông thường khác. ```JavaScript scheme://host:port/resource ws://localhost:8080/echo wss://cjihrig.com ``` **2.3. Closing WebSockets** Việc close một kết nốt nó như một qui tắc chung trong lập trình. Quy định này cũng áp dụng đối với WebSockets. Trong Websocket để đóng một kết nối ta sử dụng hàm close() và nó có 2 tham số truyền vào như bên dưới. ```Javascript close( [code[, reason]] ) ``` Tuy nhiên trong thực tế, chúng ta thường sử dụng **close()** là không có đối số truyền vào. **2.4. Checking a Connection’s Status** Một Websocket có thể có 4 trạng thái sau: - Connecting: Khi một WebSocket được khởi tạo, nó sẽ kết nối với URL của nó. Trong thời gian đó, nó sẽ trạng thái 0. - Open: Sau một WebSocket kết nối thành công tới URL của nó vào trạng thái mở. Một WebSocket phải ở trong trạng thái mở để gửi và nhận dữ liệu qua mạng. Một WebSocket trong trạng thái mở có một "readyState" giá trị của 1 - Closing: Khi một WebSocket bị được đóng lại, Server sẽ thông báo disconnnect nó. Trong thời gian này, các WebSocket được coi là ở trạng thái closing. Một WebSocket ở trạng thái closing có một "readyState" giá trị của 2. - Closed: Một WebSocket ở trạng thái closed có một “readyState” với giá trị là 3. WebSocket định nghĩa các hằng số tĩnh đại diện cho "readyState". Ví dụ sau đây giúp các bạn có thể để đánh giá tình trạng của một kết nối. ```JavaScript switch (socket.readyState) { case WebSocket.CONNECTING: // do something break; case WebSocket.OPEN: // do something break; case WebSocket.CLOSING: // do something break; case WebSocket.CLOSED: // do something break; default: // this never happens break; } ``` **2.4. Handling Binary Data** JavaScript sử dụng text formats giống như JSON and XML để chuyển dữ liệu sang kiểu chuỗi. Tuy nhiên, với HTML5 nó cho phép ứng dụng làm việc với dữ liệu kiểu nhị phân để tăng performance. WebSockets hỗ trợ hai loại dữ liệu nhị phân là large objects (blobs) và ArrayBuffers. Tuy nhiên trong cùng một thời điểm, Websocket chỉ làm việc được với một trong hai kiểu dữ liệu nhị phân trên. Khi một Websocket được cài đặt, nó sẽ mang dữ liệu mặc định là blobs. Thuộc tính kiểu nhị phân được dùng để chuyển đổi giữa blobs và ArrayBuffer Dưới đây là ví dụ chuyển đổi dữ liệu kiểu blobs và ArrayBuffer ```Javascript socket.binaryType = "blob"; // receive some blob data socket.binaryType = "arraybuffer"; // now receive ArrayBuffer data ``` **2.4. WebSocket Event Handlers** Event handlers đóng một vai trò quan trọng trong lập trình WebSockets. Tất cả các dữ liệu nhận được, được xử lý bởi Event handlers (Bộ xử lý sự kiện). Dưới đây là các phương thức xử lý sự kiện trong Websocket. **Onopen** Khi một WebSocket chuyển sang trạng thái mở, phương thức "onopen" sẽ được gọi. Phương thức này được thể hiện như dưới đây. ```Javasript socket.onopen = function(event) { // handle open event }; ``` Event handlers có thể được tạo ra bằng cách sử dụng phương thức **addEventListener()**. Ví dụ sau đây sử dụng **addEventListener()** để thêm một sự kiện cho một WebSocket "open". ```Javascript socket.addEventListener("open", function(event) { // handle open event }); ``` **Onmessage** Khi một WebSocket nhận dữ liệu từ Server, phương thức "onmessage" được gọi. ```avascriptJavascript socket.onmessage = function(event) { var data = event.data; // process data as string, blob, or ArrayBuffer }; ``` Phương thức “onmessage” event handler cũng sử dụng addEventListener(). ```Javascript socket.addEventListener("message", function(event) { var data = event.data; // process data as string, blob, or ArrayBuffer }); ``` **onclose** Khi WebSocket đã được lại, phương thức “onclose” được gọi. Những sự kiện được truyên cho đối tượng “onclose” có ba tham số là “code”, “reason”, and “wasClean”. Tham số “code” là một số được cung cấp từ server, và có thể giữ các giá trị tương tự như “code” code của hàm close(). Tham số “reason” là một chuỗi mô tả trường hợp của event “close”. Tham số “wasClean” mang kiểu dữ liệu boolean. Under normal circumstances, “wasClean” is true. An example “onclose” event handler is shown below. Trong những trường hợp bình thường, "wasClean" là true. **Ví dụ "onClose"** ```Javascript socket.onclose = function(event) { var code = event.code; var reason = event.reason; var wasClean = event.wasClean; // handle close event }; ``` Thực hiện handler "onClose" sự kiện sử dụng addEventListener() ```Javascript socket.addEventListener("close", function(event) { var code = event.code; var reason = event.reason; var wasClean = event.wasClean; // handle close event }); ``` **onerror** Khi xảy ra một lỗi bất kỳ với WebSocket, phương thức "onerror" của nó được gọi. Sự kiện này được handler theo một error object chuẩn chưa “name” và “message”. ```Javascript socket.onerror = function(event) { // handle error event };avascript ``` Một "onerror" event handler có thể sử **addEventListener()** ```Javascript socket.addEventListener("error", function(event) { // handle error event }); ``` **Sending Data** WebSockets truyền dữ liệu thông qua phương thức send(). Phương thức send() có 3 kiểu dữ liệu - Message data là text - Message data ArrayBuffers - Message data blobs Cú pháp như sau ```Javascript send(data) ``` Khi phương thức send() được gọi, các dữ liệu đối số được đặt trong một bộ đệm. Tuy nhiên, dữ liệu có thể không được gửi ngay lập tức. Để kiểm tra số lượng byte trong bộ đệm ra, sử dụng "bufferedAmount" Để hiểu hơn điều này các bạn xem ví dụ bên dưới. ```Javascript var data = new ArrayBuffer(10000000); // perform some operations on the ArrayBuffer socket.send(data); if (socket.bufferedAmount === 0) { // the data sent } else { // the data did not send } ``` Vậy là tôi đã giới thiệu cho các bạn các khái niệm về Websocket và cách sử dụng Websocket như thế nào. Các thuộc tính và phương thức trong Websocket mà chúng thường dùng. Các bạn có thể tạo một ứng dụng đơn giản (chat) theo hướng dẫn ở đây https://viblo.asia/phamkykhoi/posts/NznmMd5LGr69 Chúc các bạn học tốt