Mô-đun socket

Python SELF VI
Mức độ , Bài học
Có sẵn

7.1 Socket là gì?

Hãy đi sâu hơn một chút. Đầu tiên, chúng ta đã học cách làm việc với request, sau đó là http.client, tiếp đó là proxy. Vậy tiếp theo là gì? Tiếp theo chúng ta sẽ tìm hiểu cặn kẽ về các thư viện này...

Socket (tạm dịch: ổ điện) là một điểm trong mạng mà qua đó dữ liệu được gửinhận. Socket có thể được coi là điểm cuối của một kênh liên lạc hai chiều giữa hai chương trình, hoạt động trên cùng một hoặc trên các máy tính khác nhau.

Sockets hỗ trợ các giao thức mạng khác nhau, nhưng hai giao thức được sử dụng thường xuyên nhất là:

  • TCP (Transmission Control Protocol): Một giao thức đáng tin cậy đảm bảo việc thiết lập kết nối, kiểm tra tính toàn vẹn của dữ liệu và trình tự chính xác của chúng.
  • UDP (User Datagram Protocol): Giao thức không định hướng kết nối, không đảm bảo việc truyền tải dữ liệu, nhưng nhanh hơn và hiệu quả hơn cho một số loại ứng dụng nhất định.

Để định danh một socket, chúng ta sử dụng địa chỉ IP (định danh thiết bị trong mạng) và cổng (định danh một ứng dụng hoặc dịch vụ cụ thể trên thiết bị đó).

Quan trọng! Địa chỉ IP và cổng xác định duy nhất một chương trình trong mạng. Nó giống như địa chỉ nhà và số căn hộ. Địa chỉ nhà (địa chỉ IP) là địa chỉ của máy tính bạn trong mạng, còn cổng là số căn hộ mà chương trình sử dụng để gửi và nhận tin nhắn.

Để biết thêm chi tiết về địa chỉ IP và cổng, bạn có thể tham khảo các bài giảng về cấu trúc của mạng.

Các bước cơ bản khi làm việc với socket:

  1. Tạo socket: Chương trình tạo một socket, xác định loại giao thức (TCP hoặc UDP).
  2. Kết nối với địa chỉ: Socket được liên kết với một địa chỉ IP và số cổng, để có thể kết nối hoặc gửi/nhận dữ liệu.
  3. Nghe và thiết lập kết nối (đối với TCP):
    • Nghe: Socket phía server được đặt vào chế độ nghe, chờ kết nối đến.
    • Thiết lập kết nối: Client khởi tạo kết nối với server. Server chấp nhận kết nối, tạo socket mới để tương tác với client.
  4. Trao đổi dữ liệu: Dữ liệu được trao đổi giữa client và server. Đối với TCP, dữ liệu được gửi trong một thứ tự đáng tin cậy.
  5. Đóng kết nối: Sau khi trao đổi dữ liệu xong, kết nối sẽ được đóng.

Lợi ích của việc sử dụng sockets:

  • Sự linh hoạt: Sockets cho phép các ứng dụng trao đổi dữ liệu bất kể vị trí và nền tảng của chúng.
  • Hiệu suất: Sockets cung cấp một cách nhanh chóng và hiệu quả để truyền dữ liệu, đặc biệt khi sử dụng UDP.
  • Độ tin cậy (trong trường hợp TCP): Giao thức TCP đảm bảo việc truyền dữ liệu đáng tin cậy với kiểm tra tính toàn vẹn và khôi phục gói tin bị mất.

7.2 Tạo socket-server

Làm việc với socket trong Python được thực hiện thông qua mô-đun socket được tích hợp sẵn, cung cấp giao diện cho lập trình mạng cấp thấp.

Sockets có thể được sử dụng để tạo socket-server — ứng dụng/đối tượng sẽ nhận yêu cầu từ client và phản hồi lại. Và cũng có thể tạo socket-client — ứng dụng/đối tượng sẽ gửi yêu cầu tới socket-server và nhận phản hồi từ nó.

Để tạo socket-server, cần thực hiện ba bước:

  1. Tạo đối tượng socket-server.
  2. Ràng buộc (bind) nó với một IP và cổng nào đó.
  3. Bật chế độ lắng nghe (listen) các tin nhắn đến.

Mã nguồn sẽ trông khoảng như thế này:


import socket

# Tạo socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
# Ràng buộc socket với địa chỉ và cổng
server_socket.bind(('localhost', 12345))
    
# Nghe kết nối đến
server_socket.listen(5)
print("Server đang chờ kết nối...")

Ở đây socket.AF_INET chỉ định rằng chúng ta sử dụng IPv4 cho giao thức mạng, và socket.SOCK_STREAM có nghĩa là chúng ta sử dụng TCP. Đây là các tham số thường dùng nhất khi tạo các ứng dụng mạng.

Sau khi có một tin nhắn đến, cần thực hiện thêm bốn bước nữa:

  1. Thiết lập kết nối (accept) với client.
  2. Nhận (receive) yêu cầu (dữ liệu) từ client.
  3. Gửi (send) phản hồi đến client — cũng là một số dữ liệu nào đó.
  4. Đóng (close) kết nối.

Mã nguồn sẽ trông như thế này:


# Chấp nhận kết nối mới
client_socket, client_address = server_socket.accept()
print(f"Kết nối được thiết lập với {client_address}")

# Nhận dữ liệu từ client
data = client_socket.recv(1024)
print(f"Nhận được: {data.decode('utf-8')}")

# Gửi dữ liệu đến client
client_socket.sendall(b'Hello, client!')

# Đóng kết nối với client
client_socket.close()

Phương thức sendall() được sử dụng thay vì send(), bởi vì nó đảm bảo rằng tất cả dữ liệu sẽ được gửi đi. Phương thức send() có thể chỉ gửi một phần dữ liệu, nếu bộ đệm bị đầy.

Số 1024 trong recv(1024) chỉ định số byte tối đa có thể nhận được trong một lần. Điều này giúp kiểm soát khối lượng dữ liệu được xử lý trong một thao tác.

Mã nguồn của ví dụ cuối cùng thường được thực thi trong một vòng lặp vô tận — server xử lý một yêu cầu, sau đó chờ yêu cầu mới, rồi xử lý nó, cứ thế liên tục.

Nếu bạn muốn chạy nó trên máy của mình hoặc chỉ muốn xem ví dụ đầy đủ, thì mình sẽ đưa ví dụ tại đây:


import socket

# Tạo socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        
# Ràng buộc socket với địa chỉ và cổng
server_socket.bind(('localhost', 12345))
        
# Nghe kết nối đến
server_socket.listen(5)
print("Server đang chờ kết nối...")
        
while True:
    # Chấp nhận kết nối mới
    client_socket, client_address = server_socket.accept()
    print(f"Kết nối được thiết lập với {client_address}")
        
    # Nhận dữ liệu từ client
    data = client_socket.recv(1024)
    print(f"Nhận được: {data.decode('utf-8')}")
        
    # Gửi dữ liệu đến client
    client_socket.sendall(b'Hello, client!')
        
    # Đóng kết nối với client
    client_socket.close()

7.3 Tạo socket-client

Chúng ta đã tạo socket-server, giờ hãy viết một socket-client, cái sẽ gửi yêu cầu đến server và nhận lại dữ liệu từ nó.

Để làm điều đó, cần thực hiện năm bước:

  1. Tạo đối tượng socket-client.
  2. Thiết lập kết nối (connect) với địa chỉ IP và cổng của socket-server.
  3. Gửi (send) tin nhắn đến server.
  4. Nhận (receive) dữ liệu từ server.
  5. Đóng (close) kết nối.

Thực sự thì nó đơn giản hơn bạn tưởng. Đây là cách mã nguồn sẽ trông:


import socket

# Tạo socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
# Thiết lập kết nối với server
client_socket.connect(('localhost', 12345))
    
# Gửi dữ liệu đến server
client_socket.sendall(b'Hello, server!')
    
# Nhận dữ liệu từ server
data = client_socket.recv(1024)
print(f"Nhận được từ server: {data.decode('utf-8')}")
    
# Đóng socket
client_socket.close()

Nếu bên kia không có socket-server đang chạy hoặc kết nối bị mất, thì sẽ phát sinh ngoại lệ kiểu socket.error. Vì vậy, đừng quên xử lý ngoại lệ.

Chúng ta sẽ kết thúc việc làm việc với socket hôm nay ở đây.

Trong bất kỳ công việc nào với mạng, bạn sẽ thường xuyên gặp phải các host, cổng, địa chỉ IP, thiết lập kết nối, lắng nghe yêu cầu và các thứ khác tương tự. Vậy nên, hiểu được cách nó hoạt động sâu bên trong sẽ giúp bạn rất nhiều trong việc hợp nhất các kiến thức phân tán thành một bức tranh toàn cảnh.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION