Khái Niệm Cơ Bản Về Socket
Socket xuất phát từ hệ điều hành Unix, tuân theo triết lý "mọi thứ đều là tập tin". Đây là cơ chế cho phép xử lý mạng thông qua các thao tác mở, đọc, ghi và đóng tương tự như tập tin. Socket thực chất là một loại tập tin đặc biệt, với các hàm xử lý (IO, mở, đóng) dành riêng cho giao tiếp mạng.
Socket là nền tảng cho mọi kết nối mạng. Khi bạn truy cập http://www.example.com, trình duyệt sẽ tạo socket để kết nối, nhận phản hồi và hiển thị trang. Tương tự, các ứng dụng chat như Slack cũng sử dụng socket để truyền dữ liệu.
Phân Loại Socket
| Loại | Mô Tả |
|---|---|
| AF_INET | Giao tiếp mạng giữa các máy chủ (IPv4) |
| AF_INET6 | Giao tiếp mạng IPv6 |
| SOCK_STREAM | Socket luồng (giao thức TCP) |
| SOCK_DGRAM | Socket gói tin (giao thức UDP) |
Các Hàm Socket Chính
Phương Thức Giao Tiếp
bind(): Liên kết socket với địa chỉ và cổnglisten(): Chấp nhận kết nối TCP (tham số hàng đợi)accept(): Xử lý kết nối mới (trả về socket và địa chỉ client)connect(): Thiết lập kết nối tới serverrecv(): Nhận dữ liệu (TCP)send(): Gửi dữ liệu (TCP)recvfrom(): Nhận dữ liệu (UDP)sendto(): Gửi dữ liệu (UDP)
Ví Dụ Server/Client Cơ Bản
Server (server.py)
import socket
import threading
def start_server():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('0.0.0.0', 5555))
server_socket.listen(8)
print("Chờ kết nối...")
while True:
client_conn, client_addr = server_socket.accept()
client_thread = threading.Thread(target=handle_client, args=(client_conn, client_addr))
client_thread.start()
def handle_client(conn, addr):
conn.send(b"Chào mừng đến máy chủ!")
while True:
data = conn.recv(1024)
if not data or data == b'quit':
break
print(f"Nhận từ {addr}: {data.decode()}")
conn.send(f"Phản hồi: {data.decode()}".encode())
conn.close()
if __name__ == "__main__":
start_server()
Client (client.py)
import socket
def start_client():
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(('127.0.0.1', 5555))
print(client_socket.recv(1024).decode())
while True:
msg = input("Nhập tin nhắn: ").encode()
client_socket.send(msg)
if msg == b'quit':
break
print("Phản hồi:", client_socket.recv(1024).decode())
client_socket.close()
if __name__ == "__main__":
start_client()
Truyền Tệp Qua Socket
Server (file_server.py)
import socket
import struct
import os
def handle_file_transfer(conn):
header_size = struct.calcsize('128sQ')
header = conn.recv(header_size)
filename, filesize = struct.unpack('128sQ', header)
filename = filename.decode().strip('\x00')
with open(f"received_{filename}", 'wb') as f:
bytes_received = 0
while bytes_received < filesize:
chunk = conn.recv(min(1024, filesize - bytes_received))
f.write(chunk)
bytes_received += len(chunk)
print(f"Đã nhận file: {filename}")
conn.close()
def start_file_server():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('0.0.0.0', 6666))
server.listen(5)
print("Chờ kết nối nhận file...")
while True:
conn, addr = server.accept()
handle_file_transfer(conn)
if __name__ == "__main__":
start_file_server()
Client (file_client.py)
import socket
import struct
import os
def send_file(filename):
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 6666))
file_size = os.path.getsize(filename)
header = struct.pack('128sQ', filename.encode(), file_size)
client.send(header)
with open(filename, 'rb') as f:
while chunk := f.read(1024):
client.send(chunk)
print(f"Đã gửi file: {filename}")
client.close()
if __name__ == "__main__":
send_file("test.jpg")
SocketServer: Framework Xây Dựng Server
SocketServer cung cấp lớp trừu tượng để xây dựng server mạng nhanh chóng, hỗ trợ đa luồng và đa tiến trình.
Thiết Kế Lớp
ThreadingTCPServer: Server TCP đa luồngForkingUDPServer: Server UDP đa tiến trình
Ví Dụ Server Đa Người Dùng
import socketserver
import logging
class MultiUserHandler(socketserver.BaseRequestHandler):
clients = {}
def setup(self):
self.clients[self.client_address] = self.request
logging.info(f"Kết nối mới từ {self.client_address}")
def handle(self):
while True:
data = self.request.recv(1024).decode()
if data == 'exit':
break
for addr, sock in self.clients.items():
if addr != self.client_address:
sock.send(f"[{self.client_address}] {data}".encode())
def finish(self):
del self.clients[self.client_address]
logging.info(f"Kết nối {self.client_address} đóng")
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
server = socketserver.ThreadingTCPServer(('0.0.0.0', 7777), MultiUserHandler)
server.serve_forever()