Giới Thiệu
WebSocket là một giao thức mạnh mẽ cung cấp kênh truyền thông hai chiều (full-duplex) thông qua một kết nối TCP duy nhất. Với khả năng này, cả máy khách và máy chủ có thể gửi và nhận dữ liệu đồng thời mà không cần bất kỳ yêu cầu nào thêm. Đây là một tính năng nổi bật so với giao thức HTTP truyền thống, nơi các kết nối thường chỉ cho phép một chiều giao tiếp tại một thời điểm.
Cách Thức Hoạt Động Của WebSocket
Quá trình thiết lập một kết nối WebSocket bắt đầu với yêu cầu từ máy khách:
- Client gửi yêu cầu đến máy chủ với trường
Upgrade
để chuyển đổi từ giao thức HTTP sang WebSocket. - Máy chủ nhận yêu cầu và phản hồi với mã HTTP 101 (Switching Protocols) nếu chấp nhận chuyển đổi.
Cả hai quá trình này tương tự như handshake trong TCP để thiết lập một kết nối. Sau khi kết nối được thiết lập, dữ liệu sẽ được trao đổi giữa client và server thông qua các frame được định nghĩa trong tiêu chuẩn RFC 6455.
Khi kết thúc, một trong hai bên sẽ gửi yêu cầu đóng kết nối.
Những Lợi Ích Của WebSocket Trong Phát Triển Ứng Dụng Web Thời Gian Thực
WebSocket giải quyết nhiều vấn đề khó khăn khi phát triển ứng dụng web thời gian thực với các lợi ích nổi bật so với HTTP truyền thống:
- Header Nhẹ: Giảm thiểu kích thước dữ liệu truyền tải.
- Kết Nối Một Chiều: Chỉ cần duy trì một kết nối TCP cho mỗi client.
- Push Data Từ Máy Chủ: Máy chủ có khả năng đẩy dữ liệu mới tới client mà không cần yêu cầu từ phía client.
Lý Thuyết
Keep Alive
Có thể bạn thắc mắc về khả năng giữ kết nối (keep alive) của WebSocket, trong khi giao thức HTTP thường sẽ đóng kết nối sau khi gửi phản hồi. Cơ chế giữ kết nối được định nghĩa bởi trường keep-alive
trong RFC 2616 của HTTP/1.0. Cụ thể, nếu không có yêu cầu nào trong khoảng thời gian quy định, một lệnh yêu cầu rỗng sẽ được gửi để kiểm tra xem bên kia còn hoạt động hay không, và nếu không có phản hồi, kết nối sẽ được đóng.
Tổng Quan Về Giao Thức
Phần tổng quan về giao thức đã được trình bày ở phần trước. Để hiểu rõ hơn, bạn có thể tìm hiểu thêm trong tài liệu về tổng quan giao thức WebSocket.
Cài Đặt WebSocket
Để giúp bạn áp dụng lý thuyết vào thực tiễn, trong phần này, tôi sẽ giới thiệu một cách thực hiện một cơ chế đơn giản sử dụng WebSocket bằng thư viện "github.com/gorilla/websocket" trong Golang.
Máy Chủ
Mã nguồn dưới đây thể hiện cách một server mở kết nối WebSocket để truyền file từ client tới server. Đoạn mã này có thể được áp dụng trong nhiều tình huống khác nhau, chẳng hạn như đồng bộ hóa dữ liệu giữa các node trong một mạng lưới blockchain. Hãy theo dõi mã nguồn kèm bình luận để nắm rõ hơn.
go
package main
import (
"bufio"
"fmt"
"log"
"net/http"
"os"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
var filename = "000000000.fdb"
func handlerWS(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return
}
defer conn.Close()
fmt.Println("Client connected, start sync data from file " + filename)
for {
messageType := websocket.TextMessage
file, err := os.Open(filename)
if err != nil {
log.Fatalf("Failed to open file: %s", err)
}
defer file.Close()
reader := bufio.NewReader(file)
count := 0
for {
data, err := reader.ReadBytes('\n')
if err != nil {
if err.Error() == "EOF" {
log.Println("End of file, close connection")
closeMsg := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "Server is closing the connection")
err = conn.WriteMessage(websocket.CloseMessage, closeMsg)
return
}
log.Fatalf("Failed to read file: %s", err)
}
err = conn.WriteMessage(messageType, data)
if err != nil {
fmt.Println("len: ", len(data))
log.Println("Error write message:", err)
return
}
fmt.Printf("Successfully sent %d messages\n", count)
count++
}
}
}
func main() {
http.HandleFunc("/ws", handlerWS)
fmt.Println("Server started on localhost:8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
Client
Client chỉ cần thực hiện nhiệm vụ tạo một kết nối tới server, đọc dữ liệu từ kết nối đó và ghi vào file. Dưới đây là mã nguồn cho client:
go
package main
import (
"fmt"
"os"
"github.com/gorilla/websocket"
)
var filename = "000000000.fdb"
func main() {
conn, _, err := websocket.DefaultDialer.Dial("ws://localhost:8080/ws", nil)
if err != nil {
fmt.Println("Error create Dial:", err)
panic(err)
}
defer conn.Close()
fmt.Println("Client connected, start sync data from file " + filename)
count := 0
for {
_, p, err := conn.ReadMessage()
if err != nil {
if websocket.IsCloseError(err, websocket.CloseNormalClosure) {
fmt.Println("Close connection, err: ", err)
return
}
fmt.Println("read message from connection fail, err: ", err)
return
}
file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
fmt.Printf("Failed to open file: %s", err)
}
defer file.Close()
_, err = file.Write(p)
if err != nil {
fmt.Printf("Failed to write file: %s", err)
}
fmt.Printf("Successfully received %d messages\n", count)
count++
}
}
Repo: GitHub Repository
Tổng Kết
Bài viết này đã giới thiệu về WebSocket với các đoạn mã minh họa dễ hiểu, giúp bạn thực hành và áp dụng vào các ý tưởng thực tiễn, như việc xây dựng ứng dụng chat hay đồng bộ hóa dữ liệu. Mời các bạn tham khảo và phát triển thêm các ứng dụng sáng tạo của riêng mình!
source: viblo