socketserver --- Framework cho máy chủ mạng

Source code: Lib/socketserver.py


Mô-đun socketserver đơn giản hóa nhiệm vụ ghi máy chủ mạng.

sẵn có: not WASI.

Mô-đun này không hoạt động hoặc không có trên WebAssembly. Xem Nền tảng WebAssugging để biết thêm thông tin.

Có bốn lớp máy chủ cụ thể cơ bản:

class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)

Điều này sử dụng giao thức TCP internet, cung cấp các luồng dữ liệu liên tục giữa máy khách và máy chủ. Nếu bind_and_activate là đúng, hàm tạo sẽ tự động thử gọi server_bind()server_activate(). Các tham số khác được chuyển đến lớp cơ sở BaseServer.

class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)

Điều này sử dụng các gói dữ liệu, là các gói thông tin riêng biệt có thể đến không đúng thứ tự hoặc bị mất trong khi truyền. Các thông số giống như đối với TCPServer.

class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)
class socketserver.UnixDatagramServer(server_address, RequestHandlerClass, bind_and_activate=True)

Các lớp ít được sử dụng hơn này tương tự như các lớp TCP và UDP, nhưng sử dụng ổ cắm miền Unix; chúng không có sẵn trên các nền tảng không phải Unix. Các thông số giống như đối với TCPServer.

Bốn lớp này xử lý các yêu cầu synchronously; mỗi yêu cầu phải được hoàn thành trước khi yêu cầu tiếp theo có thể được bắt đầu. Điều này không phù hợp nếu mỗi yêu cầu mất nhiều thời gian để hoàn thành vì nó đòi hỏi nhiều tính toán hoặc vì nó trả về nhiều dữ liệu mà máy khách xử lý chậm. Giải pháp là tạo một quy trình hoặc luồng riêng biệt để xử lý từng yêu cầu; các lớp hỗn hợp ForkingMixInThreadingMixIn có thể được sử dụng để hỗ trợ hành vi không đồng bộ.

Tạo một máy chủ yêu cầu một số bước. Trước tiên, bạn phải tạo một lớp xử lý yêu cầu bằng cách phân lớp con BaseRequestHandler và ghi đè phương thức handle() của nó; phương pháp này sẽ xử lý các yêu cầu đến. Thứ hai, bạn phải khởi tạo một trong các lớp máy chủ, chuyển cho nó địa chỉ của máy chủ và lớp xử lý yêu cầu. Bạn nên sử dụng máy chủ trong câu lệnh with. Sau đó gọi phương thức handle_request() hoặc serve_forever() của đối tượng máy chủ để xử lý một hoặc nhiều yêu cầu. Cuối cùng, gọi server_close() để đóng socket (trừ khi bạn sử dụng câu lệnh with).

Khi kế thừa từ ThreadingMixIn cho hành vi kết nối theo luồng, bạn nên khai báo rõ ràng cách bạn muốn các luồng của mình hoạt động khi tắt đột ngột. Lớp ThreadingMixIn định nghĩa một thuộc tính daemon_threads, cho biết liệu máy chủ có nên chờ kết thúc luồng hay không. Bạn nên đặt cờ rõ ràng nếu bạn muốn các luồng hoạt động tự chủ; mặc định là False, nghĩa là Python sẽ không thoát cho đến khi tất cả các luồng được tạo bởi ThreadingMixIn đã thoát.

Các lớp máy chủ có cùng các phương thức và thuộc tính bên ngoài, bất kể chúng sử dụng giao thức mạng nào.

Ghi chú tạo máy chủ

Có năm lớp trong sơ đồ kế thừa, bốn trong số đó đại diện cho các máy chủ đồng bộ gồm bốn loại:

+-----------+
| BaseServer |
+-----------+
      |
      v
+----------+ +-------------------+
| TCPServer |------->| UnixStreamServer |
+----------+ +-------------------+
      |
      v
+----------+ +----------------------+
| UDPServer |------->| UnixDatagramServer |
+----------+ +----------------------+

Lưu ý rằng UnixDatagramServer bắt nguồn từ UDPServer, không phải từ UnixStreamServer --- sự khác biệt duy nhất giữa IP và máy chủ Unix là họ địa chỉ.

class socketserver.ForkingMixIn
class socketserver.ThreadingMixIn

Các phiên bản phân nhánh và phân luồng của từng loại máy chủ có thể được tạo bằng cách sử dụng các lớp trộn này. Ví dụ: ThreadingUDPServer được tạo như sau

lớp ThreadingUDPServer(ThreadingMixIn, UDPServer):
    vượt qua

Lớp hỗn hợp xuất hiện trước vì nó ghi đè một phương thức được xác định trong UDPServer. Việc đặt các thuộc tính khác nhau cũng làm thay đổi hành vi của cơ chế máy chủ cơ bản.

ForkingMixIn và các lớp Forking được đề cập bên dưới chỉ khả dụng trên nền tảng POSIX hỗ trợ fork().

block_on_close

ForkingMixIn.server_close đợi cho đến khi tất cả các tiến trình con hoàn tất, ngoại trừ nếu thuộc tính block_on_closeFalse.

ThreadingMixIn.server_close đợi cho đến khi tất cả các luồng không phải daemon hoàn tất, ngoại trừ nếu thuộc tính block_on_closeFalse.

max_children

Chỉ định có bao nhiêu tiến trình con sẽ tồn tại để xử lý các yêu cầu tại một thời điểm cho ForkingMixIn. Nếu đạt đến giới hạn, các yêu cầu mới sẽ đợi cho đến khi một tiến trình con kết thúc.

daemon_threads

Đối với ThreadingMixIn, hãy sử dụng các luồng daemon bằng cách đặt ThreadingMixIn.daemon_threads thành True để không đợi cho đến khi các luồng hoàn tất.

Thay đổi trong phiên bản 3.7: ForkingMixIn.server_closeThreadingMixIn.server_close hiện đợi cho đến khi tất cả các tiến trình con và các luồng không phải daemon hoàn tất. Thêm thuộc tính lớp ForkingMixIn.block_on_close mới để chọn tham gia hành vi trước 3.7.

class socketserver.ForkingTCPServer
class socketserver.ForkingUDPServer
class socketserver.ThreadingTCPServer
class socketserver.ThreadingUDPServer
class socketserver.ForkingUnixStreamServer
class socketserver.ForkingUnixDatagramServer
class socketserver.ThreadingUnixStreamServer
class socketserver.ThreadingUnixDatagramServer

Các lớp này được xác định trước bằng cách sử dụng các lớp trộn lẫn.

Added in version 3.12: Các lớp ForkingUnixStreamServerForkingUnixDatagramServer đã được thêm vào.

Để triển khai một dịch vụ, bạn phải lấy được một lớp từ BaseRequestHandler và xác định lại phương thức handle() của nó. Sau đó, bạn có thể chạy các phiên bản khác nhau của dịch vụ bằng cách kết hợp một trong các lớp máy chủ với lớp xử lý yêu cầu của mình. Lớp xử lý yêu cầu phải khác nhau đối với các dịch vụ datagram hoặc luồng. Điều này có thể được ẩn bằng cách sử dụng các lớp con xử lý StreamRequestHandler hoặc DatagramRequestHandler.

Tất nhiên, bạn vẫn phải sử dụng cái đầu của mình! Ví dụ: sẽ vô nghĩa khi sử dụng máy chủ phân nhánh nếu dịch vụ chứa trạng thái trong bộ nhớ có thể được sửa đổi bởi các yêu cầu khác nhau, vì các sửa đổi trong quy trình con sẽ không bao giờ đạt đến trạng thái ban đầu được lưu giữ trong quy trình cha và được truyền cho từng quy trình con. Trong trường hợp này, bạn có thể sử dụng máy chủ phân luồng, nhưng có thể bạn sẽ phải sử dụng các khóa để bảo vệ tính toàn vẹn của dữ liệu được chia sẻ.

Mặt khác, nếu bạn đang xây dựng một máy chủ HTTP nơi tất cả dữ liệu được lưu trữ bên ngoài (ví dụ: trong hệ thống tệp), về cơ bản, một lớp đồng bộ sẽ khiến dịch vụ bị "điếc" trong khi một yêu cầu đang được xử lý -- điều này có thể xảy ra trong một thời gian rất dài nếu máy khách nhận được tất cả dữ liệu mà nó yêu cầu chậm. Ở đây một máy chủ phân luồng hoặc phân nhánh là thích hợp.

Trong một số trường hợp, có thể phù hợp để xử lý một phần yêu cầu một cách đồng bộ, nhưng phải xử lý xong ở phần con được phân nhánh tùy thuộc vào dữ liệu yêu cầu. Điều này có thể được thực hiện bằng cách sử dụng một máy chủ đồng bộ và thực hiện một nhánh rõ ràng trong phương thức handle() của lớp xử lý yêu cầu.

Một cách tiếp cận khác để xử lý nhiều yêu cầu đồng thời trong môi trường không hỗ trợ cả luồng lẫn fork() (hoặc khi những yêu cầu này quá đắt hoặc không phù hợp với dịch vụ) là duy trì một bảng rõ ràng gồm các yêu cầu đã hoàn thành một phần và sử dụng selectors để quyết định yêu cầu nào sẽ được thực hiện tiếp theo (hoặc có xử lý yêu cầu mới đến hay không). Điều này đặc biệt quan trọng đối với các dịch vụ phát trực tuyến nơi mỗi máy khách có thể được kết nối trong thời gian dài (nếu không thể sử dụng các luồng hoặc quy trình con).

Đối tượng máy chủ

class socketserver.BaseServer(server_address, RequestHandlerClass)

Đây là siêu lớp của tất cả các đối tượng Máy chủ trong mô-đun. Nó định nghĩa giao diện được đưa ra dưới đây, nhưng không triển khai hầu hết các phương thức được thực hiện trong các lớp con. Hai tham số được lưu trữ trong các thuộc tính server_addressRequestHandlerClass tương ứng.

fileno()

Trả về một bộ mô tả tệp số nguyên cho ổ cắm mà máy chủ đang lắng nghe. Chức năng này thường được chuyển tới selectors để cho phép giám sát nhiều máy chủ trong cùng một quy trình.

handle_request()

Xử lý một yêu cầu duy nhất. Hàm này gọi các phương thức sau theo thứ tự: get_request(), verify_request()process_request(). Nếu phương thức handle() do người dùng cung cấp của lớp trình xử lý đưa ra một ngoại lệ, thì phương thức handle_error() của máy chủ sẽ được gọi. Nếu không nhận được yêu cầu nào trong vòng timeout giây, handle_timeout() sẽ được gọi và handle_request() sẽ quay trở lại.

serve_forever(poll_interval=0.5)

Xử lý các yêu cầu cho đến khi có yêu cầu shutdown() rõ ràng. Thăm dò ý kiến ​​tắt máy mỗi giây poll_interval. Bỏ qua thuộc tính timeout. Nó cũng gọi service_actions(), có thể được sử dụng bởi một lớp con hoặc mixin để cung cấp các hành động cụ thể cho một dịch vụ nhất định. Ví dụ: lớp ForkingMixIn sử dụng service_actions() để dọn sạch các tiến trình con zombie.

Thay đổi trong phiên bản 3.3: Đã thêm lệnh gọi service_actions vào phương thức serve_forever.

service_actions()

Điều này được gọi trong vòng lặp serve_forever(). Phương thức này có thể được ghi đè bởi các lớp con hoặc lớp mixin để thực hiện các hành động cụ thể cho một dịch vụ nhất định, chẳng hạn như hành động dọn dẹp.

Added in version 3.3.

shutdown()

Yêu cầu vòng lặp serve_forever() dừng lại và đợi cho đến khi nó dừng lại. shutdown() phải được gọi trong khi serve_forever() đang chạy trong một luồng khác nếu không nó sẽ bế tắc.

server_close()

Dọn dẹp máy chủ. Có thể bị ghi đè.

address_family

Nhóm giao thức chứa socket của máy chủ. Các ví dụ phổ biến là socket.AF_INET, socket.AF_INET6socket.AF_UNIX. Phân lớp các lớp máy chủ TCP hoặc UDP trong mô-đun này với thuộc tính lớp address_family = AF_INET6 được đặt nếu bạn muốn các lớp máy chủ IPv6.

RequestHandlerClass

Lớp xử lý yêu cầu do người dùng cung cấp; một thể hiện của lớp này được tạo cho mỗi yêu cầu.

server_address

Địa chỉ mà máy chủ đang lắng nghe. Định dạng của địa chỉ khác nhau tùy thuộc vào họ giao thức; xem tài liệu về mô-đun socket để biết chi tiết. Đối với các giao thức internet, đây là một bộ dữ liệu chứa một chuỗi cung cấp địa chỉ và số cổng số nguyên: ('127.0.0.1', 80) chẳng hạn.

socket

Đối tượng socket mà máy chủ sẽ lắng nghe các yêu cầu gửi đến.

Các lớp máy chủ hỗ trợ các biến lớp sau:

allow_reuse_address

Liệu máy chủ có cho phép sử dụng lại địa chỉ hay không. Giá trị mặc định này là False và có thể được đặt trong các lớp con để thay đổi chính sách.

request_queue_size

Kích thước của hàng đợi yêu cầu. Nếu phải mất nhiều thời gian để xử lý một yêu cầu, mọi yêu cầu đến trong khi máy chủ đang bận sẽ được đưa vào hàng đợi, tối đa là yêu cầu request_queue_size. Khi hàng đợi đã đầy, các yêu cầu tiếp theo từ khách hàng sẽ gặp lỗi "Kết nối bị từ chối". Giá trị mặc định thường là 5, nhưng giá trị này có thể bị ghi đè bởi các lớp con.

socket_type

Loại ổ cắm được máy chủ sử dụng; socket.SOCK_STREAMsocket.SOCK_DGRAM là hai giá trị chung.

timeout

Thời lượng chờ, được tính bằng giây hoặc None nếu không muốn hết thời gian chờ. Nếu handle_request() không nhận được yêu cầu nào trong khoảng thời gian chờ, phương thức handle_timeout() sẽ được gọi.

Có nhiều phương thức máy chủ khác nhau có thể bị ghi đè bởi các lớp con của các lớp máy chủ cơ sở như TCPServer; những phương thức này không hữu ích đối với người dùng bên ngoài của đối tượng máy chủ.

finish_request(request, client_address)

Trên thực tế xử lý yêu cầu bằng cách khởi tạo RequestHandlerClass và gọi phương thức handle() của nó.

get_request()

Phải chấp nhận yêu cầu từ ổ cắm và trả về 2 bộ dữ liệu chứa đối tượng ổ cắm new được sử dụng để liên lạc với máy khách và địa chỉ của máy khách.

handle_error(request, client_address)

Hàm này được gọi nếu phương thức handle() của phiên bản RequestHandlerClass đưa ra một ngoại lệ. Hành động mặc định là in truy tìm lỗi tiêu chuẩn và tiếp tục xử lý các yêu cầu tiếp theo.

Thay đổi trong phiên bản 3.6: Bây giờ chỉ gọi các ngoại lệ bắt nguồn từ lớp Exception.

handle_timeout()

Hàm này được gọi khi thuộc tính timeout được đặt thành giá trị khác None và khoảng thời gian chờ đã trôi qua mà không nhận được yêu cầu nào. Hành động mặc định cho các máy chủ phân nhánh là thu thập trạng thái của bất kỳ tiến trình con nào đã thoát, trong khi ở các máy chủ phân luồng, phương pháp này không thực hiện gì.

process_request(request, client_address)

Gọi finish_request() để tạo một phiên bản của RequestHandlerClass. Nếu muốn, chức năng này có thể tạo một tiến trình hoặc luồng mới để xử lý yêu cầu; các lớp ForkingMixInThreadingMixIn thực hiện việc này.

server_activate()

Được gọi bởi hàm tạo của máy chủ để kích hoạt máy chủ. Hành vi mặc định cho máy chủ TCP chỉ gọi listen() trên ổ cắm của máy chủ. Có thể bị ghi đè.

server_bind()

Được hàm tạo của máy chủ gọi để liên kết ổ cắm với địa chỉ mong muốn. Có thể bị ghi đè.

verify_request(request, client_address)

Phải trả về giá trị Boolean; nếu giá trị là True thì yêu cầu sẽ được xử lý và nếu là False thì yêu cầu sẽ bị từ chối. Chức năng này có thể được ghi đè để triển khai các biện pháp kiểm soát quyền truy cập cho máy chủ. Việc triển khai mặc định luôn trả về True.

Thay đổi trong phiên bản 3.6: Hỗ trợ cho giao thức context manager đã được thêm vào. Thoát khỏi trình quản lý bối cảnh tương đương với việc gọi server_close().

Đối tượng xử lý yêu cầu

class socketserver.BaseRequestHandler

Đây là siêu lớp của tất cả các đối tượng xử lý yêu cầu. Nó xác định giao diện, được đưa ra dưới đây. Lớp con xử lý yêu cầu cụ thể phải xác định phương thức handle() mới và có thể ghi đè bất kỳ phương thức nào khác. Một phiên bản mới của lớp con được tạo cho mỗi yêu cầu.

setup()

Được gọi trước phương thức handle() để thực hiện mọi hành động khởi tạo cần thiết. Việc triển khai mặc định không làm gì cả.

handle()

Chức năng này phải thực hiện tất cả công việc cần thiết để phục vụ một yêu cầu. Việc triển khai mặc định không làm gì cả. Một số thuộc tính phiên bản có sẵn cho nó; yêu cầu có sẵn dưới dạng request; địa chỉ khách hàng là client_address; và phiên bản máy chủ là server, trong trường hợp nó cần quyền truy cập vào thông tin trên mỗi máy chủ.

Loại request khác nhau đối với các dịch vụ datagram hoặc luồng. Đối với các dịch vụ truyền phát, request là một đối tượng socket; đối với các dịch vụ datagram, request là một cặp chuỗi và ổ cắm.

finish()

Được gọi theo phương thức handle() để thực hiện mọi hành động dọn dẹp cần thiết. Việc triển khai mặc định không làm gì cả. Nếu setup() đưa ra một ngoại lệ, hàm này sẽ không được gọi.

request

Đối tượng new socket.socket được sử dụng để giao tiếp với khách hàng.

client_address

Địa chỉ khách hàng được trả về bởi BaseServer.get_request().

server

Đối tượng BaseServer được sử dụng để xử lý yêu cầu.

class socketserver.StreamRequestHandler
class socketserver.DatagramRequestHandler

Các lớp con BaseRequestHandler này ghi đè các phương thức setup()finish(), đồng thời cung cấp các thuộc tính rfilewfile.

rfile

Một đối tượng tệp nhận được yêu cầu sẽ được đọc. Hỗ trợ giao diện dễ đọc io.BufferedIOBase.

wfile

Một đối tượng tập tin mà câu trả lời được viết vào. Hỗ trợ giao diện có thể ghi io.BufferedIOBase

Thay đổi trong phiên bản 3.6: wfile cũng hỗ trợ giao diện có thể ghi io.BufferedIOBase.

Ví dụ

socketserver.TCPServer Ví dụ

Đây là phía máy chủ:

nhập khẩu máy chủ  cắm

lớp MyTCPHandler(socketserver.BaseRequestHandler):
    """
    Lớp xử lý yêu cầu cho máy chủ của chúng tôi.

    Nó được khởi tạo một lần cho mỗi kết nối tới máy chủ và phải
    ghi đè phương thức hand() để thực hiện giao tiếp với
    khách hàng.
    """

    xử  def (tự):
        # self.request là ổ cắm TCP được kết nối với máy khách
        miếng = [b'']
        tổng cộng = 0
        trong khi b'\n' không  dạng miếng[-1]  tổng số < 10_000:
            miếng.append(self.request.recv(2000))
            tổng += len(miếng[-1])
        self.data = b''.join(mảnh)
        print(f"Đã nhận từ {self.client_address[0]}:")
        print(self.data.decode("utf-8"))
        # just gửi lại dữ liệu tương tự nhưng được viết hoa
        self.request.sendall(self.data.upper())
        # after chúng tôi quay lại, ổ cắm sẽ đóng lại.

nếu __name__ == "__main__":
    HOST, PORT = "localhost", 9999

    # Create máy chủ, liên kết với localhost trên cổng 9999
    với socketserver.TCPServer((HOST, PORT), MyTCPHandler) làm máy chủ:
        # Activate máy chủ; cái này sẽ tiếp tục chạy cho đến khi bạn
        # interrupt chương trình với Ctrl-C
        server.serve_forever()

Một lớp xử lý yêu cầu thay thế sử dụng các luồng (các đối tượng giống như tệp giúp đơn giản hóa việc giao tiếp bằng cách cung cấp giao diện tệp tiêu chuẩn):

lớp MyTCPHandler(socketserver.StreamRequestHandler):

    xử  def (tự):
        # self.rfile là một đối tượng giống như tệp được tạo bởi trình xử lý.
        # We hiện có thể sử dụng ví dụ: readline() thay vì các lệnh gọi recv() thô.
        # We giới hạn ở mức 10000 byte để tránh bị người gửi lạm dụng.
        self.data = self.rfile.readline(10000).rstrip()
        print(f"{self.client_address[0]} đã viết:")
        print(self.data.decode("utf-8"))
        # Likewise, self.wfile là một đối tượng dạng file dùng để ghi lại
        # to khách hàng
        self.wfile.write(self.data.upper())

Sự khác biệt là lệnh gọi readline() trong trình xử lý thứ hai sẽ gọi recv() nhiều lần cho đến khi gặp một ký tự dòng mới, trong khi trình xử lý đầu tiên phải sử dụng vòng lặp recv() để tích lũy dữ liệu cho đến khi có một dòng mới. Nếu nó chỉ sử dụng một recv() duy nhất mà không có vòng lặp thì nó sẽ trả về những gì đã nhận được cho đến nay từ máy khách. TCP dựa trên luồng: dữ liệu đến theo thứ tự được gửi nhưng không có mối tương quan giữa các lệnh gọi send() hoặc sendall() của máy khách và số lượng lệnh gọi recv() trên máy chủ cần thiết để nhận dữ liệu đó.

Đây là phía khách hàng:

 cắm nhập khẩu
hệ thống nhập khẩu

HOST, PORT = "localhost", 9999
dữ liệu = " ".join(sys.argv[1:])

# Create một ổ cắm (SOCK_STREAM có nghĩa là ổ cắm TCP)
với socket.socket(socket.AF_INET, socket.SOCK_STREAM)  sock:
    # Connect đến máy chủ và gửi dữ liệu
    sock.connect((HOST, PORT))
    sock.sendall(byte(dữ liệu, "utf-8"))
    sock.sendall(b"\n")

    dữ liệu # Receive từ máy chủ và tắt
    đã nhận = str(sock.recv(1024), "utf-8")

print("Đã gửi: ", dữ liệu)
print("Đã nhận:", đã nhận)

Đầu ra của ví dụ sẽ trông giống như thế này:

Máy chủ:

$ trăn TCPServer.py
127.0.0.1 đã viết:
b'xin chào thế giới với TCP'
127.0.0.1 đã viết:
b'python thật tuyệt'

Khách hàng:

$ python TCPClient.py xin chào thế giới với TCP
Đã gửi: xin chào thế giới với TCP
Đã nhận: HELLO WORLD WITH TCP
$ python TCPClient.py python thật tuyệt
Đã gửi: trăn thật tuyệt
Đã nhận: PYTHON LÀ NICE

socketserver.UDPServer Ví dụ

Đây là phía máy chủ:

nhập khẩu máy chủ  cắm

lớp MyUDPHandler(socketserver.BaseRequestHandler):
    """
    Lớp này hoạt động tương tự như lớp xử lý TCP, ngoại trừ việc
    self.request bao gồm một cặp ổ cắm dữ liệu và máy khách, và vì
    không có kết nối, địa chỉ khách hàng phải được cung cấp rõ ràng
    khi gửi dữ liệu trở lại qua sendto().
    """

    xử  def (tự):
        data = self.request[0].strip()
        socket = self.request[1]
        print(f"{self.client_address[0]} đã viết:")
        in (dữ liệu)
        socket.sendto(data.upper(), self.client_address)

nếu __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    với socketserver.UDPServer((HOST, PORT), MyUDPHandler) làm máy chủ:
        server.serve_forever()

Đây là phía khách hàng:

 cắm nhập khẩu
hệ thống nhập khẩu

HOST, PORT = "localhost", 9999
dữ liệu = " ".join(sys.argv[1:])

# SOCK_DGRAM là loại socket sử dụng cho socket UDP
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# As bạn có thể thấy, không có lệnh gọi connect(); UDP không có kết nối.
# Instead, dữ liệu được gửi trực tiếp đến người nhận thông qua sendto().
sock.sendto(byte(data + "\n", "utf-8"), (HOST, PORT))
đã nhận = str(sock.recv(1024), "utf-8")

print("Đã gửi: ", dữ liệu)
print("Đã nhận:", đã nhận)

Đầu ra của ví dụ sẽ trông giống hệt như ví dụ về máy chủ TCP.

Mixin không đồng bộ

Để xây dựng các trình xử lý không đồng bộ, hãy sử dụng các lớp ThreadingMixInForkingMixIn.

Một ví dụ cho lớp ThreadingMixIn:

 cắm nhập khẩu
nhập luồng
nhập khẩu máy chủ  cắm

lớp ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):

    xử  def (tự):
        data = str(self.request.recv(1024), 'ascii')
        cur_thread = threading.current_thread()
        phản hồi = byte("{}: {}".format(cur_thread.name, data), 'ascii')
        self.request.sendall(response)

lớp ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    vượt qua

máy khách def (ip, cổng, tin nhắn):
    với socket.socket(socket.AF_INET, socket.SOCK_STREAM)  sock:
        sock.connect((ip, port))
        sock.sendall(byte(tin nhắn, 'ascii'))
        phản hồi = str(sock.recv(1024), 'ascii')
        print("Đã nhận: {}".format(response))

nếu __name__ == "__main__":
    # Port 0 có nghĩa là chọn một cổng không sử dụng tùy ý
    HOST, PORT = "localhost", 0

    máy chủ = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
    với máy chủ:
        ip, port = server.server_address

        # Start một luồng với máy chủ -- luồng đó sau đó sẽ bắt đầu một luồng
        chủ đề # more cho mỗi yêu cầu
        server_thread = threading.Thread(target=server.serve_forever)
        # Exit luồng máy chủ khi luồng chính kết thúc
        server_thread.daemon = Đúng
        server_thread.start()
        print("Vòng lặp máy chủ đang chạy trong luồng:", server_thread.name)

        client(ip, port, "Xin chào thế giới 1")
        client(ip, port, "Xin chào thế giới 2")
        client(ip, port, "Xin chào thế giới 3")

        máy chủ.shutdown()

Đầu ra của ví dụ sẽ trông giống như thế này:

$ python ThreadedTCPServer.py
Vòng lặp máy chủ đang chạy trong luồng: Thread-1
Đã nhận: Chủ đề-2: Hello World 1
Đã nhận: Chủ đề-3: Hello World 2
Đã nhận: Chủ đề-4: Hello World 3

Lớp ForkingMixIn được sử dụng theo cách tương tự, ngoại trừ việc máy chủ sẽ tạo ra một quy trình mới cho mỗi yêu cầu. Chỉ khả dụng trên nền tảng POSIX hỗ trợ fork().