PHP
Python

Trang chủ

Hướng dẫn chi tiết lập trình socket trong Python

Ngôn ngữ Python là một ngôn ngữ lập trình đa mục đích, nổi tiếng với cú pháp dễ đọc, dễ học và tính ứng dụng cao. Trong lĩnh vực phát triển web, Python thường được sử dụng thông qua các framework như Django và Flask để xây dựng các ứng dụng web mạnh mẽ, bảo mật và dễ mở rộng. Trong chuyên mục này, Vietnix không chỉ cung cấp kiến thức nền tảng về ngôn ngữ Python mà còn hướng dẫn chi tiết cách xây dựng các ứng dụng web thực tế, sử dụng các framework phổ biến và áp dụng các kỹ thuật tiên tiến. Vietnix cam kết liên tục cập nhật những bài viết mới nhất về các tính năng mới của Python, các thư viện hỗ trợ hữu ích và những phương pháp tốt nhất, giúp bạn khai thác tối đa sức mạnh của Python và hoàn thiện kỹ năng lập trình web của mình.
html
CSS
javascript
sql
python
php
c
c++
bootstrap
react
mysql
reactjs
vuejs
Javascript Tutorials
19/03/2025
22 phút đọc
Theo dõi Vietnix trên

Hướng dẫn chi tiết lập trình socket trong Python

Lập trình socket trong Python cho phép các ứng dụng giao tiếp qua mạng bằng cách gửi và nhận dữ liệu giữa các thiết bị. Kỹ thuật này giúp xây dựng các ứng dụng mạng như chat, truyền file hoặc server-client một cách linh hoạt và hiệu quả. Trong bài viết này, bạn sẽ được hướng dẫn chi tiết cách sử dụng socket trong Python, từ những bước cơ bản đến các ứng dụng thực tế.

Những điểm chính

  • Khái niệm: Hiểu khái niệm lập trình socket và cách sử dụng để giao tiếp giữa các thiết bị trong mạng.
  • Module socket trong Python: Nắm được cú pháp, các tham số và kiểu dữ liệu trả về khi làm việc với module socket.
  • Phương pháp Server và Client Socket: Biết cách thiết lập server và client, sử dụng các phương thức như connect(), send(), recv(), giúp truyền dữ liệu hiệu quả.
  • Chuyển file Python với Module Socket: Học cách gửi và nhận file giữa server và client bằng module socket.
  • Module socketserver của Python: Hiểu cách dùng module socketserver để tạo server đơn giản và linh hoạt hơn.
  • Vietnix – Nhà cung cấp dịch vụ lưu trữ tốc độ cao: Lựa chọn dịch vụ phù hợp để đảm bảo hiệu suất tốt nhất khi triển khai ứng dụng mạng.
  • Câu hỏi thường gặp: Giải đáp những thắc mắc phổ biến về lập trình socket trong Python, giúp bạn hiểu rõ hơn về cách hoạt động và ứng dụng thực tế.

Lập trình Socket trong Python là gì?

Lập trình socket trong Python là kỹ thuật cho phép giao tiếp giữa hai thiết bị (node) trong cùng một mạng, trong đó một thiết bị đóng vai trò server lắng nghe và xử lý yêu cầu từ các client. Trong Python, module socket được sử dụng để lập trình socket. Đây là một phần của thư viện tiêu chuẩn, cung cấp các chức năng cần thiết để thiết lập kết nối và truyền dữ liệu giữa server và client ở cấp độ phần cứng. Module này sử dụng giao diện socket BSD, hỗ trợ trên hầu hết các hệ điều hành như Linux, Windows, macOS.

Lập trình socket trong Python là kỹ thuật cho phép giao tiếp giữa hai thiết bị (node) trong cùng một mạng
Lập trình socket trong Python là kỹ thuật cho phép giao tiếp giữa hai thiết bị (node) trong cùng một mạng

Tìm hiểu chi tiết về socket

Socket là điểm cuối của một kênh giao tiếp hai chiều, cho phép các tiến trình trao đổi dữ liệu với nhau. Việc truyền thông qua socket có thể diễn ra trong cùng một tiến trình, giữa các tiến trình trên cùng một máy tính hoặc giữa các thiết bị trên khắp thế giới.

Tìm hiểu chi tiết về socket
Tìm hiểu chi tiết về socket

Mỗi socket được xác định bằng sự kết hợp giữa địa chỉ IP và số cổng (port). Để giao tiếp hiệu quả, cả hai đầu socket cần được cấu hình đúng. Có nhiều loại socket khác nhau, bao gồm Unix domain sockets, TCP, UDP,… Python cung cấp thư viện socket giúp lập trình viên dễ dàng thiết lập và quản lý các kết nối này. Lập trình socket là quá trình thiết lập và quản lý kết nối giữa các thiết bị để truyền và nhận dữ liệu. Có hai giao thức chính thường được sử dụng:

  • Giao thức kết nối (Connection-oriented)TCP (Transmission Control Protocol): Đây là giao thức đảm bảo độ tin cậy cao, dữ liệu được chia thành từng gói và gửi theo đúng thứ tự. Trước khi truyền dữ liệu, socket ở hai đầu cần được thiết lập một kết nối ổn định. Điều này làm cho TCP trở thành lựa chọn phổ biến trong các ứng dụng yêu cầu độ chính xác cao, như máy chủ web hoặc dịch vụ hosting.
  • Giao thức không kết nối (Connection-less)UDP (User Datagram Protocol): Không yêu cầu thiết lập kết nối trước khi gửi dữ liệu, do đó tốc độ truyền nhanh hơn nhưng không đảm bảo dữ liệu đến nơi theo đúng thứ tự hoặc không bị mất gói. UDP thường được dùng trong các ứng dụng thời gian thực như truyền phát video hoặc game online.

Module socket trong Python

Module socket trong Python được sử dụng để tạo và quản lý lập trình socket, giúp các thiết bị trong mạng có thể giao tiếp với nhau. Module này cung cấp lớp socket, cho phép bạn khởi tạo và sử dụng socket bằng phương thức socket.socket().

Cú pháp

Để tạo một socket trong Python, bạn sử dụng cú pháp sau:

import socket  
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  

Các tham số

Phương thức socket.socket() chấp nhận ba tham số quan trọng, giúp xác định cách thức hoạt động của socket:

  • socket_family – Xác định loại địa chỉ được sử dụng. Một số giá trị phổ biến:
  • AF_INET (mặc định): Sử dụng IPv4.
  • AF_INET6: Sử dụng IPv6.
  • AF_UNIX: Dùng cho giao tiếp giữa các tiến trình trên cùng một hệ thống.
  • socket_type – Xác định kiểu giao tiếp giữa các socket:
  • SOCK_STREAM: Kết nối sử dụng TCP (mặc định).
  • SOCK_DGRAM: Kết nối sử dụng UDP.
  • SOCK_RAW: Gửi dữ liệu mà không qua xử lý của giao thức.
  • protocol – Thông thường là 0, cho phép hệ thống tự động chọn giao thức phù hợp.

Kiểu dữ liệu trả về

Hàm socket.socket() trả về một đối tượng socket, cho phép bạn thực hiện các thao tác như kết nối đến server, gửi và nhận dữ liệu. Giả sử, bạn cần xây dựng một ứng dụng truyền dữ liệu giữa máy chủ và máy khách, bạn có thể sử dụng module socket để thiết lập kết nối giữa hai hệ thống một cách ổn định và bảo mật. Dưới đây là ví dụ tạo một socket đơn giản:

import socket  
# Tạo socket TCP  
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
# Kết nối đến server  
s.connect(("your-vps-ip", 8080))  
# Gửi dữ liệu  
s.sendall(b"Hello, Server!")  
# Nhận phản hồi  
data = s.recv(1024)  
print("Received:", data.decode())  
# Đóng kết nối  
s.close()  

Phương pháp Server Socket

Server socket là socket được khởi tạo trên máy chủ để lắng nghe và chấp nhận kết nối từ client. Trong Python, module socket cung cấp các phương thức quan trọng để thiết lập và vận hành server socket:

  • bind() – Ràng buộc socket với một địa chỉ IP và số cổng cụ thể.
  • listen() – Đưa server vào trạng thái chờ, liên tục lắng nghe các yêu cầu kết nối từ client.
  • accept() – Khi có yêu cầu kết nối, phương thức này sẽ chấp nhận và xác định client bằng địa chỉ của nó.

Dưới đây là ví dụ về cách tạo server socket đơn giản:

import socket  
# Tạo socket server  
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
server.bind(('localhost', 12345))  # Ràng buộc socket với địa chỉ IP và cổng  
server.listen()  # Bắt đầu lắng nghe kết nối  
print("Server đang chạy và chờ kết nối...")  
client, addr = server.accept()  # Chấp nhận kết nối từ client  
print(f"Kết nối nhận từ: {addr}")  

Phương pháp Client Socket

Client socket là một điểm cuối trong quá trình giao tiếp mạng, có nhiệm vụ gửi yêu cầu kết nối đến server socket đang lắng nghe tại một địa chỉ IP và cổng cụ thể. Khi server chấp nhận kết nối, cả hai socket có thể trao đổi dữ liệu qua lại.

Phương thức connect()

Phương thức này giúp client socket kết nối đến server. Nó nhận vào một tuple chứa địa chỉ IP và cổng của server.

import socket
host = "your-server-ip"  # Địa chỉ IP của server
port = 12345  # Cổng server đang lắng nghe
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((host, port))
print("Kết nối đến server thành công!")

Phương thức send()

Sau khi kết nối thành công, client có thể gửi dữ liệu đến server bằng phương thức send().

message = "Xin chào server từ client!"
client.send(message.encode())

Phương thức sendall()

Tương tự như send(), nhưng sendall() sẽ tiếp tục gửi dữ liệu cho đến khi toàn bộ dữ liệu được truyền đi hoặc gặp lỗi.

message = "Dữ liệu quan trọng cần gửi đến server."
client.sendall(message.encode())

Phương thức sendto()

Phương thức này chỉ sử dụng trong giao thức UDP, cho phép gửi dữ liệu đến một địa chỉ cụ thể.

client_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_udp.sendto(b"Dữ liệu gửi bằng UDP", (host, port))

Phương thức recv()

Phương thức này giúp client nhận dữ liệu từ server.

data = client.recv(1024)  # Nhận tối đa 1024 byte
print("Dữ liệu từ server:", data.decode())

Phương thức recvfrom()

Tương tự như recv(), nhưng dành riêng cho giao thức UDP.

data, addr = client_udp.recvfrom(1024)
print("Nhận dữ liệu từ:", addr, "Nội dung:", data.decode())

Socket Server trong Python

Để xây dựng một server trên Internet, bạn sử dụng module socket trong Python. Trước tiên, bạn cần tạo một socket bằng cách gọi socket.socket(). Sau đó, dùng phương thức bind(hostname, port) để chỉ định địa chỉ và cổng mà server sẽ lắng nghe. Khi server khởi động, nó sẽ gọi listen() để chờ các kết nối từ client. Khi có client kết nối, phương thức accept() sẽ nhận kết nối và trả về một đối tượng kết nối, giúp server có thể giao tiếp với client.

Dưới đây là một ví dụ về cách triển khai socket server trong Python:

import socket
# Khai báo địa chỉ IP và cổng của server
host = "127.0.0.1"  
port = 5001  
# Tạo socket server
server = socket.socket()
server.bind((host, port))
server.listen()
print(f"Server đang lắng nghe trên {host}:{port}...")
# Chấp nhận kết nối từ client
conn, addr = server.accept()
print(f"Kết nối từ: {addr}")
while True:
    data = conn.recv(1024).decode()
    if not data:
        break
    print(f"Dữ liệu nhận từ client: {data}")
    # Chuyển dữ liệu thành chữ hoa và gửi lại client
    response = data.upper()
    conn.send(response.encode())
conn.close()

Ví dụ trên minh họa cách một server nhận dữ liệu từ client, xử lý và gửi phản hồi lại. Khi triển khai thực tế, bạn có thể mở rộng chương trình để xử lý nhiều kết nối cùng lúc hoặc tích hợp vào các hệ thống lớn hơn.

Socket Client trong Python

Socket Client trong Python là một chương trình kết nối đến một server thông qua địa chỉ IP và cổng xác định. Khi kết nối được thiết lập, client có thể gửi và nhận dữ liệu từ server, tạo thành một mô hình giao tiếp hai chiều. Trong Python, việc tạo một socket client rất đơn giản nhờ module socket. Phương thức connect(hostname, port) giúp mở kết nối TCP đến server trên cổng xác định. Sau khi kết nối thành công, client có thể gửi dữ liệu bằng send() và nhận phản hồi từ server bằng recv(). Khi hoàn tất, bạn cần đóng socket bằng close(), tương tự như khi làm việc với file.

Dưới đây là một đoạn code Python đơn giản cho một client kết nối đến server trên localhost (127.0.0.1) tại cổng 5001. Client sẽ gửi tin nhắn đến server và nhận phản hồi cho đến khi người dùng nhập q để thoát:

import socket  
# Định nghĩa địa chỉ server
host = '127.0.0.1'  
port = 5001  
# Tạo socket client
client_socket = socket.socket()  
client_socket.connect((host, port))  
# Gửi và nhận dữ liệu từ server
message = input("Nhập tin nhắn: ")  
while message.lower() != 'q':  
    client_socket.send(message.encode())  
    data = client_socket.recv(1024).decode()  
    print('Phản hồi từ server:', data)  
    message = input("Nhập tin nhắn: ")  
# Đóng kết nối
client_socket.close()

Mô hình client-server này có thể được ứng dụng trong nhiều lĩnh vực, từ chat bot, truyền file, đến các hệ thống quản lý dữ liệu từ xa. Nếu muốn chạy chương trình trên hai máy tính khác nhau, hãy thay thế 127.0.0.1 bằng địa chỉ IP thực của server. Trên Windows, bạn có thể dùng lệnh ipconfig, còn trên Ubuntu, dùng lệnh ifconfig để lấy địa chỉ IP.

Chuyển file Python với Module Socket 

Trong lập trình mạng, việc truyền file giữa server và client là một ứng dụng phổ biến của socket. Python cung cấp module socket, giúp thiết lập kết nối và trao đổi dữ liệu giữa các thiết bị. Dưới đây là cách thực hiện truyền file từ server sang client bằng socket.

Mô hình hoạt động:

  • Server mở một file ở chế độ nhị phân (rb), đọc dữ liệu từng phần và gửi đến client.
  • Client nhận dữ liệu từ server và ghi vào một file mới ở chế độ ghi nhị phân (wb).
  • Quá trình tiếp tục cho đến khi toàn bộ nội dung file được truyền tải xong.

Server Code

Server sẽ thiết lập kết nối, chấp nhận yêu cầu từ client và gửi file từng phần:

import socket
host = "127.0.0.1"
port = 5001
server = socket.socket()
server.bind((host, port))
server.listen()
print("Server đang chờ kết nối...")
conn, addr = server.accept()
print(f"Kết nối từ: {addr}")
filename = 'test.txt'  # File cần gửi
with open(filename, 'rb') as f:
    while (data := f.read(1024)):  # Đọc từng phần của file
        conn.send(data)
        print(f"Đã gửi: {repr(data)}")
print("File đã được truyền thành công.")
conn.close()

Client Code

Client sẽ nhận dữ liệu từ server và lưu vào một file mới:

import socket
host = "127.0.0.1"
port = 5001
client = socket.socket()
client.connect((host, port))
print("Đã kết nối tới server.")
with open('recv.txt', 'wb') as f:
    while True:
        data = client.recv(1024)
        if not data:
            break
        f.write(data)
print("File đã được nhận thành công.")
client.close()

Ứng dụng thực tế:

  • Truyền file giữa các máy chủ nội bộ hoặc từ server đến client trên internet.
  • Xây dựng các hệ thống sao lưu dữ liệu hoặc chia sẻ file trong doanh nghiệp.
  • Ứng dụng trong các dịch vụ lưu trữ, giúp khách hàng dễ dàng tải và quản lý dữ liệu từ xa.

Module socketserver của Python

Module socketserver trong Python cung cấp các lớp giúp đơn giản hóa việc xây dựng máy chủ socket. Nó hỗ trợ cả giao thức TCP và UDP, cho phép tạo server lắng nghe kết nối từ client mà không cần xử lý trực tiếp từng socket một cách thủ công.

Các lớp chính trong socketserver:

  • TCPServer: Sử dụng giao thức TCP để duy trì luồng dữ liệu liên tục giữa client và server. Khi khởi tạo, nó tự động gọi server_bind()server_activate().
  • UDPServer: Dùng cho giao thức UDP, phù hợp với các ứng dụng không yêu cầu kết nối liên tục.
  • StreamRequestHandler: Cung cấp các thuộc tính self.rfileself.wfile để đọc và ghi dữ liệu giữa client và server.
  • DatagramRequestHandler và UnixDatagramServer: Dùng trong hệ thống Unix, không khả dụng trên các nền tảng khác.

Để tạo một server bằng socketserver, bạn cần viết một lớp RequestHandler, định nghĩa phương thức handle() để xử lý dữ liệu từ client:

Server Code

import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        host, port = self.client_address
        print(f"{host}:{port} gửi: {self.data.decode()}")
        # Trả lời client
        response = "Dữ liệu đã được nhận!"
        self.request.sendall(response.encode())
if __name__ == "__main__":
    HOST, PORT = "0.0.0.0", 9999  # Server lắng nghe trên tất cả địa chỉ IP
    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        print("Server đang chạy tại Vietnix, chờ kết nối từ client...")
        server.serve_forever()

Client Code

import socket
HOST, PORT = "localhost", 9999
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    sock.connect((HOST, PORT))
    message = "Xin chào từ client!"
    sock.sendall(message.encode())
    received = sock.recv(1024).decode()
    print(f"Server phản hồi: {received}")

Dưới đây là mô phỏng quá trình giao tiếp giữa server và nhiều client khi sử dụng socketserver:

ServerClient-1Client-2
127.0.0.1:54518 wrote:
from client-1Sent: from client-1
enter text ..Received: hello
127.0.0.1:54522 wrote:
how are youSent: how are you
enter text ..Received: fine
127.0.0.1:54523 wrote:
from client-2Sent: from client-2
enter text ..Received: hi client-2
127.0.0.1:54526 wrote:
good byeSent: good bye
enter text ..Received: bye bye
127.0.0.1:54530 wrote:
thanksSent: thanks
enter text ..Received: bye client-2

Vietnix – Nhà cung cấp dịch vụ lưu trữ tốc độ cao

Vietnix là một trong những đơn vị hàng đầu tại Việt Nam trong lĩnh vực web hostingVPSthuê máy chủ và domain, mang đến giải pháp lưu trữ tối ưu với hiệu suất cao và bảo mật vượt trội. Nhờ hạ tầng mạnh mẽ cùng đội ngũ kỹ thuật hỗ trợ 24/7, Vietnix cam kết đảm bảo tốc độ truy cập nhanh, ổn định, giúp website của bạn vận hành mượt mà. Hơn 80.000 khách hàng đã tin tưởng sử dụng dịch vụ của Vietnix để tối ưu hiệu suất và bảo vệ dữ liệu quan trọng. Liên hệ ngay để được tư vấn dịch vụ phù hợp!

Thông tin liên hệ:

  • Hotline: 18001093
  • Email: sales@vietnix.com.vn 
  • Địa chỉ: 265 Hồng Lạc, Phường 10, Quận Tân Bình, Thành Phố Hồ Chí Minh.
  • Website: https://vietnix.vn/

Câu hỏi thường gặp

Làm thế nào để socket Python có thể hoạt động sau một lần mất kết nối mà không cần khởi động lại toàn bộ ứng dụng?

Cách giữ socket Python hoạt động sau khi mất kết nối
Phát hiện mất kết nối: Kiểm tra lỗi từ send() hoặc recv() (ví dụ: socket.error, BrokenPipeError).
Tự động kết nối lại: Dùng vòng lặp thử kết nối lại sau một khoảng thời gian ngắn nếu bị gián đoạn.
Giữ phiên làm việc: Truyền lại dữ liệu chưa gửi hoặc yêu cầu server tiếp tục từ trạng thái trước khi mất kết nối.
Dùng timeout và keep-alive: Thiết lập socket.settimeout() và bật tùy chọn SO_KEEPALIVE để duy trì kết nối.
Tách luồng xử lý: Chạy socket trên một luồng riêng để ứng dụng chính không bị gián đoạn khi thử kết nối lại.

Tại sao đôi khi socket không nhận đủ dữ liệu như mong muốn khi gọi recv() và làm thế nào để khắc phục?

Lý do socket không nhận đủ dữ liệu khi gọi recv():
TCP không đảm bảo kích thước gói tin: Dữ liệu có thể bị chia nhỏ hoặc đến không đầy đủ trong một lần gọi recv().
Bộ đệm của socket: recv() chỉ lấy dữ liệu hiện có trong bộ đệm, có thể chưa đủ toàn bộ gói tin mong muốn.
Gói tin bị cắt ngang: Khi mạng không ổn định, dữ liệu có thể đến từng phần nhỏ.
Cách khắc phục
Đọc theo độ dài mong muốn: Dùng vòng lặp để nhận dữ liệu cho đến khi đủ.
Gửi kèm kích thước dữ liệu: Server gửi trước số byte của dữ liệu, client đọc trước rồi gọi recv() theo kích thước đó.
Sử dụng giao thức cấp cao hơn: Dùng struct hoặc pickle để đóng gói dữ liệu chính xác.

Có thể sử dụng socket để giao tiếp giữa hai chương trình Python chạy trên hai mạng khác nhau không? Nếu có thì làm thế nào?

Có thể sử dụng socket để giao tiếp giữa hai chương trình Python trên hai mạng khác nhau, nhưng cần đảm bảo các điều kiện sau:
Xác định địa chỉ IP công khai: Máy chủ cần có IP công khai hoặc sử dụng dịch vụ Dynamic DNS (DDNS) nếu IP thay đổi thường xuyên.
Cấu hình NAT và Port Forwarding: Nếu máy chủ nằm trong mạng LAN, cần mở cổng (port forwarding) trên router để cho phép kết nối từ bên ngoài.
Dùng giao thức TCP hoặc UDP: TCP đảm bảo độ tin cậy, còn UDP nhanh hơn nhưng không có cơ chế kiểm soát lỗi.
Thiết lập firewall: Kiểm tra và cho phép lưu lượng qua cổng socket trên cả máy chủ và router nếu cần.
Dùng VPN hoặc WebSocket: Nếu gặp hạn chế về mạng hoặc firewall, có thể dùng VPN để nối hai mạng lại hoặc WebSocket để giao tiếp qua HTTP.

Làm thế nào để tạo một hệ thống chat hoàn chỉnh chỉ sử dụng socket Python mà không cần framework bên thứ ba?

Tạo hệ thống chat bằng Python Socket (Không dùng Framework):
Hệ thống gồm Server (quản lý kết nối, broadcast tin nhắn) và Client (gửi & nhận tin nhắn).
Các bước chính:
Server:
– Dùng socket + threading để xử lý nhiều client.
– Triển khai broadcast gửi tin nhắn đến tất cả client.
Client:
– Kết nối server, gửi tin nhắn.
– Chạy luồng riêng để nhận tin nhắn.
Mở rộng:
GUI, gửi file, mã hóa SSL, phòng chat, lưu lịch sử.

Lời kết

Lập trình socket trong Python là một kỹ năng quan trọng giúp bạn xây dựng các ứng dụng mạng, từ chat server đơn giản đến hệ thống giao tiếp phức tạp. Qua bài viết này, bạn đã nắm được các kiến thức cơ bản về cách tạo và quản lý socket, xử lý kết nối TCP/UDP, cũng như những lưu ý quan trọng để đảm bảo hiệu suất và bảo mật. Nếu bạn có bất kỳ thắc mắc hoặc muốn tìm hiểu sâu hơn, đừng ngần ngại để lại bình luận hoặc tiếp tục khám phá thêm các tài liệu nâng cao. Chúc bạn thành công!

Mọi người cũng xem:

Cao Lê Viết Tiến

PHP Leader
tại
Vietnix

Kết nối với mình qua

Icon Quote
Icon Quote

Học lập trình online cùng vietnix

Học lập trình online cùng Vietnix

PHPXem thêmThu gọn