Nghe trong ổ cắm python

Ổ cắm và API ổ cắm được sử dụng để gửi tin nhắn qua mạng. Họ cung cấp một hình thức giao tiếp giữa các quá trình [IPC]. Mạng có thể là mạng cục bộ, hợp lý với máy tính hoặc mạng được kết nối vật lý với mạng bên ngoài, có kết nối riêng với các mạng khác. Ví dụ rõ ràng là Internet mà bạn kết nối thông qua ISP của mình

Trong hướng dẫn này, bạn sẽ tạo

  • Một máy chủ và máy khách ổ cắm đơn giản
  • Phiên bản cải tiến xử lý đồng thời nhiều kết nối
  • Một ứng dụng máy chủ-máy khách có chức năng giống như một ứng dụng ổ cắm chính thức, hoàn chỉnh với tiêu đề và nội dung tùy chỉnh của riêng nó

Đến cuối hướng dẫn này, bạn sẽ hiểu cách sử dụng các hàm và phương thức chính trong mô-đun socket của Python để viết các ứng dụng máy khách-máy chủ của riêng bạn. Bạn sẽ biết cách sử dụng lớp tùy chỉnh để gửi tin nhắn và dữ liệu giữa các điểm cuối mà bạn có thể xây dựng và sử dụng cho các ứng dụng của riêng mình

Các ví dụ trong hướng dẫn này yêu cầu Python 3. 6 trở lên và đã được thử nghiệm bằng Python 3. 10. Để tận dụng tối đa hướng dẫn này, tốt nhất bạn nên tải xuống mã nguồn và có sẵn nó để tham khảo khi đọc

Nhận mã nguồn. Nhấp vào đây để lấy mã nguồn mà bạn sẽ sử dụng cho các ví dụ trong hướng dẫn này

Mạng và ổ cắm là những chủ đề lớn. Khối lượng văn học đã được viết về họ. Nếu bạn chưa quen với ổ cắm hoặc kết nối mạng, việc bạn cảm thấy choáng ngợp với tất cả các thuật ngữ và phần là hoàn toàn bình thường.

Đừng nản lòng mặc dù. Hướng dẫn này là dành cho bạn. Như với bất kỳ thứ gì liên quan đến Python, bạn có thể học từng chút một. Đánh dấu bài viết này và quay lại khi bạn đã sẵn sàng cho phần tiếp theo

Tiểu sử

Ổ cắm có một lịch sử lâu dài. Việc sử dụng chúng bắt nguồn từ ARPANET vào năm 1971 và sau đó trở thành API trong hệ điều hành Phân phối phần mềm Berkeley [BSD] phát hành năm 1983 được gọi là ổ cắm Berkeley

Khi Internet cất cánh vào những năm 1990 với World Wide Web, lập trình mạng cũng vậy. Máy chủ và trình duyệt web không phải là ứng dụng duy nhất tận dụng các mạng mới được kết nối và sử dụng ổ cắm. Các ứng dụng máy khách-máy chủ thuộc mọi loại và kích cỡ được sử dụng rộng rãi

Ngày nay, mặc dù các giao thức cơ bản được sử dụng bởi API ổ cắm đã phát triển qua nhiều năm và các giao thức mới đã được phát triển, nhưng API cấp thấp vẫn giữ nguyên

Loại ứng dụng ổ cắm phổ biến nhất là ứng dụng máy khách-máy chủ, trong đó một bên đóng vai trò là máy chủ và chờ kết nối từ máy khách. Đây là loại ứng dụng mà bạn sẽ tạo trong hướng dẫn này. Cụ thể hơn, bạn sẽ tập trung vào API ổ cắm cho ổ cắm Internet, đôi khi được gọi là ổ cắm Berkeley hoặc BSD. Ngoài ra còn có các ổ cắm tên miền Unix, chỉ có thể được sử dụng để giao tiếp giữa các quy trình trên cùng một máy chủ

Loại bỏ các quảng cáo

Tổng quan về API ổ cắm

Mô-đun ổ cắm của Python cung cấp giao diện cho API ổ cắm Berkeley. Đây là mô-đun mà bạn sẽ sử dụng trong hướng dẫn này

Các hàm và phương thức API ổ cắm chính trong mô-đun này là

  • $ python echo-server.py
    
    8
  • $ python echo-server.py
    
    9
  • # echo-server.py
    
    # ...
    
    with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
        s.bind[[HOST, PORT]]
        s.listen[]
        conn, addr = s.accept[]
        with conn:
            print[f"Connected by {addr}"]
            while True:
                data = conn.recv[1024]
                if not data:
                    break
                conn.sendall[data]
    
    0
  • # echo-server.py
    
    # ...
    
    with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
        s.bind[[HOST, PORT]]
        s.listen[]
        conn, addr = s.accept[]
        with conn:
            print[f"Connected by {addr}"]
            while True:
                data = conn.recv[1024]
                if not data:
                    break
                conn.sendall[data]
    
    1
  • # echo-server.py
    
    # ...
    
    with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
        s.bind[[HOST, PORT]]
        s.listen[]
        conn, addr = s.accept[]
        with conn:
            print[f"Connected by {addr}"]
            while True:
                data = conn.recv[1024]
                if not data:
                    break
                conn.sendall[data]
    
    2
  • # echo-server.py
    
    # ...
    
    with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
        s.bind[[HOST, PORT]]
        s.listen[]
        conn, addr = s.accept[]
        with conn:
            print[f"Connected by {addr}"]
            while True:
                data = conn.recv[1024]
                if not data:
                    break
                conn.sendall[data]
    
    3
  • # echo-server.py
    
    # ...
    
    with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
        s.bind[[HOST, PORT]]
        s.listen[]
        conn, addr = s.accept[]
        with conn:
            print[f"Connected by {addr}"]
            while True:
                data = conn.recv[1024]
                if not data:
                    break
                conn.sendall[data]
    
    4
  • # echo-server.py
    
    # ...
    
    with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
        s.bind[[HOST, PORT]]
        s.listen[]
        conn, addr = s.accept[]
        with conn:
            print[f"Connected by {addr}"]
            while True:
                data = conn.recv[1024]
                if not data:
                    break
                conn.sendall[data]
    
    5
  • # echo-server.py
    
    # ...
    
    with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
        s.bind[[HOST, PORT]]
        s.listen[]
        conn, addr = s.accept[]
        with conn:
            print[f"Connected by {addr}"]
            while True:
                data = conn.recv[1024]
                if not data:
                    break
                conn.sendall[data]
    
    6

Python cung cấp một API thuận tiện và nhất quán ánh xạ trực tiếp tới các lệnh gọi hệ thống, các đối tác C của chúng. Trong phần tiếp theo, bạn sẽ tìm hiểu cách chúng được sử dụng cùng nhau

Là một phần của thư viện chuẩn, Python cũng có các lớp giúp sử dụng các hàm ổ cắm cấp thấp này dễ dàng hơn. Mặc dù nó không được đề cập trong hướng dẫn này, nhưng bạn có thể xem mô-đun socketserver, một khung dành cho máy chủ mạng. Ngoài ra còn có nhiều mô-đun triển khai các giao thức Internet cấp cao hơn như HTTP và SMTP. Để biết tổng quan, hãy xem Hỗ trợ và Giao thức Internet

ổ cắm TCP

Bạn sẽ tạo một đối tượng ổ cắm bằng cách sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
7, chỉ định loại ổ cắm là
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
8. Khi bạn làm điều đó, giao thức mặc định được sử dụng là Giao thức điều khiển truyền dẫn [TCP]. Đây là một mặc định tốt và có thể là những gì bạn muốn

Tại sao bạn nên sử dụng TCP?

  • Đáng tin cậy. Các gói bị rơi trong mạng được phát hiện và truyền lại bởi người gửi
  • Có phân phối dữ liệu theo thứ tự. Dữ liệu được đọc bởi ứng dụng của bạn theo thứ tự được viết bởi người gửi

Ngược lại, ổ cắm Giao thức gói dữ liệu người dùng [UDP] được tạo bằng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
9 không đáng tin cậy và dữ liệu được đọc bởi người nhận có thể không theo thứ tự từ việc ghi của người gửi

Tại sao nó quan trọng? . Không có gì đảm bảo rằng dữ liệu của bạn sẽ đến đích hoặc bạn sẽ nhận được những gì đã được gửi cho bạn

Các thiết bị mạng, chẳng hạn như bộ định tuyến và bộ chuyển mạch, có sẵn băng thông hữu hạn và đi kèm với các giới hạn hệ thống vốn có của riêng chúng. Chúng có CPU, bộ nhớ, bus và bộ đệm gói giao diện, giống như máy khách và máy chủ của bạn. TCP giúp bạn không phải lo lắng về việc mất gói, dữ liệu đến không theo thứ tự và các cạm bẫy khác luôn xảy ra khi bạn liên lạc qua mạng

Để hiểu rõ hơn về điều này, hãy xem trình tự lệnh gọi API ổ cắm và luồng dữ liệu cho TCP

Luồng ổ cắm TCP [Nguồn hình ảnh]

Cột bên trái đại diện cho máy chủ. Ở phía bên tay phải là khách hàng

Bắt đầu từ cột trên cùng bên trái, hãy lưu ý các lệnh gọi API mà máy chủ thực hiện để thiết lập ổ cắm “nghe”

  • $ python echo-server.py
    
    8
  • $ python echo-server.py
    
    9
  • # echo-server.py
    
    # ...
    
    with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
        s.bind[[HOST, PORT]]
        s.listen[]
        conn, addr = s.accept[]
        with conn:
            print[f"Connected by {addr}"]
            while True:
                data = conn.recv[1024]
                if not data:
                    break
                conn.sendall[data]
    
    0
  • # echo-server.py
    
    # ...
    
    with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
        s.bind[[HOST, PORT]]
        s.listen[]
        conn, addr = s.accept[]
        with conn:
            print[f"Connected by {addr}"]
            while True:
                data = conn.recv[1024]
                if not data:
                    break
                conn.sendall[data]
    
    1

Ổ cắm nghe thực hiện đúng như tên gọi của nó. Nó lắng nghe các kết nối từ khách hàng. Khi máy khách kết nối, máy chủ gọi

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
1 để chấp nhận hoặc hoàn tất kết nối

Máy khách gọi

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
2 để thiết lập kết nối với máy chủ và bắt đầu quá trình bắt tay ba bước. Bước bắt tay rất quan trọng vì nó đảm bảo rằng mỗi bên của kết nối đều có thể truy cập được trong mạng, hay nói cách khác là máy khách có thể truy cập máy chủ và ngược lại. Có thể chỉ một máy chủ, máy khách hoặc máy chủ có thể kết nối với nhau

Ở giữa là phần khứ hồi, nơi dữ liệu được trao đổi giữa máy khách và máy chủ bằng cách gọi tới

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
4 và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
5

Ở phía dưới, máy khách và máy chủ đóng các ổ cắm tương ứng của chúng

Loại bỏ các quảng cáo

Máy khách và máy chủ Echo

Bây giờ bạn đã có cái nhìn tổng quan về API socket và cách máy khách và máy chủ giao tiếp với nhau, bạn đã sẵn sàng để tạo máy khách và máy chủ đầu tiên của mình. Bạn sẽ bắt đầu với một triển khai đơn giản. Máy chủ sẽ chỉ lặp lại bất cứ điều gì nó nhận được trở lại máy khách

Máy chủ tiếng vang

Đây là máy chủ

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
0

Ghi chú. Đừng lo lắng về việc hiểu mọi thứ ở trên ngay bây giờ. Có rất nhiều thứ đang diễn ra trong vài dòng mã này. Đây chỉ là điểm khởi đầu để bạn có thể thấy một máy chủ cơ bản đang hoạt động

Có một phần tham khảo ở cuối hướng dẫn này có thêm thông tin và liên kết đến các tài nguyên bổ sung. Bạn cũng sẽ tìm thấy những liên kết này và các liên kết hữu ích khác trong suốt hướng dẫn

Được rồi, vậy chính xác điều gì đang xảy ra trong lệnh gọi API?

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
7 tạo một đối tượng ổ cắm hỗ trợ loại trình quản lý bối cảnh, vì vậy bạn có thể sử dụng nó trong câu lệnh
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
29. Không cần gọi
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
30

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
4

Các đối số được truyền cho

$ python echo-server.py
8 là các hằng số được sử dụng để chỉ định họ địa chỉ và loại ổ cắm.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
32 là họ địa chỉ Internet cho IPv4.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
33 là loại ổ cắm cho TCP, giao thức sẽ được sử dụng để vận chuyển thông điệp trong mạng

Phương pháp

$ python echo-server.py
9 được sử dụng để liên kết ổ cắm với một giao diện mạng và số cổng cụ thể

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
9

Các giá trị được chuyển đến

$ python echo-server.py
9 phụ thuộc vào họ địa chỉ của ổ cắm. Trong ví dụ này, bạn đang sử dụng
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
36 [IPv4]. Vì vậy, nó mong đợi một hai tuple.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
37

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
38 có thể là tên máy chủ, địa chỉ IP hoặc chuỗi rỗng. Nếu địa chỉ IP được sử dụng, thì
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
38 phải là chuỗi địa chỉ có định dạng IPv4. Địa chỉ IP
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
400 là địa chỉ IPv4 tiêu chuẩn cho giao diện loopback, vì vậy chỉ các quy trình trên máy chủ mới có thể kết nối với máy chủ. Nếu bạn chuyển một chuỗi trống, máy chủ sẽ chấp nhận các kết nối trên tất cả các giao diện IPv4 có sẵn

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
401 đại diện cho số cổng TCP để chấp nhận kết nối từ máy khách. Nó phải là một số nguyên từ
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
402 đến
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
403, vì
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
404 được đặt trước. Một số hệ thống có thể yêu cầu đặc quyền siêu người dùng nếu số cổng nhỏ hơn
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
405

Đây là một lưu ý về việc sử dụng tên máy chủ với

$ python echo-server.py
9

“Nếu bạn sử dụng tên máy chủ trong phần máy chủ của địa chỉ ổ cắm IPv4/v6, chương trình có thể hiển thị hành vi không xác định, vì Python sử dụng địa chỉ đầu tiên được trả về từ độ phân giải DNS. Địa chỉ ổ cắm sẽ được phân giải khác thành địa chỉ IPv4/v6 thực tế, tùy thuộc vào kết quả từ độ phân giải DNS và/hoặc cấu hình máy chủ. Đối với hành vi xác định, hãy sử dụng địa chỉ số trong phần máy chủ. " [Nguồn]

Bạn sẽ tìm hiểu thêm về điều này sau, trong Sử dụng tên máy chủ. Hiện tại, bạn chỉ cần hiểu rằng khi sử dụng tên máy chủ, bạn có thể thấy các kết quả khác nhau tùy thuộc vào những gì được trả về từ quá trình phân giải tên. Những kết quả này có thể là bất cứ điều gì. Lần đầu tiên bạn chạy ứng dụng của mình, bạn có thể nhận được địa chỉ

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
407. Lần sau, bạn nhận được một địa chỉ khác,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
408. Lần thứ ba, bạn có thể nhận được
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
409, v.v.

Trong ví dụ máy chủ,

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
0 cho phép máy chủ chấp nhận kết nối. Nó làm cho máy chủ trở thành ổ cắm “nghe”

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
6

Phương thức

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
0 có tham số
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
412. Nó chỉ định số lượng kết nối không được chấp nhận mà hệ thống sẽ cho phép trước khi từ chối kết nối mới. Bắt đầu bằng Python 3. 5, nó là tùy chọn. Nếu không được chỉ định, giá trị
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
412 mặc định sẽ được chọn

Nếu máy chủ của bạn nhận được nhiều yêu cầu kết nối đồng thời, việc tăng giá trị

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
412 có thể hữu ích bằng cách đặt độ dài tối đa của hàng đợi cho các kết nối đang chờ xử lý. Giá trị tối đa phụ thuộc vào hệ thống. Ví dụ: trên Linux, xem
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
415

Phương thức

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
1 chặn thực thi và đợi kết nối đến. Khi một máy khách kết nối, nó trả về một đối tượng ổ cắm mới đại diện cho kết nối và một bộ chứa địa chỉ của máy khách. Bộ dữ liệu sẽ chứa
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
37 cho các kết nối IPv4 hoặc
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
418 cho IPv6. Xem Các họ địa chỉ ổ cắm trong phần tham khảo để biết chi tiết về các giá trị bộ dữ liệu

Một điều bắt buộc phải hiểu là bây giờ bạn có một đối tượng ổ cắm mới từ

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
1. Điều này rất quan trọng vì nó là ổ cắm mà bạn sẽ sử dụng để giao tiếp với khách hàng. Nó khác với ổ cắm nghe mà máy chủ đang sử dụng để chấp nhận các kết nối mới

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
6

Sau khi

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
1 cung cấp đối tượng ổ cắm máy khách
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
421, một vòng lặp vô hạn
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
422 được sử dụng để lặp lại các cuộc gọi chặn tới
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
423. Điều này đọc bất kỳ dữ liệu nào mà khách hàng gửi và lặp lại dữ liệu đó bằng cách sử dụng
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
424

Nếu

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
423 trả về một đối tượng
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
426 trống,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
427, báo hiệu rằng máy khách đã đóng kết nối và vòng lặp kết thúc. Câu lệnh
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
29 được sử dụng với
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
421 để tự động đóng socket ở cuối khối

Loại bỏ các quảng cáo

Máy khách Echo

Bây giờ hãy nhìn vào khách hàng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
7

So với máy chủ, máy khách khá đơn giản. Nó tạo một đối tượng ổ cắm, sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
2 để kết nối với máy chủ và gọi
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
431 để gửi tin nhắn của nó. Cuối cùng, nó gọi
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
432 để đọc câu trả lời của máy chủ và sau đó in nó

Chạy máy khách và máy chủ Echo

Trong phần này, bạn sẽ chạy máy khách và máy chủ để xem cách chúng hoạt động và kiểm tra điều gì đang xảy ra

Ghi chú. Nếu bạn gặp khó khăn khi lấy các ví dụ hoặc mã của riêng mình để chạy từ dòng lệnh, hãy đọc Làm cách nào để tôi tạo các lệnh dòng lệnh của riêng mình bằng Python? . Nếu bạn đang sử dụng Windows, hãy xem Câu hỏi thường gặp về Windows về Python

Mở một thiết bị đầu cuối hoặc dấu nhắc lệnh, điều hướng đến thư mục chứa tập lệnh của bạn, đảm bảo rằng bạn có Python 3. 6 trở lên được cài đặt và trên đường dẫn của bạn, sau đó chạy máy chủ

$ python echo-server.py

Thiết bị đầu cuối của bạn sẽ xuất hiện để treo. Đó là do máy chủ bị chặn hoặc bị treo vào ngày

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
1

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]

Nó đang chờ kết nối máy khách. Bây giờ, hãy mở một cửa sổ đầu cuối khác hoặc dấu nhắc lệnh và chạy ứng dụng khách

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
2

Trong cửa sổ máy chủ, bạn sẽ nhận thấy một cái gì đó như thế này

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
3

Trong kết quả ở trên, máy chủ đã in bộ dữ liệu

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
434 được trả về từ
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
435. Đây là địa chỉ IP và số cổng TCP của máy khách. Số cổng,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
436, rất có thể sẽ khác khi bạn chạy nó trên máy của mình

Xem trạng thái ổ cắm

Để xem trạng thái hiện tại của ổ cắm trên máy chủ của bạn, hãy sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
437. Nó có sẵn theo mặc định trên macOS, Linux và Windows

Đây là đầu ra netstat từ macOS sau khi khởi động máy chủ

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
40

Lưu ý rằng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
438 là
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
439. Nếu
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
440 đã sử dụng
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
441 thay vì
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
442, netstat sẽ hiển thị điều này

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
41

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
438 là
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
444, có nghĩa là tất cả các giao diện máy chủ có sẵn hỗ trợ họ địa chỉ sẽ được sử dụng để chấp nhận các kết nối đến. Trong ví dụ này,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
36 đã được sử dụng [IPv4] trong lệnh gọi tới
$ python echo-server.py
8. Bạn có thể thấy điều này trong cột
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
447.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
448

Đầu ra ở trên được cắt bớt để chỉ hiển thị máy chủ tiếng vang. Bạn có thể sẽ thấy nhiều đầu ra hơn, tùy thuộc vào hệ thống mà bạn đang chạy nó. Những điều cần chú ý là các cột

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
447,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
438 và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
451. Trong ví dụ cuối cùng ở trên, netstat cho thấy rằng máy chủ echo đang sử dụng ổ cắm IPv4 TCP [
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
448], trên cổng 65432 trên tất cả các giao diện [
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
444] và nó đang ở trạng thái lắng nghe [
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
454]

Một cách khác để truy cập điều này, cùng với thông tin hữu ích bổ sung, là sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
455 [liệt kê các tệp đang mở]. Nó có sẵn theo mặc định trên macOS và có thể được cài đặt trên Linux bằng trình quản lý gói của bạn, nếu nó chưa có

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
42

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
455 cung cấp cho bạn
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
457,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
458 [ID quy trình] và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
459 [ID người dùng] của ổ cắm Internet đang mở khi được sử dụng với tùy chọn
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
460. Trên đây là quá trình echo server

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
437 và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
455 có rất nhiều tùy chọn khả dụng và khác nhau tùy thuộc vào hệ điều hành mà bạn đang chạy chúng. Kiểm tra trang
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
463 hoặc tài liệu cho cả hai. Họ chắc chắn đáng để dành một chút thời gian và tìm hiểu. Bạn sẽ được thưởng. Trên macOS và Linux, hãy sử dụng
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
464 và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
465. Đối với Windows, hãy sử dụng
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
466

Đây là một lỗi phổ biến mà bạn sẽ gặp phải khi thử kết nối với một cổng không có ổ cắm nghe

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
43

Số cổng được chỉ định sai hoặc máy chủ không chạy. Hoặc có thể có tường lửa trong đường dẫn đang chặn kết nối, điều này có thể dễ dàng bị quên. Bạn cũng có thể thấy lỗi

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
467. Thêm quy tắc tường lửa cho phép máy khách kết nối với cổng TCP

Có một danh sách các lỗi phổ biến trong phần tham khảo

Loại bỏ các quảng cáo

sự cố truyền thông

Bây giờ bạn sẽ xem xét kỹ hơn cách máy khách và máy chủ giao tiếp với nhau

Khi sử dụng giao diện loopback [địa chỉ IPv4

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
400 hoặc địa chỉ IPv6
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
469], dữ liệu không bao giờ rời khỏi máy chủ hoặc chạm vào mạng bên ngoài. Trong sơ đồ trên, giao diện loopback được chứa bên trong máy chủ. Điều này thể hiện bản chất bên trong của giao diện loopback và cho thấy rằng các kết nối và dữ liệu truyền qua nó là cục bộ của máy chủ. Đây là lý do tại sao bạn cũng sẽ nghe thấy giao diện loopback và địa chỉ IP
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
400 hoặc
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
469 được gọi là “localhost. ”

Các ứng dụng sử dụng giao diện loopback để liên lạc với các quy trình khác đang chạy trên máy chủ và để bảo mật và cách ly với mạng bên ngoài. Bởi vì nó là nội bộ và chỉ có thể truy cập từ bên trong máy chủ nên nó không bị lộ

Bạn có thể thấy điều này đang hoạt động nếu bạn có một máy chủ ứng dụng sử dụng cơ sở dữ liệu riêng của nó. Nếu nó không phải là cơ sở dữ liệu được sử dụng bởi các máy chủ khác, thì nó có thể được định cấu hình để chỉ lắng nghe các kết nối trên giao diện loopback. Nếu đây là trường hợp, các máy chủ khác trên mạng không thể kết nối với nó

Khi bạn sử dụng địa chỉ IP không phải là

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
400 hoặc
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
469 trong các ứng dụng của mình, địa chỉ đó có thể bị ràng buộc với giao diện Ethernet được kết nối với mạng bên ngoài. Đây là cổng vào các máy chủ khác bên ngoài vương quốc “localhost” của bạn

Hãy cẩn thận ở ngoài đó. Đó là một thế giới khó chịu, tàn nhẫn. Hãy nhớ đọc phần Sử dụng tên máy chủ trước khi mạo hiểm thoát khỏi giới hạn an toàn của “localhost. ” Có một lưu ý bảo mật áp dụng ngay cả khi bạn không sử dụng tên máy chủ mà chỉ sử dụng địa chỉ IP

Xử lý nhiều kết nối

Máy chủ echo chắc chắn có những hạn chế của nó. Cái lớn nhất là nó chỉ phục vụ một khách hàng và sau đó thoát. Ứng dụng khách echo cũng có giới hạn này, nhưng có một vấn đề khác. Khi máy khách sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
432, có thể nó sẽ chỉ trả về một byte,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
475 từ
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
476

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
44

Đối số

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
477 của
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
405 được sử dụng ở trên là lượng dữ liệu tối đa được nhận cùng một lúc. Điều đó không có nghĩa là
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
5 sẽ trả về
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
405 byte

Phương thức

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
4 cũng hoạt động theo cách này. Nó trả về số byte đã gửi, có thể nhỏ hơn kích thước của dữ liệu được truyền vào. Bạn chịu trách nhiệm kiểm tra điều này và gọi cho
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
4 nhiều lần nếu cần để gửi tất cả dữ liệu

“Các ứng dụng có trách nhiệm kiểm tra xem tất cả dữ liệu đã được gửi chưa; . " [Nguồn]

Trong ví dụ trên, bạn tránh phải làm điều này bằng cách sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
483

“Không giống như send[], phương thức này tiếp tục gửi dữ liệu từ byte cho đến khi tất cả dữ liệu đã được gửi hoặc xảy ra lỗi.

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
484 được trả lại khi thành công. " [Nguồn]

Bạn có hai vấn đề tại thời điểm này

  • Làm thế nào để bạn xử lý nhiều kết nối đồng thời?
  • Bạn cần gọi
    # echo-server.py
    
    # ...
    
    with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
        s.bind[[HOST, PORT]]
        s.listen[]
        conn, addr = s.accept[]
        with conn:
            print[f"Connected by {addr}"]
            while True:
                data = conn.recv[1024]
                if not data:
                    break
                conn.sendall[data]
    
    4 và
    # echo-server.py
    
    # ...
    
    with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
        s.bind[[HOST, PORT]]
        s.listen[]
        conn, addr = s.accept[]
        with conn:
            print[f"Connected by {addr}"]
            while True:
                data = conn.recv[1024]
                if not data:
                    break
                conn.sendall[data]
    
    5 cho đến khi tất cả dữ liệu được gửi hoặc nhận

Bạn có thể làm gì? . Một cách tiếp cận phổ biến là sử dụng I/O không đồng bộ.

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
487 đã được đưa vào thư viện chuẩn trong Python 3. 4. Sự lựa chọn truyền thống là sử dụng chủ đề

Rắc rối với đồng thời là rất khó để hiểu đúng. Có nhiều điều tế nhị để xem xét và đề phòng. Tất cả những gì cần làm là để một trong số này tự hiển thị và ứng dụng của bạn có thể đột nhiên bị lỗi theo những cách không mấy tế nhị

Điều này không có nghĩa là làm bạn sợ hãi khi học và sử dụng lập trình đồng thời. Nếu ứng dụng của bạn cần mở rộng quy mô, thì đó là điều cần thiết nếu bạn muốn sử dụng nhiều bộ xử lý hoặc một lõi. Tuy nhiên, đối với hướng dẫn này, bạn sẽ sử dụng thứ gì đó thậm chí còn truyền thống hơn các chủ đề và dễ suy luận hơn. Bạn sẽ sử dụng các cuộc gọi hệ thống.

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488

Phương pháp

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488 cho phép bạn kiểm tra việc hoàn thành I/O trên nhiều ổ cắm. Vì vậy, bạn có thể gọi
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488 để xem ổ cắm nào có sẵn I/O để đọc và/hoặc ghi. Nhưng đây là Python, vì vậy có nhiều hơn nữa. Bạn sẽ sử dụng mô-đun bộ chọn trong thư viện chuẩn để triển khai hiệu quả nhất được sử dụng, bất kể hệ điều hành bạn đang chạy trên đó là gì.

“Mô-đun này cho phép ghép kênh I/O ở mức độ cao và hiệu quả, được xây dựng dựa trên các nguyên mẫu mô-đun đã chọn. Thay vào đó, người dùng được khuyến khích sử dụng mô-đun này, trừ khi họ muốn kiểm soát chính xác các nguyên mẫu cấp hệ điều hành được sử dụng. " [Nguồn]

Tuy nhiên, bằng cách sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488, bạn không thể chạy đồng thời. Điều đó nói rằng, tùy thuộc vào khối lượng công việc của bạn, phương pháp này có thể vẫn còn rất nhanh. Nó phụ thuộc vào những gì ứng dụng của bạn cần thực hiện khi nó phục vụ một yêu cầu và số lượng máy khách mà nó cần hỗ trợ

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
487 sử dụng đa nhiệm hợp tác đơn luồng và vòng lặp sự kiện để quản lý tác vụ. Với
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488, bạn sẽ viết phiên bản vòng lặp sự kiện của riêng mình, mặc dù đơn giản và đồng bộ hơn. Khi sử dụng nhiều luồng, mặc dù bạn có đồng thời, nhưng hiện tại bạn phải sử dụng GIL [Khóa phiên dịch toàn cầu] với CPython và PyPy. Điều này hạn chế hiệu quả số lượng công việc bạn có thể làm song song

Đây là tất cả để nói rằng sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488 có thể là một lựa chọn hoàn toàn tốt. Đừng cảm thấy như bạn phải sử dụng
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
487, chủ đề hoặc thư viện không đồng bộ mới nhất. Thông thường, trong ứng dụng mạng, ứng dụng của bạn vẫn bị ràng buộc I/O. nó có thể đang đợi trên mạng cục bộ, các điểm cuối ở phía bên kia của mạng, để ghi đĩa, v.v.

Nếu bạn đang nhận được yêu cầu từ các máy khách bắt đầu công việc liên kết với CPU, hãy xem đồng thời. mô-đun tương lai. Nó chứa lớp ProcessPoolExecutor, sử dụng một nhóm các quy trình để thực hiện các lệnh gọi không đồng bộ

Nếu bạn sử dụng nhiều quy trình, hệ điều hành có thể lên lịch mã Python của bạn để chạy song song trên nhiều bộ xử lý hoặc lõi mà không cần GIL. Để có ý tưởng và cảm hứng, hãy xem buổi nói chuyện về PyCon John Reese - Tư duy bên ngoài GIL với AsyncIO và Đa xử lý - PyCon 2018

Trong phần tiếp theo, bạn sẽ xem xét các ví dụ về máy chủ và máy khách giải quyết những vấn đề này. Họ sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488 để xử lý đồng thời nhiều kết nối và gọi
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
4 và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
5 nhiều lần nếu cần

Loại bỏ các quảng cáo

Máy khách và máy chủ đa kết nối

Trong hai phần tiếp theo, bạn sẽ tạo một máy chủ và máy khách xử lý nhiều kết nối bằng cách sử dụng đối tượng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
499 được tạo từ mô-đun bộ chọn

Máy chủ đa kết nối

Đầu tiên, hướng sự chú ý của bạn đến máy chủ đa kết nối. Phần đầu tiên thiết lập ổ cắm nghe

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
45

Sự khác biệt lớn nhất giữa máy chủ này và máy chủ echo là lệnh gọi tới

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
900 để định cấu hình ổ cắm ở chế độ không chặn. Các cuộc gọi đến ổ cắm này sẽ không còn bị chặn. Khi nó được sử dụng với
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
901, như bạn sẽ thấy bên dưới, bạn có thể đợi các sự kiện trên một hoặc nhiều ổ cắm, sau đó đọc và ghi dữ liệu khi sẵn sàng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
902 đăng ký ổ cắm sẽ được giám sát với
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
901 cho các sự kiện mà bạn quan tâm. Đối với ổ cắm nghe, bạn muốn đọc các sự kiện.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
904

Để lưu trữ bất kỳ dữ liệu tùy ý nào bạn muốn cùng với ổ cắm, bạn sẽ sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
905. Nó được trả lại khi
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488 trả về. Bạn sẽ sử dụng
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
905 để theo dõi những gì đã được gửi và nhận trên ổ cắm

Tiếp theo là vòng lặp sự kiện

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
46

Khối

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
908 cho đến khi có ổ cắm sẵn sàng cho I/O. Nó trả về một danh sách các bộ dữ liệu, một bộ cho mỗi ổ cắm. Mỗi bộ dữ liệu chứa một
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
909 và một
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
910.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
909 là SelectorKey
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
912 có chứa thuộc tính
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
913.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
914 là đối tượng ổ cắm và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
910 là mặt nạ sự kiện của các hoạt động đã sẵn sàng

Nếu

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
916 là
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
484, thì bạn biết đó là từ ổ cắm đang nghe và bạn cần chấp nhận kết nối. Bạn sẽ gọi hàm
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
918 của riêng mình để lấy đối tượng ổ cắm mới và đăng ký nó với bộ chọn. Bạn sẽ nhìn vào đó trong giây lát

Nếu

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
916 không phải là
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
484, thì bạn biết đó là ổ cắm máy khách đã được chấp nhận và bạn cần bảo dưỡng nó. Sau đó,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
921 được gọi với
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
909 và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
910 làm đối số và đó là mọi thứ bạn cần để vận hành trên ổ cắm

Đây là chức năng của hàm

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
918 của bạn

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
47

Bởi vì ổ cắm nghe đã được đăng ký cho sự kiện

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
904, nó sẽ sẵn sàng để đọc. Bạn gọi
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
926 rồi gọi
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
927 để đặt ổ cắm ở chế độ non-blocking

Hãy nhớ rằng, đây là mục tiêu chính trong phiên bản máy chủ này vì bạn không muốn nó bị chặn. Nếu nó chặn, thì toàn bộ máy chủ sẽ bị đình trệ cho đến khi nó hoạt động trở lại. Điều đó có nghĩa là các ổ cắm khác đang chờ mặc dù máy chủ không hoạt động tích cực. Đây là trạng thái “treo” đáng sợ mà bạn không muốn máy chủ của mình gặp phải

Tiếp theo, bạn tạo một đối tượng để chứa dữ liệu mà bạn muốn đưa vào cùng với ổ cắm bằng cách sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
928. Bởi vì bạn muốn biết khi nào kết nối máy khách sẵn sàng để đọc và ghi, cả hai sự kiện đó đều được đặt bằng toán tử OR theo bit

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
48

Sau đó, các đối tượng mặt nạ, ổ cắm và dữ liệu của

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
929 được chuyển đến
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
902

Bây giờ hãy xem

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
921 để xem kết nối máy khách được xử lý như thế nào khi sẵn sàng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
49

Đây là trái tim của máy chủ đa kết nối đơn giản.

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
909 là
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
912 được trả về từ
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488 có chứa đối tượng ổ cắm [
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
913] và đối tượng dữ liệu.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
910 chứa các sự kiện đã sẵn sàng

Nếu ổ cắm đã sẵn sàng để đọc, thì

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
937 sẽ đánh giá thành
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
938, do đó,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
939 được gọi là. Bất kỳ dữ liệu nào được đọc đều được thêm vào _______ 2940 để có thể gửi sau

Lưu ý khối

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
941 để kiểm tra nếu không nhận được dữ liệu

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
90

Nếu không nhận được dữ liệu, điều này có nghĩa là máy khách đã đóng ổ cắm của họ, vì vậy máy chủ cũng vậy. Nhưng đừng quên gọi cho

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
942 trước khi đóng cửa, để nó không còn bị giám sát bởi
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488

Khi ổ cắm đã sẵn sàng để ghi, đây luôn là trường hợp đối với ổ cắm khỏe mạnh, mọi dữ liệu đã nhận được lưu trữ trong

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
940 sẽ được gửi lại cho máy khách bằng cách sử dụng
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
945. Các byte được gửi sau đó được xóa khỏi bộ đệm gửi

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
91

Phương thức

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
4 trả về số byte đã gửi. Số này sau đó có thể được sử dụng với ký hiệu lát cắt trên bộ đệm
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
947 để loại bỏ các byte đã gửi

Loại bỏ các quảng cáo

Máy khách đa kết nối

Bây giờ hãy xem ứng dụng khách đa kết nối,

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
948. Nó rất giống với máy chủ, nhưng thay vì lắng nghe kết nối, nó bắt đầu bằng cách bắt đầu kết nối qua
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
949

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
92

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
950 được đọc từ dòng lệnh và là số lượng kết nối cần tạo tới máy chủ. Giống như máy chủ, mỗi ổ cắm được đặt ở chế độ không chặn

Bạn sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
3 thay vì
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
2 vì
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
2 sẽ ngay lập tức đưa ra một ngoại lệ
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
954. Phương pháp
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
3 ban đầu trả về một chỉ báo lỗi,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
956, thay vì đưa ra một ngoại lệ có thể cản trở kết nối đang diễn ra. Sau khi kết nối hoàn tất, ổ cắm đã sẵn sàng để đọc và ghi và được trả về bởi
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488

Sau khi ổ cắm được thiết lập, dữ liệu bạn muốn lưu trữ với ổ cắm được tạo bằng cách sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
928. Các tin nhắn mà máy khách sẽ gửi đến máy chủ được sao chép bằng cách sử dụng
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
959 vì mỗi kết nối sẽ gọi
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
960 và sửa đổi danh sách. Mọi thứ cần thiết để theo dõi những gì khách hàng cần gửi, đã gửi và đã nhận, bao gồm tổng số byte trong tin nhắn, được lưu trữ trong đối tượng
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
905

Kiểm tra các thay đổi được thực hiện từ

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
921 của máy chủ đối với phiên bản của máy khách

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
93

Về cơ bản là giống nhau nhưng có một điểm khác biệt quan trọng. Máy khách theo dõi số byte mà nó nhận được từ máy chủ để có thể đóng phần kết nối của nó. Khi máy chủ phát hiện ra điều này, nó cũng sẽ đóng phần kết nối của nó

Lưu ý rằng bằng cách này, máy chủ phụ thuộc vào máy khách có hoạt động tốt không. máy chủ yêu cầu máy khách đóng phía kết nối của nó khi gửi tin nhắn xong. Nếu máy khách không đóng, máy chủ sẽ để kết nối mở. Trong một ứng dụng thực tế, bạn có thể muốn bảo vệ chống lại điều này trong máy chủ của mình bằng cách triển khai thời gian chờ để ngăn các kết nối máy khách tích lũy nếu chúng không gửi yêu cầu sau một khoảng thời gian nhất định

Chạy máy khách và máy chủ đa kết nối

Bây giờ là lúc để chạy

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
963 và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
948. Cả hai đều sử dụng đối số dòng lệnh. Bạn có thể chạy chúng mà không cần đối số để xem các tùy chọn

Đối với máy chủ, hãy chuyển số

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
38 và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
401

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
94

Đối với máy khách, cũng chuyển số lượng kết nối cần tạo tới máy chủ,

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
967

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
95

Dưới đây là đầu ra của máy chủ khi nghe trên giao diện loopback trên cổng 65432

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
96

Dưới đây là đầu ra của máy khách khi nó tạo hai kết nối đến máy chủ ở trên

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
97

Tuyệt quá. Bây giờ bạn đã chạy máy khách và máy chủ đa kết nối. Trong phần tiếp theo, bạn sẽ sử dụng ví dụ này nhiều hơn nữa

Loại bỏ các quảng cáo

Máy khách và máy chủ ứng dụng

Ví dụ về máy khách và máy chủ đa kết nối chắc chắn là một cải tiến so với nơi bạn bắt đầu. Tuy nhiên, bây giờ bạn có thể thực hiện thêm một bước nữa và giải quyết những thiếu sót của ví dụ

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
968 trước đó trong lần triển khai cuối cùng. ứng dụng khách và máy chủ

Bạn muốn có một máy khách và máy chủ xử lý lỗi một cách thích hợp để các kết nối khác không bị ảnh hưởng. Rõ ràng, máy khách hoặc máy chủ của bạn sẽ không bị sập trong cơn thịnh nộ nếu một ngoại lệ không bị phát hiện. Đây là điều mà bạn không phải lo lắng cho đến bây giờ, bởi vì các ví dụ đã cố tình bỏ qua việc xử lý lỗi để cho ngắn gọn và rõ ràng

Bây giờ bạn đã quen thuộc với API cơ bản, ổ cắm không chặn và

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488, bạn có thể thêm một số cách xử lý lỗi và xử lý con voi trong phòng, mà các ví dụ đã giấu bạn đằng sau bức màn lớn đằng kia. Hãy nhớ rằng lớp tùy chỉnh đã được đề cập trở lại trong phần giới thiệu?

Đầu tiên, bạn sẽ giải quyết các lỗi

“Tất cả các lỗi đều có ngoại lệ. Các ngoại lệ thông thường đối với các loại đối số không hợp lệ và các điều kiện hết bộ nhớ có thể được nêu ra; . 3, các lỗi liên quan đến ngữ nghĩa ổ cắm hoặc địa chỉ nâng cao

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
970 hoặc một trong các lớp con của nó. " [Nguồn]

Vì vậy, một điều bạn cần làm là bắt

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
970. Một cân nhắc quan trọng khác liên quan đến lỗi là thời gian chờ. Bạn sẽ thấy chúng được thảo luận ở nhiều nơi trong tài liệu. Hết thời gian chờ xảy ra và được gọi là lỗi bình thường. Máy chủ và bộ định tuyến được khởi động lại, cổng chuyển đổi bị hỏng, cáp bị hỏng, cáp bị rút phích cắm, bạn đặt tên cho nó. Bạn nên chuẩn bị cho những lỗi này và các lỗi khác, xử lý chúng trong mã của bạn

Còn con voi trong phòng thì sao? . Nó giống như đọc từ một tệp trên đĩa, nhưng thay vào đó bạn đang đọc các byte từ mạng. Tuy nhiên, không giống như đọc tệp, không có

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
973

Nói cách khác, bạn không thể định vị lại con trỏ ổ cắm, nếu có và di chuyển xung quanh dữ liệu

Khi byte đến ổ cắm của bạn, có bộ đệm mạng liên quan. Sau khi bạn đã đọc chúng, chúng cần được lưu ở đâu đó, nếu không bạn sẽ đánh rơi chúng. Gọi lại

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
5 để đọc luồng byte tiếp theo có sẵn từ ổ cắm

Bạn sẽ đọc từ ổ cắm theo khối. Vì vậy, bạn cần gọi

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
5 và lưu dữ liệu vào bộ đệm cho đến khi bạn đọc đủ byte để có một thông báo hoàn chỉnh có ý nghĩa đối với ứng dụng của bạn

Tùy thuộc vào bạn để xác định và theo dõi vị trí của ranh giới tin nhắn. Đối với ổ cắm TCP, nó chỉ gửi và nhận các byte thô đến và từ mạng. Nó không biết gì về ý nghĩa của những byte thô đó

Đây là lý do tại sao bạn cần xác định giao thức tầng ứng dụng. Giao thức tầng ứng dụng là gì? . Định dạng của những thông báo này là giao thức của ứng dụng của bạn

Nói cách khác, độ dài và định dạng mà bạn chọn cho các thông báo này sẽ xác định ngữ nghĩa và hành vi của ứng dụng của bạn. Điều này liên quan trực tiếp đến những gì bạn đã học trong đoạn trước về việc đọc byte từ ổ cắm. Khi bạn đang đọc các byte bằng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
5, bạn cần theo dõi số lượng byte đã được đọc và tìm ra ranh giới của thông báo nằm ở đâu

Làm thế nào bạn có thể làm điều này? . Nếu chúng luôn có cùng kích thước, thì thật dễ dàng. Khi bạn đã đọc số byte đó vào bộ đệm, thì bạn biết mình có một thông báo hoàn chỉnh

Tuy nhiên, việc sử dụng các tin nhắn có độ dài cố định sẽ không hiệu quả đối với các tin nhắn nhỏ mà bạn cần sử dụng phần đệm để điền vào chúng. Ngoài ra, bạn vẫn gặp phải vấn đề phải làm gì với dữ liệu không phù hợp với một tin nhắn

Trong hướng dẫn này, bạn sẽ học một cách tiếp cận chung, một cách tiếp cận được sử dụng bởi nhiều giao thức, bao gồm cả HTTP. Bạn sẽ thêm tiền tố vào thư với tiêu đề bao gồm độ dài nội dung cũng như bất kỳ trường nào khác mà bạn cần. Bằng cách này, bạn sẽ chỉ cần theo kịp tiêu đề. Khi bạn đã đọc tiêu đề, bạn có thể xử lý nó để xác định độ dài của nội dung thư. Với độ dài nội dung, bạn có thể đọc số byte đó để sử dụng nó

Bạn sẽ thực hiện điều này bằng cách tạo một lớp tùy chỉnh có thể gửi và nhận tin nhắn chứa dữ liệu văn bản hoặc nhị phân. Bạn có thể cải thiện và mở rộng lớp này cho các ứng dụng của riêng mình. Điều quan trọng nhất là bạn sẽ có thể xem một ví dụ về cách thực hiện điều này

Trước khi bắt đầu, có một số điều bạn cần biết về socket và byte. Như bạn đã biết trước đó, khi gửi và nhận dữ liệu qua socket, bạn đang gửi và nhận các byte thô

Nếu bạn nhận được dữ liệu và muốn sử dụng dữ liệu đó trong ngữ cảnh mà dữ liệu đó được hiểu là nhiều byte, chẳng hạn như số nguyên 4 byte, bạn sẽ cần tính đến việc dữ liệu đó có thể ở định dạng không có nguồn gốc từ CPU của máy bạn. Máy khách hoặc máy chủ ở đầu bên kia có thể có CPU sử dụng thứ tự byte khác với thứ tự byte của bạn. Nếu trường hợp này xảy ra, thì bạn sẽ cần chuyển đổi nó thành thứ tự byte gốc của máy chủ trước khi sử dụng

Thứ tự byte này được gọi là tuổi thọ của CPU. Xem Byte Endianness trong phần tham khảo để biết chi tiết. Bạn sẽ tránh được vấn đề này bằng cách tận dụng Unicode cho tiêu đề thư của mình và sử dụng mã hóa UTF-8. Vì UTF-8 sử dụng mã hóa 8 bit nên không có vấn đề về thứ tự byte

Bạn có thể tìm thấy lời giải thích trong tài liệu Mã hóa và Unicode của Python. Lưu ý rằng điều này chỉ áp dụng cho tiêu đề văn bản. Bạn sẽ sử dụng một loại rõ ràng và mã hóa được xác định trong tiêu đề cho nội dung đang được gửi, trọng tải tin nhắn. Điều này sẽ cho phép bạn chuyển bất kỳ dữ liệu nào bạn muốn [văn bản hoặc nhị phân], ở bất kỳ định dạng nào

Bạn có thể dễ dàng xác định thứ tự byte của máy bằng cách sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
977. Ví dụ, bạn có thể thấy một cái gì đó như thế này

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
98

Nếu bạn chạy cái này trong một máy ảo mô phỏng CPU big-endian [PowerPC], thì điều tương tự sẽ xảy ra

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
99

Trong ứng dụng ví dụ này, giao thức lớp ứng dụng của bạn xác định tiêu đề là văn bản Unicode với mã hóa UTF-8. Đối với nội dung thực tế trong tin nhắn, trọng tải tin nhắn, bạn sẽ vẫn phải hoán đổi thứ tự byte theo cách thủ công nếu cần

Điều này sẽ phụ thuộc vào ứng dụng của bạn và liệu nó có cần xử lý dữ liệu nhị phân nhiều byte từ một máy có độ bền khác hay không. Bạn có thể giúp máy khách hoặc máy chủ của mình triển khai hỗ trợ nhị phân bằng cách thêm các tiêu đề bổ sung và sử dụng chúng để truyền tham số, tương tự như HTTP

Đừng lo lắng nếu điều này chưa có ý nghĩa. Trong phần tiếp theo, bạn sẽ thấy tất cả những thứ này hoạt động và ăn khớp với nhau như thế nào

Loại bỏ các quảng cáo

Tiêu đề giao thức ứng dụng

Bây giờ bạn sẽ xác định đầy đủ tiêu đề giao thức. Tiêu đề giao thức là

  • Văn bản có độ dài thay đổi
  • Unicode với bảng mã UTF-8
  • Một từ điển Python được tuần tự hóa bằng JSON

Các tiêu đề hoặc tiêu đề phụ bắt buộc trong từ điển của tiêu đề giao thức như sau

TênMô tả

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
978 Thứ tự byte của máy [sử dụng
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
977]. Điều này có thể không cần thiết cho ứng dụng của bạn.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
980Độ dài của nội dung tính bằng byte.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
981Loại nội dung trong tải trọng, ví dụ:
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
982 hoặc
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
983.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
984Mã hóa mà nội dung sử dụng, ví dụ:
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
985 cho văn bản Unicode hoặc
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
986 cho dữ liệu nhị phân

Các tiêu đề này thông báo cho người nhận về nội dung trong tải trọng của tin nhắn. Điều này cho phép bạn gửi dữ liệu tùy ý trong khi cung cấp đủ thông tin để người nhận có thể giải mã và diễn giải chính xác nội dung. Vì các tiêu đề nằm trong từ điển nên bạn có thể dễ dàng thêm các tiêu đề bổ sung bằng cách chèn các cặp khóa-giá trị nếu cần

Gửi tin nhắn ứng dụng

Vẫn còn một chút vấn đề. Bạn có một tiêu đề có độ dài thay đổi, rất đẹp và linh hoạt, nhưng làm thế nào để bạn biết độ dài của tiêu đề khi đọc nó bằng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
5?

Trước đây, khi bạn đã học về cách sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
5 và ranh giới thư, bạn cũng đã biết rằng các tiêu đề có độ dài cố định có thể không hiệu quả. Điều đó đúng, nhưng bạn sẽ sử dụng một tiêu đề nhỏ, 2 byte, có độ dài cố định để làm tiền tố cho tiêu đề JSON chứa độ dài của nó

Bạn có thể coi đây là một cách tiếp cận hỗn hợp để gửi tin nhắn. Trên thực tế, bạn đang khởi động quá trình nhận tin nhắn bằng cách gửi độ dài của tiêu đề trước. Điều này giúp người nhận của bạn dễ dàng giải cấu trúc tin nhắn

Để giúp bạn hiểu rõ hơn về định dạng thư, hãy xem toàn bộ thư

Một thông báo bắt đầu với tiêu đề có độ dài cố định gồm hai byte, là một số nguyên theo thứ tự byte mạng. Đây là độ dài của tiêu đề tiếp theo, tiêu đề JSON có độ dài thay đổi. Khi bạn đã đọc hai byte bằng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
5, thì bạn biết rằng bạn có thể xử lý hai byte dưới dạng số nguyên rồi đọc số byte đó trước khi giải mã tiêu đề JSON UTF-8

Tiêu đề JSON chứa một từ điển các tiêu đề bổ sung. Một trong số đó là

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
980, là số byte nội dung của tin nhắn [không bao gồm tiêu đề JSON]. Khi bạn đã gọi
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
5 và đọc
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
980 byte, thì bạn đã đạt đến ranh giới tin nhắn, nghĩa là bạn đã đọc toàn bộ tin nhắn

Lớp tin nhắn ứng dụng

Cuối cùng, phần thưởng. Trong phần này, bạn sẽ nghiên cứu về lớp

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 và xem nó được sử dụng như thế nào với
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488 khi các sự kiện đọc và ghi xảy ra trên ổ cắm

Ứng dụng ví dụ này phản ánh loại thông báo nào mà máy khách và máy chủ có thể sử dụng một cách hợp lý. Tại thời điểm này, bạn vượt xa các máy khách và máy chủ đồ chơi

Để giữ cho mọi thứ đơn giản và vẫn chứng minh cách mọi thứ sẽ hoạt động trong một ứng dụng thực, ví dụ này sử dụng một giao thức ứng dụng triển khai tính năng tìm kiếm cơ bản. Máy khách gửi yêu cầu tìm kiếm và máy chủ thực hiện tìm kiếm kết quả khớp. Nếu yêu cầu do khách hàng gửi không được công nhận là tìm kiếm, thì máy chủ sẽ cho rằng đó là yêu cầu nhị phân và trả về phản hồi nhị phân

Sau khi đọc các phần sau, chạy các ví dụ và thử nghiệm mã, bạn sẽ thấy mọi thứ hoạt động như thế nào. Sau đó, bạn có thể sử dụng lớp

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 làm điểm bắt đầu và sửa đổi nó để sử dụng cho riêng mình

Ứng dụng này không xa lắm so với ví dụ máy khách và máy chủ

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
968. Mã vòng lặp sự kiện giữ nguyên trong
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
997 và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
998. Những gì bạn sẽ làm là di chuyển mã tin nhắn vào một lớp có tên là
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 và thêm các phương thức để hỗ trợ đọc, viết và xử lý các tiêu đề và nội dung. Đây là một ví dụ tuyệt vời cho việc sử dụng một lớp

Như bạn đã học trước đây và bạn sẽ thấy bên dưới, làm việc với socket liên quan đến việc giữ trạng thái. Bằng cách sử dụng một lớp, bạn giữ tất cả trạng thái, dữ liệu và mã được nhóm lại với nhau trong một đơn vị có tổ chức. Một thể hiện của lớp được tạo cho mỗi ổ cắm trong máy khách và máy chủ khi kết nối được bắt đầu hoặc được chấp nhận

Lớp này hầu như giống nhau cho cả máy khách và máy chủ đối với các phương thức tiện ích và trình bao bọc. Chúng bắt đầu bằng dấu gạch dưới, chẳng hạn như

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
600. Các phương thức này đơn giản hóa việc làm việc với lớp. They help other methods by allowing them to stay shorter and support the DRY principle

The server’s

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 class works in essentially the same way as the client’s and vice-versa. The difference is that the client initiates the connection and sends a request message, followed by processing the server’s response message. Conversely, the server waits for a connection, processes the client’s request message, and then sends a response message

Nó trông như thế này

StepEndpointAction / Message Content1ClientSends a

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 containing request content2ServerReceives and processes client request
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
9933ServerSends a
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 containing response content4ClientReceives and processes server response
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993

Here’s the file and code layout

ApplicationFileCodeServer

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
998The server’s main scriptServer
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
607The server’s
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 classClient
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
997The client’s main scriptClient
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
610The client’s
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 class

Message Entry Point

Understanding how the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 class works can be a challenge because there’s an aspect of its design that might not be immediately obvious. Why? Managing state

After a

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 object is created, it’s associated with a socket that’s monitored for events using
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
614

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
60

Ghi chú. Some of the code examples in this section are from the server’s main script and

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 class, but this section and discussion applies equally to the client as well. Bạn sẽ được cảnh báo khi phiên bản của khách hàng khác

When events are ready on the socket, they’re returned by

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
616. You can then get a reference back to the message object using the
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
905 attribute on the
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
909 object and call a method in
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
61

Looking at the event loop above, you’ll see that

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
901 is in the driver’s seat. It’s blocking, waiting at the top of the loop for events. It’s responsible for waking up when read and write events are ready to be processed on the socket. Which means, indirectly, it’s also responsible for calling the method
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
621. That’s why
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
621 is the entry point

Here’s what the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
621 method does

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
62

That’s good.

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
621 is simple. It can only do two things. call
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
625 and
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
626

This is where managing state comes in. Nếu một phương thức khác phụ thuộc vào các biến trạng thái có một giá trị nhất định, thì chúng sẽ chỉ được gọi từ

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
625 và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
626. This keeps the logic as simple as possible as events come in on the socket for processing

You might be tempted to use a mix of some methods that check the current state variables and, depending on their value, call other methods to process data outside

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
625 or
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
626. In the end, this would likely prove too complex to manage and keep up with

You should definitely modify the class to suit your own needs so that it works best for you, but you’ll probably have the best results if you keep the state checks and the calls to methods that depend on that state to the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
625 and
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
626 methods if possible

Now look at

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
625. This is the server’s version, but the client’s is the same. It just uses a different method name,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
634 instead of
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
635

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
63

The

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
636 method is called first. It calls
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
637 to read data from the socket and store it in a receive buffer

Remember that when

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
637 is called, all of the data that makes up a complete message may not have arrived yet.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
637 có thể cần được gọi lại. This is why there are state checks for each part of the message before the appropriate method to process it is called

Before a method processes its part of the message, it first checks to make sure enough bytes have been read into the receive buffer. If they have, it processes its respective bytes, removes them from the buffer and writes its output to a variable that’s used by the next processing stage. Because there are three components to a message, there are three state checks and

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
640 method calls

Message ComponentMethodOutputFixed-length header

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
641
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
642JSON header
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
643
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
644Content
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
645
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
646

Next, check out

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
626. This is the server’s version

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
64

The

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
626 method checks first for a
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
649. If one exists and a response hasn’t been created,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
650 is called. The
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
650 method sets the state variable
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
652 and writes the response to the send buffer

The

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
653 method calls
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
960 if there’s data in the send buffer

Remember that when

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
960 is called, all of the data in the send buffer may not have been queued for transmission. The network buffers for the socket may be full, and
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
960 may need to be called again. This is why there are state checks. The
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
650 method should only be called once, but it’s expected that
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
653 will need to be called multiple times

The client version of

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
626 is similar

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
65

Vì máy khách bắt đầu kết nối với máy chủ và gửi yêu cầu trước nên biến trạng thái

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
660 được chọn. If a request hasn’t been queued, it calls
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
661. The
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
662 method creates the request and writes it to the send buffer. It also sets the state variable
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
660 so that it’s only called once

Just like for the server,

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
653 calls
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
960 if there’s data in the send buffer

The notable difference in the client’s version of

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
626 is the last check to see if the request has been queued. This will be explained more in the section Client Main Script, but the reason for this is to tell
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
616 to stop monitoring the socket for write events. If the request has been queued and the send buffer is empty, then you’re done writing and you’re only interested in read events. Không có lý do gì để được thông báo rằng ổ cắm có thể ghi

To wrap up this section, consider this thought. the main purpose of this section was to explain that

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
616 is calling into the
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 class via the method
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
621 and to describe how state is managed

This is important because

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
621 will be called many times over the life of the connection. Therefore, make sure that any methods that should only be called once are either checking a state variable themselves, or the state variable set by the method is checked by the caller

Server Main Script

In the server’s main script

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
998, arguments are read from the command line that specify the interface and port to listen on

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
66

For example, to listen on the loopback interface on port

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
673, enter

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
67

Use an empty string for

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
674 to listen on all interfaces

After creating the socket, a call is made to

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
675 with the option
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
676

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
68

Setting this socket option avoids the error

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
677. Bạn sẽ thấy điều này khi khởi động máy chủ trên một cổng có kết nối ở trạng thái TIME_WAIT

For example, if the server actively closed a connection, it’ll remain in the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
678 state for two minutes or more, depending on the operating system. If you try to start the server again before the
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
678 state expires, then you’ll get an
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
970 exception of
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
677. This is a safeguard to make sure that any delayed packets in the network aren’t delivered to the wrong application

The event loop catches any errors so that the server can stay up and continue to run

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
69

Khi kết nối máy khách được chấp nhận, đối tượng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 được tạo

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
60

Đối tượng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 được liên kết với ổ cắm trong lệnh gọi tới
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
902 và ban đầu được đặt để chỉ giám sát các sự kiện đọc. Khi yêu cầu đã được đọc, bạn sẽ sửa đổi nó để chỉ lắng nghe các sự kiện ghi

Một lợi thế của việc sử dụng phương pháp này trong máy chủ là trong hầu hết các trường hợp, khi ổ cắm hoạt động tốt và không có sự cố mạng, nó sẽ luôn có thể ghi được

Nếu bạn yêu cầu

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
902 cũng theo dõi
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
686, thì vòng lặp sự kiện sẽ ngay lập tức thức dậy và thông báo cho bạn rằng đây là trường hợp. However, at this point, there’s no reason to wake up and call
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
4 on the socket. There’s no response to send, because a request hasn’t been processed yet. This would consume and waste valuable CPU cycles

Server Message Class

In the section Message Entry Point, you learned how the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 object was called into action when socket events were ready via
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
621. Now you’ll learn what happens as data is read on the socket and a component, or piece, of the message is ready to be processed by the server

The server’s message class is in

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
607, which is part of the source code you downloaded earlier. Bạn cũng có thể tải xuống mã bằng cách nhấp vào liên kết bên dưới

Nhận mã nguồn. Nhấp vào đây để lấy mã nguồn mà bạn sẽ sử dụng cho các ví dụ trong hướng dẫn này

The methods appear in the class in the order in which processing takes place for a message

When the server has read at least two bytes, the fixed-length header can be processed

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
61

The fixed-length header is a 2-byte integer in network, or big-endian, byte order. Nó chứa độ dài của tiêu đề JSON. You’ll use struct. unpack[] to read the value, decode it, and store it in

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
642. After processing the piece of the message it’s responsible for,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
692 removes it from the receive buffer

Just like with the fixed-length header, when there’s enough data in the receive buffer to contain the JSON header, it can be processed as well

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
62

The method

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
693 is called to decode and deserialize the JSON header into a dictionary. Because the JSON header is defined as Unicode with a UTF-8 encoding,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
985 is hardcoded in the call. The result is saved to
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
644. After processing the piece of the message that it’s responsible for,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
643 removes it from the receive buffer

Next is the actual content, or payload, of the message. It’s described by the JSON header in

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
644. When
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
980 bytes are available in the receive buffer, the request can be processed

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
63

After saving the message content to the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
905 variable,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
635 removes it from the receive buffer. Then, if the content type is JSON,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
635 decodes and deserializes it. If it’s not, this example application assumes that it’s a binary request and simply prints the content type

The last thing

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
635 does is modify the selector to monitor write events only. In the server’s main script,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
998, the socket is initially set to monitor read events only. Now that the request has been fully processed, you’re no longer interested in reading

A response can now be created and written to the socket. When the socket is writable,

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
650 is called from
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
626

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
64

A response is created by calling other methods, depending on the content type. In this example application, a simple dictionary lookup is done for JSON requests when

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
606. For your own applications, you can define other methods that get called here

After creating the response message, the state variable

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
607 is set so that
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
626 doesn’t call
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
650 again. Finally, the response is appended to the send buffer. This is seen by and sent via
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
653

One tricky bit to figure out is how to close the connection after the response is written. You can put the call to

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
6 in the method
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
653

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
65

Although it’s somewhat hidden, this is an acceptable trade-off given that the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 class only handles one message per connection. After the response is written, there’s nothing left for the server to do. It’s completed its work

Client Main Script

In the client’s main script,

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
997, arguments are read from the command line and used to create requests and start connections to the server

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
66

Here’s an example

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
67

After creating a dictionary representing the request from the command-line arguments, the host, port, and request dictionary are passed to

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
615

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
68

A socket is created for the server connection, as well as a

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 object using the
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
649 dictionary

Like for the server, the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 object is associated with the socket in the call to
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
902. However, for the client, the socket is initially set to be monitored for both read and write events. Once the request has been written, you’ll modify it to listen for read events only

This approach gives you the same advantage as the server. not wasting CPU cycles. After the request has been sent, you’re no longer interested in write events, so there’s no reason to wake up and process them

Client Message Class

In the section Message Entry Point, you learned how the message object was called into action when socket events were ready via

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
621. Now you’ll learn what happens after data is read and written on the socket and a message is ready to be processed by the client

The client’s message class is in

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
610, which is part of the source code you downloaded earlier. You can also download the code by clicking the link below

Nhận mã nguồn. Nhấp vào đây để lấy mã nguồn mà bạn sẽ sử dụng cho các ví dụ trong hướng dẫn này

The methods appear in the class in the order in which processing takes place for a message

The first task for the client is to queue the request

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
69

The dictionaries used to create the request, depending on what was passed on the command line, are in the client’s main script,

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
997. The request dictionary is passed as an argument to the class when a
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 object is created

The request message is created and appended to the send buffer, which is then seen by and sent via

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
653. Biến trạng thái
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
625 được đặt sao cho
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
661 không được gọi lại

After the request has been sent, the client waits for a response from the server

The methods for reading and processing a message in the client are the same as for the server. As response data is read from the socket, the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
640 header methods are called.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
692 and
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
629

The difference is in the naming of the final

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
640 methods and the fact that they’re processing a response, not creating one.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
634,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
632, and
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
633

Last, but certainly not least, is the final call for

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
634

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
70

Message Class Wrapup

To conclude your learning about the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
993 class, it’s worth mentioning a couple of things that are important to notice with a few of the supporting methods

Any exceptions raised by the class are caught by the main script in the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
636 clause inside the event loop

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
71

Note the line.

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
637

This is a really important line, for more than one reason. Not only does it make sure that the socket is closed, but

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
637 also removes the socket from being monitored by
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488. This greatly simplifies the code in the class and reduces complexity. If there’s an exception or you explicitly raise one yourself, you know
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
6 will take care of the cleanup

Các phương pháp

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
641 và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
642 cũng chứa một số điều thú vị

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
72

Note the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
643 line

The

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
653 method has one too. These lines are important because they catch a temporary error and skip over it using
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
645. The temporary error is when the socket would block, for example if it’s waiting on the network or the other end of the connection, also known as its peer

By catching and skipping over the exception with

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
645,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
488 will eventually trigger a new call, and you’ll get another chance to read or write the data

Loại bỏ các quảng cáo

Running the Application Client and Server

After all of this hard work, it’s time to have some fun and run some searches

In these examples, you’ll run the server so that it listens on all interfaces by passing an empty string for the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
38 argument. This will allow you to run the client and connect from a virtual machine that’s on another network. It emulates a big-endian PowerPC machine

Đầu tiên, khởi động máy chủ

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
73

Now run the client and enter a search. See if you can find him

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
74

You might notice that the terminal is running a shell that’s using a text encoding of Unicode [UTF-8], so the output above prints nicely with emojis

Now see if you can find the puppies

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
75

Notice the byte string sent over the network for the request in the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
649 line. It’s easier to see if you look for the bytes printed in hex that represent the puppy emoji.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
650. If your terminal is using Unicode with the encoding UTF-8, you’ll be able to enter the emoji for the search

This demonstrates that you’re sending raw bytes over the network and they need to be decoded by the receiver to be interpreted correctly. This is why you went to all of the trouble to create a header that contains the content type and encoding

Here’s the server output from both client connections above

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
76

Look at the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
649 line to see the bytes that were written to the client’s socket. This is the server’s response message

You can also test sending binary requests to the server if the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
652 argument is anything other than
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
653

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
77

Because the request’s

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
981 is not
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
982, the server treats it as a custom binary type and doesn’t perform JSON decoding. It simply prints the
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
981 and returns the first ten bytes to the client

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
78

Troubleshooting

Inevitably, something won’t work, and you’ll be wondering what to do. Don’t worry, it happens to everyone. Hopefully, with the help of this tutorial, your debugger, and your favorite search engine, you’ll be able to get going again with the source code part

If not, your first stop should be Python’s socket module documentation. Make sure you read all of the documentation for each function or method you’re calling. Also, read through the Reference section below for ideas. In particular, check the Errors section

Sometimes, it’s not all about the source code. The source code might be correct, and it’s just the other host, the client, or server. Or it could be the network. Maybe a router, firewall, or some other networking device is playing man-in-the-middle

For these types of issues, additional tools are essential. Below are a few tools and utilities that might help or at least provide some clues

Loại bỏ các quảng cáo

ping

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
657 will check if a host is alive and connected to the network by sending an ICMP echo request. It communicates directly with the operating system’s TCP/IP protocol stack, so it works independently from any application running on the host

Below is an example of running ping on macOS

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
79

Note the statistics at the end of the output. This can be helpful when you’re trying to discover intermittent connectivity problems. For example, is there any packet loss? How much latency is there? You can check the round-trip times

If there’s a firewall between you and the other host, a ping’s echo request may not be allowed. Some firewall administrators implement policies that enforce this. The idea is that they don’t want their hosts to be discoverable. If this is the case and you have firewall rules added to allow the hosts to communicate, then make sure that the rules also allow ICMP to pass between them

ICMP là giao thức được sử dụng bởi

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
657, nhưng nó cũng là giao thức mà TCP và các giao thức cấp thấp khác sử dụng để truyền thông báo lỗi. If you’re experiencing strange behavior or slow connections, this could be the reason

ICMP messages are identified by type and code. To give you an idea of the important information they carry, here are a few

ICMP TypeICMP CodeDescription80Echo request00Echo reply30Destination network unreachable31Destination host unreachable32Destination protocol unreachable33Destination port unreachable34Fragmentation required, and DF flag set110TTL expired in transit

See the article Path MTU Discovery for information regarding fragmentation and ICMP messages. This is an example of something that can cause strange behavior

netstat

In the section Viewing Socket State, you learned how

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
437 can be used to display information about sockets and their current state. This utility is available on macOS, Linux, and Windows

That section didn’t mention the columns

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
660 and
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
661 in the example output. These columns will show you the number of bytes that are held in network buffers that are queued for transmission or receipt, but for some reason haven’t been read or written by the remote or local application

In other words, the bytes are waiting in network buffers in the operating system’s queues. One reason could be that the application is CPU bound or is otherwise unable to call

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
637 or
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
960 and process the bytes. Or there could be network issues affecting communications, like congestion or failing network hardware or cabling

To demonstrate this and see how much data you can send before seeing an error, you can try out a test client that connects to a test server and repeatedly calls

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
960. The test server never calls
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
637. It just accepts the connection. This causes the network buffers on the server to fill, which eventually raises an error on the client

Đầu tiên, khởi động máy chủ

$ python echo-server.py
0

Then run the client to see what the error is

$ python echo-server.py
1

Here’s

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
437 output from while the client and server are still running, with the client printing out the error message above multiple times

$ python echo-server.py
2

The first entry is the server [

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
438 has port 65432]

$ python echo-server.py
3

Notice the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
660.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
669

The second entry is the client [

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
670 has port 65432]

$ python echo-server.py
4

Notice the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
661.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
672

The client sure was trying to write bytes, but the server wasn’t reading them. This caused the server’s network buffer queue to fill on the receive side and the client’s network buffer queue to fill on the send side

Windows

If you work with Windows, there’s a suite of utilities that you should definitely check out if you haven’t already. Windows Sysinternals

One of them is

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
673. TCPView is a graphical
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
437 for Windows. In addition to addresses, port numbers, and socket state, it’ll show you running totals for the number of packets and bytes sent and received. Giống như với tiện ích Unix
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
455, bạn cũng nhận được tên quy trình và ID. Check the menus for other display options

cá mập

Sometimes you need to see what’s happening on the wire. Forget about what the application log says or what the value is that’s being returned from a library call. You want to see what’s actually being sent or received on the network. Just like with debuggers, when you need to see it, there’s no substitute

Wireshark là một ứng dụng phân tích giao thức mạng và nắm bắt lưu lượng chạy trên macOS, Linux và Windows, trong số những ứng dụng khác. Có một phiên bản GUI có tên là

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
676 và cũng có một phiên bản dựa trên văn bản dựa trên thiết bị đầu cuối có tên là
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
677

Chạy nắm bắt lưu lượng là một cách tuyệt vời để xem ứng dụng hoạt động như thế nào trên mạng và thu thập bằng chứng về những gì nó gửi và nhận cũng như tần suất và mức độ. Bạn cũng có thể biết khi nào máy khách hoặc máy chủ đóng hoặc hủy kết nối hoặc ngừng phản hồi. Thông tin này có thể cực kỳ hữu ích khi bạn khắc phục sự cố

Có rất nhiều hướng dẫn hay và các tài nguyên khác trên web sẽ hướng dẫn bạn những kiến ​​thức cơ bản về cách sử dụng Wireshark và TShark

Đây là một ví dụ về nắm bắt lưu lượng sử dụng Wireshark trên giao diện loopback

Đây là ví dụ tương tự được hiển thị ở trên bằng cách sử dụng

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
677

$ python echo-server.py
5

Tiếp theo, bạn sẽ nhận được nhiều tài liệu tham khảo hơn để hỗ trợ hành trình lập trình ổ cắm của mình

Tài liệu tham khảo

Bạn có thể sử dụng phần này làm tài liệu tham khảo chung với thông tin bổ sung và liên kết đến các nguồn bên ngoài

Tài liệu Python

  • Mô-đun ổ cắm của Python
  • Lập trình socket của Python HOWTO

lỗi

Sau đây là từ tài liệu mô-đun

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
679 của Python

“Tất cả các lỗi đều có ngoại lệ. Các ngoại lệ thông thường đối với các loại đối số không hợp lệ và các điều kiện hết bộ nhớ có thể được nêu ra; . 3, các lỗi liên quan đến ngữ nghĩa ổ cắm hoặc địa chỉ nâng cao

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
970 hoặc một trong các lớp con của nó. " [Nguồn]

Dưới đây là một số lỗi phổ biến mà bạn có thể gặp phải khi làm việc với socket

Ngoại lệ

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
681 ConstantDescriptionBlockingIOErrorEWOULDBLOCKTài nguyên tạm thời không khả dụng. Ví dụ: ở chế độ không chặn, khi gọi
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
4 và máy ngang hàng đang bận và không đọc, hàng đợi gửi [bộ đệm mạng] đã đầy. Hay mạng có vấn đề. Hy vọng rằng đây là một điều kiện tạm thời. OSErrorEADDRINUSEĐịa chỉ đã được sử dụng. Đảm bảo rằng không có quy trình nào khác đang chạy sử dụng cùng số cổng và máy chủ của bạn đang đặt tùy chọn ổ cắm
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
683.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
684. ConnectionResetErrorECONNRESETThiết lập lại kết nối theo ngang hàng. Quá trình từ xa bị lỗi hoặc không đóng ổ cắm đúng cách, còn được gọi là tắt máy không sạch sẽ. Hoặc có tường lửa hoặc thiết bị khác trong đường dẫn mạng thiếu quy tắc hoặc hoạt động sai. TimeoutErrorETIMEDOUTThao tác đã hết thời gian chờ. Không có phản hồi từ đồng nghiệp. ConnectionRefusedErrorECONNREFUDKết nối bị từ chối. Không có ứng dụng nào lắng nghe trên cổng được chỉ định

Gia đình địa chỉ ổ cắm

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
36 và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
686 đại diện cho họ địa chỉ và giao thức được sử dụng cho đối số đầu tiên của
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
7. Các API sử dụng một địa chỉ mong đợi nó ở một định dạng nhất định, tùy thuộc vào việc ổ cắm được tạo bằng
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
36 hay
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
686

Địa chỉ FamilyProtocolAddress TupleDescription

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
36IPv4
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
37
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
38 là một chuỗi có tên máy chủ như
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
693 hoặc địa chỉ IPv4 như
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
694.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
401 là một số nguyên.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
686IPv6
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
418
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
38 là một chuỗi có tên máy chủ như
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
693 hoặc địa chỉ IPv6 như
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
700.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
401 là một số nguyên.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
702 và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
703 đại diện cho các thành viên
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
704 và
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
705 trong cấu trúc C
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
706

Lưu ý đoạn trích bên dưới từ tài liệu mô-đun ổ cắm của Python liên quan đến giá trị

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
38 của bộ địa chỉ

“For IPv4 addresses, two special forms are accepted instead of a host address. the empty string represents

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
708, and the string
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
709 represents
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
710. This behavior is not compatible with IPv6, therefore, you may want to avoid these if you intend to support IPv6 with your Python programs. ” [Source]

See Python’s Socket families documentation for more information

This tutorial uses IPv4 sockets, but if your network supports it, try testing and using IPv6 if possible. One way to support this easily is by using the function socket. getaddrinfo[]. It translates the

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
38 and
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
401 arguments into a sequence of five-tuples that contains all of the necessary arguments for creating a socket connected to that service.
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
713 will understand and interpret passed-in IPv6 addresses and hostnames that resolve to IPv6 addresses, in addition to IPv4

The following example returns address information for a TCP connection to

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
714 on port
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
715

>>>

$ python echo-server.py
6

Results may differ on your system if IPv6 isn’t enabled. The values returned above can be used by passing them to

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
7 and
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
717. There’s a client and server example in the Example section of Python’s socket module documentation

Using Hostnames

For context, this section applies mostly to using hostnames with

$ python echo-server.py
9 and
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
2, or
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
3, when you intend to use the loopback interface, “localhost. ” However, it also applies any time you’re using a hostname and there’s an expectation of it resolving to a certain address and having a special meaning to your application that affects its behavior or assumptions. This is in contrast to the typical scenario of a client using a hostname to connect to a server that’s resolved by DNS, like www. example. com

Sau đây là từ tài liệu mô-đun

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
679 của Python

“Nếu bạn sử dụng tên máy chủ trong phần máy chủ của địa chỉ ổ cắm IPv4/v6, chương trình có thể hiển thị hành vi không xác định, vì Python sử dụng địa chỉ đầu tiên được trả về từ độ phân giải DNS. Địa chỉ ổ cắm sẽ được phân giải khác thành địa chỉ IPv4/v6 thực tế, tùy thuộc vào kết quả từ độ phân giải DNS và/hoặc cấu hình máy chủ. Đối với hành vi xác định, hãy sử dụng địa chỉ số trong phần máy chủ. " [Nguồn]

The standard convention for the name “localhost” is for it to resolve to

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
400 or
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
469, the loopback interface. This will more than likely be the case for you on your system, but maybe not. It depends on how your system is configured for name resolution. As with all things IT, there are always exceptions, and there are no guarantees that using the name “localhost” will connect to the loopback interface

For example, on Linux, see

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
724, the Name Service Switch configuration file. Another place to check on macOS and Linux is the file
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
725. On Windows, see
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
726. The
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
727 file contains a static table of name-to-address mappings in a simple text format. DNS is another piece of the puzzle altogether

Interestingly enough, as of June 2018, there’s an RFC draft Let ‘localhost’ be localhost that discusses the conventions, assumptions, and security around using the name “localhost. ”

What’s important to understand is that when you use hostnames in your application, the returned addresses could literally be anything. Don’t make assumptions regarding a name if you have a security-sensitive application. Depending on your application and environment, this may or may not be a concern for you

Note. Security precautions and best practices still apply, even if your application isn’t explicitly security-sensitive. If your application accesses the network, it should be secured and maintained. This means, at a minimum

  • System software updates and security patches are applied regularly, including Python. Are you using any third-party libraries? If so, make sure those are checked and updated too

  • If possible, use a dedicated or host-based firewall to restrict connections to trusted systems only

  • What DNS servers are configured? Do you trust them and their administrators?

  • Make sure that request data is sanitized and validated as much as possible prior to calling other code that processes it. Use fuzz tests for this and run them regularly

Regardless of whether or not you’re using hostnames, if your application needs to support secure connections through encryption and authentication, then you’ll probably want to look into using TLS. This is its own separate topic and beyond the scope of this tutorial. See Python’s ssl module documentation to get started. This is the same protocol that your web browser uses to connect securely to web sites

With interfaces, IP addresses, and name resolution to consider, there are many variables. What should you do? Here are some recommendations that you can use if you don’t have a network application review process

ApplicationUsageRecommendationServerloopback interfaceUse an IP address, such as

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
400 or
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
469. Serverethernet interfaceUse an IP address, such as
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
407. To support more than one interface, use an empty string for all interfaces/addresses. See the security note above. Clientloopback interfaceUse an IP address, such as
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
400 or
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
469. Clientethernet interfaceUse an IP address for consistency and non-reliance on name resolution. For the typical case, use a hostname. Xem lưu ý bảo mật ở trên

For clients or servers, if you need to authenticate the host that you’re connecting to, look into using TLS

chặn cuộc gọi

A socket function or method that temporarily suspends your application is a blocking call. For example,

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
1,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
2,
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
4, and
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
5 block, meaning they don’t return immediately. Blocking calls have to wait on system calls [I/O] to complete before they can return a value. So you, the caller, are blocked until they’re done or a timeout or other error occurs

Blocking socket calls can be set to non-blocking mode so they return immediately. Nếu bạn làm điều này, thì ít nhất bạn sẽ cần cấu trúc lại hoặc thiết kế lại ứng dụng của mình để xử lý hoạt động của ổ cắm khi nó sẵn sàng

Because the call returns immediately, data may not be ready. Callee đang đợi trên mạng và không có thời gian để hoàn thành công việc của mình. Nếu đây là trường hợp, thì trạng thái hiện tại là giá trị

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
681
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
738. Chế độ không chặn được hỗ trợ với. thiết lập chặn []

Theo mặc định, ổ cắm luôn được tạo ở chế độ chặn. Xem Ghi chú về thời gian chờ của ổ cắm để biết mô tả về ba chế độ

Đóng kết nối

Một điều thú vị cần lưu ý với TCP là việc máy khách hoặc máy chủ đóng phía kết nối của họ trong khi phía bên kia vẫn mở là hoàn toàn hợp pháp. Điều này được gọi là kết nối “nửa mở”. Đó là quyết định của ứng dụng cho dù điều này có được mong muốn hay không. Nói chung là không. Ở trạng thái này, bên đã đóng kết nối không còn có thể gửi dữ liệu. Họ chỉ có thể nhận

Cách tiếp cận này không nhất thiết được khuyến nghị, nhưng ví dụ: HTTP sử dụng tiêu đề có tên "Kết nối" được sử dụng để chuẩn hóa cách các ứng dụng nên đóng hoặc duy trì các kết nối mở. Để biết chi tiết, xem phần 6. 3 trong RFC 7230, Giao thức truyền siêu văn bản [HTTP/1. 1]. Cú pháp tin nhắn và định tuyến

Khi thiết kế và viết ứng dụng của bạn cũng như giao thức lớp ứng dụng của nó, bạn nên tiếp tục và tìm ra cách bạn mong đợi các kết nối được đóng lại. Đôi khi điều này là hiển nhiên và đơn giản, hoặc đó là thứ có thể cần thử nghiệm và tạo mẫu ban đầu. Nó phụ thuộc vào ứng dụng và cách vòng lặp thông báo được xử lý với dữ liệu dự kiến ​​của nó. Chỉ cần đảm bảo rằng các ổ cắm luôn được đóng lại kịp thời sau khi họ hoàn thành công việc của mình

Độ bền byte

Xem bài viết về tuổi thọ của Wikipedia để biết chi tiết về cách các CPU khác nhau lưu trữ thứ tự byte trong bộ nhớ. Khi diễn giải các byte riêng lẻ, đây không phải là vấn đề. Tuy nhiên, khi bạn đang xử lý nhiều byte được đọc và xử lý dưới dạng một giá trị, chẳng hạn như số nguyên 4 byte, thì thứ tự byte cần được đảo ngược nếu bạn đang giao tiếp với một máy sử dụng độ cuối khác

Thứ tự byte cũng quan trọng đối với các chuỗi văn bản được biểu diễn dưới dạng chuỗi nhiều byte, như Unicode. Trừ khi bạn luôn sử dụng ASCII đúng, nghiêm ngặt và kiểm soát việc triển khai máy khách và máy chủ, tốt hơn hết bạn nên sử dụng Unicode với mã hóa như UTF-8 hoặc mã hỗ trợ dấu thứ tự byte [BOM]

Điều quan trọng là xác định rõ ràng mã hóa được sử dụng trong giao thức lớp ứng dụng của bạn. Bạn có thể làm điều này bằng cách yêu cầu tất cả văn bản là UTF-8 hoặc sử dụng tiêu đề "mã hóa nội dung" chỉ định mã hóa. Điều này ngăn không cho ứng dụng của bạn phát hiện mã hóa mà bạn nên tránh nếu có thể

Điều này trở nên có vấn đề khi có dữ liệu liên quan được lưu trữ trong tệp hoặc cơ sở dữ liệu và không có siêu dữ liệu nào chỉ định mã hóa của dữ liệu đó. Khi dữ liệu được chuyển đến một điểm cuối khác, nó sẽ phải cố gắng phát hiện mã hóa. Để thảo luận, hãy xem bài viết Unicode của Wikipedia, tham khảo RFC 3629. UTF-8, định dạng chuyển đổi của ISO 10646

“Tuy nhiên, RFC 3629, tiêu chuẩn UTF-8, khuyến nghị rằng các dấu thứ tự byte bị cấm trong các giao thức sử dụng UTF-8, nhưng thảo luận về các trường hợp không thể thực hiện được. Ngoài ra, hạn chế lớn đối với các mẫu có thể có trong UTF-8 [ví dụ: không thể có bất kỳ byte đơn lẻ nào với tập bit cao] có nghĩa là có thể phân biệt UTF-8 với các mã hóa ký tự khác mà không cần dựa vào BOM. " [Nguồn]

Điểm nổi bật của điều này là luôn lưu trữ mã hóa được sử dụng cho dữ liệu được ứng dụng của bạn xử lý nếu nó có thể thay đổi. Nói cách khác, hãy cố gắng bằng cách nào đó lưu trữ mã hóa dưới dạng siêu dữ liệu nếu nó không phải lúc nào cũng là UTF-8 hoặc một số mã hóa khác có BOM. Sau đó, bạn có thể gửi mã hóa đó trong tiêu đề cùng với dữ liệu để cho người nhận biết đó là gì

Thứ tự byte được sử dụng trong TCP/IP là thứ tự lớn và được gọi là thứ tự mạng. Thứ tự mạng được sử dụng để biểu thị các số nguyên ở các lớp thấp hơn của ngăn xếp giao thức, như địa chỉ IP và số cổng. Mô-đun ổ cắm của Python bao gồm các chức năng chuyển đổi số nguyên sang và từ thứ tự byte của mạng và máy chủ

Chức năngMô tả

# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
739Chuyển đổi số nguyên dương 32 bit từ mạng sang thứ tự byte máy chủ. Trên các máy có thứ tự byte máy chủ giống với thứ tự byte mạng, đây là lệnh cấm; .
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
740Chuyển đổi số nguyên dương 16 bit từ mạng sang thứ tự byte lưu trữ. Trên các máy có thứ tự byte máy chủ giống với thứ tự byte mạng, đây là lệnh cấm; .
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
741Chuyển đổi số nguyên dương 32 bit từ máy chủ sang thứ tự byte mạng. Trên các máy có thứ tự byte máy chủ giống với thứ tự byte mạng, đây là lệnh cấm; .
# echo-server.py

# ...

with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
    s.bind[[HOST, PORT]]
    s.listen[]
    conn, addr = s.accept[]
    with conn:
        print[f"Connected by {addr}"]
        while True:
            data = conn.recv[1024]
            if not data:
                break
            conn.sendall[data]
742Chuyển đổi số nguyên dương 16 bit từ máy chủ sang thứ tự byte mạng. Trên các máy có thứ tự byte máy chủ giống với thứ tự byte mạng, đây là lệnh cấm;

Bạn cũng có thể sử dụng mô-đun cấu trúc để đóng gói và giải nén dữ liệu nhị phân bằng các chuỗi định dạng

$ python echo-server.py
7

Sự kết luận

Bạn đã bao phủ rất nhiều nền tảng trong hướng dẫn này. Mạng và ổ cắm là những chủ đề lớn. Nếu bạn chưa quen với mạng hoặc ổ cắm, đừng nản lòng với tất cả các thuật ngữ và từ viết tắt

Có rất nhiều phần cần làm quen để hiểu cách mọi thứ hoạt động cùng nhau. Tuy nhiên, giống như Python, nó sẽ bắt đầu có ý nghĩa hơn khi bạn làm quen với từng phần riêng lẻ và dành nhiều thời gian hơn cho chúng

Trong hướng dẫn này, bạn

  • Đã xem API ổ cắm cấp thấp trong mô-đun
    # echo-server.py
    
    # ...
    
    with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
        s.bind[[HOST, PORT]]
        s.listen[]
        conn, addr = s.accept[]
        with conn:
            print[f"Connected by {addr}"]
            while True:
                data = conn.recv[1024]
                if not data:
                    break
                conn.sendall[data]
    
    679 của Python và xem cách nó có thể được sử dụng để tạo các ứng dụng máy khách-máy chủ
  • Xây dựng máy khách và máy chủ có thể xử lý nhiều kết nối bằng đối tượng
    # echo-server.py
    
    # ...
    
    with socket.socket[socket.AF_INET, socket.SOCK_STREAM] as s:
        s.bind[[HOST, PORT]]
        s.listen[]
        conn, addr = s.accept[]
        with conn:
            print[f"Connected by {addr}"]
            while True:
                data = conn.recv[1024]
                if not data:
                    break
                conn.sendall[data]
    
    744
  • Tạo lớp tùy chỉnh của riêng bạn và sử dụng lớp đó làm giao thức lớp ứng dụng để trao đổi thông báo và dữ liệu giữa các điểm cuối

Từ đây, bạn có thể sử dụng lớp tùy chỉnh của mình và xây dựng dựa trên lớp đó để tìm hiểu và giúp tạo các ứng dụng socket của riêng bạn dễ dàng và nhanh hơn

Để xem lại các ví dụ, bạn có thể nhấp vào liên kết bên dưới

Nhận mã nguồn. Nhấp vào đây để lấy mã nguồn mà bạn sẽ sử dụng cho các ví dụ trong hướng dẫn này

Chúc mừng bạn đã đi đến cuối cùng. Bây giờ bạn đang trên con đường sử dụng socket trong các ứng dụng của riêng mình. Chúc may mắn trên hành trình phát triển ổ cắm của bạn

Đánh dấu là đã hoàn thành

🐍 Thủ thuật Python 💌

Nhận một Thủ thuật Python ngắn và hấp dẫn được gửi đến hộp thư đến của bạn vài ngày một lần. Không có thư rác bao giờ. Hủy đăng ký bất cứ lúc nào. Được quản lý bởi nhóm Real Python

Gửi cho tôi thủ thuật Python »

Giới thiệu về Nathan Jennings

Nathan là thành viên của nhóm hướng dẫn Real Python, người đã bắt đầu sự nghiệp lập trình viên của mình với C từ lâu, nhưng cuối cùng đã tìm thấy Python. Từ các ứng dụng web và thu thập dữ liệu đến mạng và bảo mật mạng, anh ấy thích mọi thứ Pythonic

» Thông tin thêm về Na-than

Mỗi hướng dẫn tại Real Python được tạo bởi một nhóm các nhà phát triển để nó đáp ứng các tiêu chuẩn chất lượng cao của chúng tôi. Các thành viên trong nhóm đã làm việc trong hướng dẫn này là

Aldren

Brad

Geir Arne

Ian

Jim

Joanna

kate

Bậc thầy Kỹ năng Python trong thế giới thực Với quyền truy cập không giới hạn vào Python thực

Tham gia với chúng tôi và có quyền truy cập vào hàng nghìn hướng dẫn, khóa học video thực hành và cộng đồng các Pythonistas chuyên gia

Nâng cao kỹ năng Python của bạn »

Bậc thầy Kỹ năng Python trong thế giới thực
Với quyền truy cập không giới hạn vào Python thực

Tham gia với chúng tôi và có quyền truy cập vào hàng ngàn hướng dẫn, khóa học video thực hành và cộng đồng Pythonistas chuyên gia

Nâng cao kỹ năng Python của bạn »

Bạn nghĩ sao?

Đánh giá bài viết này

Tweet Chia sẻ Chia sẻ Email

Bài học số 1 hoặc điều yêu thích mà bạn đã học được là gì?

Mẹo bình luận. Những nhận xét hữu ích nhất là những nhận xét được viết với mục đích học hỏi hoặc giúp đỡ các sinh viên khác. Nhận các mẹo để đặt câu hỏi hay và nhận câu trả lời cho các câu hỏi phổ biến trong cổng thông tin hỗ trợ của chúng tôi

Chủ Đề