Phép thuật Truyền - Nhận dữ liệu (Send & Receive)

Hôm nay, chúng ta sẽ học Bài 3: Phép thuật Truyền - Nhận dữ liệu (Send & Receive) để bắt hai chương trình không chỉ bắt tay, mà còn gửi thư qua lại cho nhau thông qua "đường ống ngầm" Socket nội bộ này!

🏛️ Phần 1: Bí mật về Chuỗi Byte (Mã hóa và Giải mã)

Trước khi vào code, chúng ta hãy hình dung cách hệ điều hành làm việc với socket:

*"Khi con gõ chữ "Cho con mua dừa" trong Python, đó là ngôn ngữ của con người. Đường ống Socket nội bộ của Hệ điều hành giống như một dòng sông nước, nó không biết chở chữ viết đi, nó chỉ biết chở các hạt electron $0$ và $1$ (gọi là chuỗi Byte).

  • Khi gửi thư (Send): Con phải biến chữ viết thành Byte bằng lệnh .encode('utf-8') (Đóng gói bưu kiện).

  • Khi nhận thư (Receive): Con nhận được một đống Byte $0$ and $1$, con phải dịch nó ngược lại thành chữ viết bằng lệnh .decode('utf-8') (Cậy nắp bưu kiện)."*

💻 Phần 2: Thực hành viết Hệ thống gửi thư nội bộ

Hãy tạo tiếp 2 file mới trong cùng một thư mục để làm một trò chơi: Khách hàng gửi số lượng quả dừa muốn mua $\rightarrow$ Cửa hàng nhận được, tính tổng tiền rồi gửi hóa đơn ngược lại!

🏪 File số 1: tiem_dua_server.py (Chạy file này trước)

File này sẽ mở cổng 8888, đứng im đợi khách gửi số lượng dừa, tính tiền rồi gửi trả hóa đơn.

import socket

# 1. Khởi tạo bộ đàm Socket nội bộ
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('127.0.0.1', 8888)) # Cổng nội bộ số 8888
server_socket.listen(1)

print("🏪 TIỆM DỪA: Đã mở cửa cổng 8888, chờ khách đặt hàng...")

# 2. Chấp nhận kết nối từ Client nội bộ
client_com, client_address = server_socket.accept()
print(f"🤝 TIỆM DỪA: Đã kết nối với khách hàng tại phòng chat nội bộ!")

# 3. RECEIVE - Nhận số lượng dừa từ khách gửi sang
# Số 1024 nghĩa là tối đa nhận bưu kiện nặng 1024 byte cùng một lúc
thu_nhan_duoc_dang_bytes = client_com.recv(1024)

# Cậy nắp bưu kiện: Giải mã từ Bytes thành Chữ (String)
so_luong_chu = thu_nhan_duoc_dang_bytes.decode('utf-8')
print(f"📩 TIỆM DỪA: Khách vừa nhắn tin đòi mua: {so_luong_chu} quả dừa.")

# 4. Xử lý tính tiền
gia_mot_qua = 15000
tong_tien = int(so_luong_chu) * gia_mot_qua
tin_nhan_tra_loi = f"Hóa đơn của con: {so_luong_chu} quả dừa x {gia_mot_qua:,}đ = {tong_tien:,}đ. Xin cảm ơn!"

# 5. SEND - Đóng gói câu trả lời thành Bytes và gửi ngược lại cho khách
client_com.send(tin_nhan_tra_loi.encode('utf-8'))
print("📤 TIỆM DỪA: Đã gửi hóa đơn tính tiền cho khách xong!")

# 6. Dọn dẹp cửa hàng
client_com.close()
server_socket.close()

🛒 File số 2: em_be_client.py (Chạy file này sau)

File này sẽ chủ động kết nối đến cổng 8888, gửi số 5 sang và đợi hóa đơn bay về.

import socket

# 1. Khởi tạo bộ đàm của khách hàng
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

print("📞 CLIENT: Đang kết nối đến tiệm dừa nội bộ...")
client_socket.connect(('127.0.0.1', 8888))

# 2. Chuẩn bị nội dung chữ và SEND (Mã hóa thành Bytes trước khi gửi)
so_luong_mua = "5"
print(f"📤 CLIENT: Gửi tin nhắn đặt mua {so_luong_mua} quả dừa sang tiệm...")
client_socket.send(so_luong_mua.encode('utf-8'))

# 3. RECEIVE - Ngồi im đợi tiệm dừa tính tiền xong gửi bưu kiện về
hoa_don_bytes = client_socket.recv(1024)

# Cậy nắp hóa đơn để đọc chữ
hoa_don_chu = hoa_don_bytes.decode('utf-8')
print(f"🔔 CLIENT: Tin nhắn phản hồi từ Tiệm dừa bay về:")
print(f"👉 {hoa_don_chu}")

# 4. Ngắt kết nối
client_socket.close()
print("👋 CLIENT: Đã nhận hóa đơn, cất bộ đàm đi về.")

🔍 Phần 3: Nghiệm thu dòng chảy dữ liệu trên cùng một máy

Hãy chạy thử nghiệm theo đúng quy trình:

  1. Bật file tiem_dua_server.py lên bấm Run. Tiệm dừa sẽ đứng im hóng tai nghe.

  2. Bật file em_be_client.py lên bấm Run.

  3. Quan sát kết quả: Chúng sẽ thấy ở màn hình Client lập tức nhận được dòng chữ: 👉 Hóa đơn của con: 5 quả dừa x 15,000đ = 75,000đ. Xin cảm ơn!.

Phép thuật đã thành công! Thư từ chương trình Client đã bay qua cổng mạng nội bộ của Hệ điều hành, chui vào chương trình Server, Server tính toán xong lại ném kết quả ngược trở lại cho Client in ra.

🏆 Thử thách tư duy logic:

Hệ thống hiện tại của chúng ta mới chỉ nói chuyện được đúng 1 câu là tự động dập máy (close). Hãy suy nghĩ câu hỏi bên dướiđể chuẩn bị cho Bài 4 - Dự án tốt nghiệp Phòng Chat Zalo:

*"Bây giờ nếu hai máy tính gõ chữ nói chuyện qua lại liên tục không ngừng nghỉ (giống như bố con mình chat Zalo vậy), thì chúng ta phải đưa cấu trúc vòng lặp nào đã học vào trong code?

Và nếu vừa muốn gõ chữ gửi đi (input), vừa muốn đợi tin nhắn từ máy kia bay về (recv) cùng một lúc, chúng ta có cần gọi đến các 'Người công nhân Luồng' (Đa luồng - Threading) của tuần trước không?"*