Hướng dẫn lập trình mạng socket Python (UDP và TCP)

Port

Trong hệ điều hành Linux, có 65536 (2 mũ 16) cổng, được phân loại như sau:

Cổng nổi tiếng (Well Known Ports): 0-1023, ví dụ cổng 80 được phân bổ cho dịch vụ HTTP, cổng 21 cho dịch vụ FTP.

Cổng động (Dynamic Ports): 1024-65535, thường không được phân bổ cố định cho một dịch vụ cụ thể mà được phân bổ động.

Địa chỉ IP

Mỗi địa chỉ IP bao gồm hai phần: địa chỉ mạng và địa chỉ máy chủ

Địa chỉ lớp A: 1.0.0.1-126.255.255.254

Địa chỉ lớp B: 128.1.0.1-191.255.255.254

Địa chỉ lớp C: 192.0.1.1-223.255.255.254 (thường dùng)

Địa chỉ lớp D dùng cho đa điểm phát sóng

Địa chỉ lớp E được giữ lại, chỉ dùng cho thử nghiệm và phát triển

Địa chỉ IP 127.0.0.1~127.255.255.255 dùng cho kiểm tra vòng lặp

Socket

Socket (viết tắt là socket) là một phương thức giao tiếp giữa các tiến trình, điểm khác biệt chính so với các phương thức giao tiếp giữa các tiến trình khác là: nó có thể thực hiện giao tiếp giữa các tiến trình trên các máy chủ khác nhau, hầu hết các dịch vụ trên mạng của chúng ta đều dựa trên Socket để hoàn thành giao tiếp.

Quy trình sử dụng socket:

  1. Tạo socket
  2. Sử dụng socket để gửi/nhận dữ liệu
  3. Đóng socket
import socket

# Tạo socket UDP
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Tạo socket TCP
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# ...đây là sử dụng chức năng của socket (bỏ qua)...
# Khi không dùng nữa, đóng socket
udp_socket.close()
tcp_socket.close()

UDP (User Datagram Protocol)

Tên tiếng Trung là giao thức dữ liệu người dùng, là một giao thức tầng truyền tải không kết nối trong mô hình tham chiếu OSI (Open System Interconnection).

Quy trình tạo một chương trình mạng dựa trên UDP rất đơn giản, các bước cụ thể như sau:

  1. Tạo socket client
  2. Gửi dữ liệu bằng sendto()/nhận dữ liệu bằng recvfrom()
  3. Đóng socket bằng close()

UDP gửi (client):

import socket

# 1. Tạo socket UDP
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 2. Chuẩn bị địa chỉ người nhận
# '192.168.1.103' biểu thị địa chỉ IP đích
# 8080 biểu thị cổng đích
dest_addr = ('192.168.1.103', 8080)  # Lưu ý là tuple, IP là chuỗi, cổng là số

# 3. Lấy dữ liệu từ bàn phím
send_data = input("Vui lòng nhập dữ liệu cần gửi:")

# 4. Gửi dữ liệu đến chương trình cụ thể trên máy tính cụ thể
udp_socket.sendto(send_data.encode('utf-8'), dest_addr)

# 5. Đóng socket
udp_socket.close()

UDP nhận (server):

import socket

# 1. Tạo socket UDP
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 2. Ràng buộc IP và cổng
udp_socket.bind('', 7788)

# 3. Chờ nhận dữ liệu được gửi từ phía bên kia
recv_data = udp_socket.recvfrom(1024)  # 1024 biểu thị số byte tối đa nhận được lần này

# 4. Hiển thị dữ liệu được gửi bởi phía bên kia
# Dữ liệu nhận được recv_data là một tuple
# Phần tử thứ nhất là dữ liệu được gửi bởi phía bên kia, cần sử dụng bộ mã ký tự khi gửi để giải mã
# Phần tử thứ hai là IP và cổng của phía bên kia
print(recv_data[0].decode('utf-8'))
print(recv_data[1])

# 5. Đóng socket
udp_socket.close()

Sử dụng socket kết nối đến máy chủ Alibaba Cloud

Client ở trên máy Windows 10 cục bộ, server ở trên máy chủ Alibaba Cloud, để họ có thể giao tiếp cần lưu ý ba điểm (yêu cầu là máy cục bộ có thể ping được IP công cộng của máy chủ Alibaba Cloud):

1、Server cần ràng buộc địa chỉ IP của máy chủ Alibaba Cloud là IP riêng, tức là IP trong ifconfig, hoặc ràng buộc địa chỉ IP là chuỗi rỗng '', điều này cũng sẽ mặc định ràng buộc IP riêng

2、Client cần kết nối địa chỉ IP của máy chủ Alibaba Cloud là IP công cộng, không phải IP riêng.

3、Trong bảng điều khiển Alibaba Cloud cần mở quyền cổng và giao thức tương ứng, nếu không sẽ không thể kết nối vào máy chủ Alibaba Cloud, giao thức là giao thức kết nối này, cổng là cổng được ràng buộc bởi server, đối tượng ủy quyền là IP của client hoặc 0.0.0.0/0 biểu thị tất cả IP đều có thể truy cập

TCP (Transmission Control Protocol)

Tên tiếng Trung là giao thức điều khiển truyền tải, là một giao thức giao tiếp tầng truyền tải dựa trên kết nối, đáng tin cậy, dựa trên luồng byte, được định nghĩa bởi RFC 793 của IETF.

Đặc điểm của TCP

1. Dựa trên kết nối

Phía giao tiếp phải thiết lập kết nối trước khi truyền dữ liệu, cả hai bên đều phải phân bổ tài nguyên kernel hệ thống cần thiết cho kết nối này để quản lý trạng thái kết nối và truyền trên kết nối.

Dữ liệu truyền giữa hai bên đều có thể được truyền qua kết nối này.

Sau khi hoàn thành việc trao đổi dữ liệu, cả hai bên phải ngắt kết nối này để giải phóng tài nguyên hệ thống.

Kết nối này là một-một, do đó TCP không phù hợp với các ứng dụng phát sóng, các ứng dụng dựa trên phát sóng nên sử dụng giao thức UDP.

2. Truyền tin cậy

1) TCP sử dụng cơ chế gửi và nhận

Mỗi đoạn tin được gửi bởi TCP đều phải nhận được xác nhận từ phía nhận trước khi được coi là đã truyền thành công

2) Gửi lại sau thời gian chờ

Sau khi gửi một đoạn tin, phía gửi sẽ khởi động bộ đếm thời gian, nếu không nhận được xác nhận trong thời gian đếm, sẽ gửi lại đoạn tin này.

Để đảm bảo không xảy ra mất gói tin, TCP gán một số thứ tự cho mỗi gói tin, đồng thời số thứ tự cũng đảm bảo việc nhận gói tin ở phía thực thể nhận theo đúng thứ tự. Sau đó, thực thể nhận gửi lại một xác nhận tương ứng (ACK) cho các gói tin đã nhận thành công; nếu thực thể gửi không nhận được xác nhận trong thời gian quay vòng hợp lý (RTT), gói tin tương ứng sẽ được coi là đã mất và sẽ được gửi lại.

3) Kiểm tra lỗi

TCP sử dụng hàm tổng kiểm tra để kiểm tra xem dữ liệu có lỗi không; tính tổng kiểm tra khi gửi và nhận.

4) Kiểm soát luồng và quản lý tắc nghẽn

Kiểm soát luồng dùng để tránh việc máy chủ gửi quá nhanh khiến phía nhận không kịp nhận hết.

Sự khác biệt giữa TCP và UDP

  • Dựa trên kết nối (xác nhận có tạo kết nối ba bên, kết nối đã tạo mới truyền.)
  • Truyền dữ liệu có thứ tự
  • Gửi lại gói tin bị mất
  • Bỏ qua gói tin trùng lặp
  • Truyền dữ liệu không có lỗi
  • Kiểm soát tắc nghẽn/luồng

Lập trình TCP

Client:

  1. Tạo socket client
  2. Kết nối server bằng connect()
  3. Gửi dữ liệu bằng send()
  4. Nhận dữ liệu trả về bằng recv()
  5. Đóng socket bằng close()
import socket


def chinh():
    # Tạo socket
    tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # Đặt địa chỉ server
    server_addr = ('xxx.xxx.xxx', 7890) # IP công cộng của máy chủ Alibaba Cloud
    # Tạo kết nối
    tcp_client.connect(server_addr)
    while True:
        # Lấy dữ liệu cần gửi
        send_data = input('Vui lòng nhập dữ liệu cần gửi (nhập exit để thoát):\n')
        if send_data == 'exit':
            break
        tcp_client.send(send_data.encode('utf-8'))
        # Nhận dữ liệu trả về từ server
        recv_data = tcp_client.recv(1024)
        print('Dữ liệu trả về:\n', recv_data.decode('utf-8'))
    tcp_client.close()


if __name__ == '__main__':
    chinh()

Server:

  1. Tạo socket server
  2. Ràng buộc IP và cổng bằng bind()
  3. Lắng nghe bằng listen() để socket trở thành có thể kết nối thụ động
  4. Chờ kết nối của client bằng accept(), trả về socket server mới để phục vụ client này
  5. Socket server mới nhận dữ liệu bằng recv()
  6. Trả dữ liệu về cho client bằng send()
  7. Đóng socket server mới bằng close()
  8. Đóng socket server lắng nghe bằng close()
import socket


def dich_vu_client(client_socket):
    while True:
        # Nhận dữ liệu
        recv_data = client_socket.recv(1024)
        if not recv_data:
            # Đóng socket dịch vụ
            client_socket.close()
            break
        print('Dữ liệu nhận được:', recv_data.decode('utf-8'))
        # Trả dữ liệu về
        client_socket.send('Đã nhận'.encode('utf-8'))


def chinh():
    # Tạo socket TCP
    tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # Ràng buộc cổng
    local_addr = ('', 7890)
    tcp_server.bind(local_addr)
    # Sử dụng listen để trở thành thụ động
    tcp_server.listen(128)
    while True:
        print('Chuẩn bị nhận kết nối.....')
        # Chờ kết nối client, nếu có client mới kết nối, trả về một socket mới để phục vụ client này
        client_socket, client_addr = tcp_server.accept()
        print('Đã nhận kết nối, IP:%s Cổng:%s' % (client_addr[0], client_addr[1]))
        dich_vu_client(client_socket)

    # Đóng socket lắng nghe
    tcp_server.close()
if __name__ == '__main__':
    chinh()

Thẻ: socket UDP TCP python mạng máy tính

Đăng vào ngày 24 tháng 6 lúc 17:39