socketserver --- چارچوبی برای سرورهای شبکه

کد منبع: Lib/socketserver.py


ماژول socketserver کار نوشتن سرورهای شبکه را ساده‌تر می‌کند.

دسترس‌پذیری: not WASI.

این ماژول روی WebAssembly کار نمی‌کند یا در دسترس نیست. برای اطلاعات بیشتر سکوهای WebAssembly را ببینید.

چهار کلاس سرور عینی پایه وجود دارد:

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

این از پروتکل TCP اینترنتی استفاده می‌کند، که جریان‌های پیوسته‌ای از داده‌ها را بین کلاینت و سرور فراهم می‌کند. اگر bind_and_activate مقدار true داشته باشد، سازنده به‌طور خودکار تلاش می‌کند server_bind() و server_activate() را فراخوانی کند. سایر پارامترها به کلاس پایه‌ی BaseServer ارسال می‌شوند.

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

این از دیتاگرام‌ها استفاده می‌کند، که بسته‌های گسسته‌ای از اطلاعات هستند که ممکن است خارج از ترتیب برسند یا در حین انتقال گم شوند. پارامترها همان پارامترهای TCPServer هستند.

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

این کلاس‌ها که کمتر استفاده می‌شوند، مشابه کلاس‌های TCP و UDP هستند، اما از سوکت‌های دامنه Unix استفاده می‌کنند؛ آن‌ها در پلتفرم‌های غیر Unix در دسترس نیستند. پارامترها همان پارامترهای TCPServer هستند.

این چهار کلاس درخواست‌ها را به‌صورت همگام <synchronously> پردازش می‌کنند؛ هر درخواست باید پیش از آنکه درخواست بعدی بتواند آغاز شود، کامل شود. اگر کامل شدن هر درخواست زمان زیادی طول بکشد، این مناسب نیست؛ خواه به این دلیل که به محاسبات زیادی نیاز دارد، خواه به این دلیل که داده‌های زیادی برمی‌گرداند که کلاینت در پردازش آن‌ها کند است. راه‌حل، ایجاد یک فرایند یا نخ جداگانه برای مدیریت هر درخواست است؛ می‌توان از کلاس‌های میکس‌این (mix-in) ForkingMixIn و ThreadingMixIn برای پشتیبانی از رفتار ناهمگام استفاده کرد.

ایجاد یک سرور به چند مرحله نیاز دارد. ابتدا، شما باید با زیرکلاس‌سازی از کلاس BaseRequestHandler و بازنویسی متد handle() آن، یک کلاس رسیدگی به درخواست ایجاد کنید؛ این متد درخواست‌های ورودی را پردازش می‌کند. در مرحله دوم، شما باید یکی از کلاس‌های سرور را نمونه‌سازی کنید و آدرس سرور و کلاس رسیدگی به درخواست را به آن بدهید. توصیه می‌شود از سرور در یک دستور with استفاده کنید. سپس متد handle_request() یا serve_forever() شیء سرور را برای پردازش یک یا چند درخواست فراخوانی کنید. در نهایت، برای بستن سوکت، server_close() را فراخوانی کنید (مگر اینکه از یک دستور with استفاده کرده باشید).

هنگام ارث‌بری از ThreadingMixIn برای رفتار اتصال نخی، باید به‌صراحت مشخص کنید که می‌خواهید نخ‌های شما در زمان خاموش شدن ناگهانی چگونه رفتار کنند. کلاس ThreadingMixIn ویژگی daemon_threads را تعریف می‌کند، که نشان می‌دهد آیا سرور باید منتظر پایان نخ‌ها بماند یا خیر. اگر می‌خواهید نخ‌ها مستقل رفتار کنند، باید این پرچم را به‌صراحت تنظیم کنید؛ مقدار پیش‌فرض False است، به این معنا که پایتون خارج نخواهد شد، مگر آنکه همه‌ی نخ‌های ایجادشده توسط ThreadingMixIn خارج شده باشند.

کلاس‌های سرور، صرف‌نظر از پروتکل شبکه‌ای که استفاده می‌کنند، متدها و ویژگی‌های بیرونی یکسانی دارند.

نکات ایجاد سرور

در یک نمودار وراثت، پنج کلاس وجود دارد که چهار مورد از آن‌ها نشان‌دهنده‌ی سرورهای همگام از چهار نوع هستند:

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

توجه داشته باشید که UnixDatagramServer از UDPServer مشتق شده است، نه از UnixStreamServer --- تنها تفاوت بین یک سرور IP و یک سرور Unix، خانواده نشانی است.

class socketserver.ForkingMixIn
class socketserver.ThreadingMixIn

نسخه‌های forking و threading هر نوع سرور را می‌توان با استفاده از این کلاس‌های میکس‌این ایجاد کرد. برای مثال، ThreadingUDPServer به صورت زیر ایجاد می‌شود:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

کلاس میکس‌این ابتدا قرار می‌گیرد، زیرا متدی را که در UDPServer تعریف‌شده است، بازنویسی می‌کند. تنظیم ویژگی‌های مختلف نیز رفتار سازوکار زیربنایی سرور را تغییر می‌دهد.

ForkingMixIn و کلاس‌های Forking ذکرشده در زیر، فقط در پلتفرم‌های POSIX که از fork() پشتیبانی می‌کنند، در دسترس هستند.

block_on_close

ForkingMixIn.server_close منتظر می‌ماند تا همه فرایندهای فرزند به پایان برسند، مگر اینکه ویژگی block_on_close برابر False باشد.

ThreadingMixIn.server_close منتظر می‌ماند تا تمام نخ‌های غیردیمن به پایان برسند، مگر اینکه ویژگی block_on_close برابر False باشد.

max_children

تعیین کنید که در ForkingMixIn چند فرایند فرزند برای رسیدگی همزمان به درخواست‌ها وجود خواهند داشت. اگر به محدودیت رسیده شود، درخواست‌های جدید منتظر می‌مانند تا یک فرایند فرزند به پایان برسد.

daemon_threads

برای ThreadingMixIn، با تنظیم ThreadingMixIn.daemon_threads روی True از نخ‌های daemon استفاده کنید تا منتظر تکمیل شدن نخ‌ها نمانید.

تغییر یافته در نسخه‌ی 3.7: ForkingMixIn.server_close و ThreadingMixIn.server_close اکنون تا زمانی که تمام فرآیندهای فرزند و نخ‌های غیر daemon به پایان برسند، صبر می‌کنند. یک ویژگی کلاس جدید ForkingMixIn.block_on_close برای فعال‌سازی رفتار پیش از 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

این کلاس‌ها از پیش با استفاده از کلاس‌های میکس‌این تعریف‌شده‌اند.

اضافه شده در نسخه‌ی 3.12: کلاس‌های ForkingUnixStreamServer و ForkingUnixDatagramServer افزوده شدند.

برای پیاده‌سازی یک سرویس، باید کلاسی را از BaseRequestHandler مشتق کنید و متد handle() آن را بازتعریف کنید. سپس می‌توانید نسخه‌های گوناگونی از سرویس را با ترکیب یکی از کلاس‌های سرور با کلاس هندلر درخواست خود اجرا کنید. کلاس هندلر درخواست باید برای سرویس‌های دیتاگرامی یا جریانی متفاوت باشد. این موضوع را می‌توان با استفاده از زیرکلاس‌های هندلر StreamRequestHandler یا DatagramRequestHandler پنهان کرد.

البته، همچنان باید عقل خود را به کار ببرید! برای مثال، اگر سرویس دارای وضعیتی در حافظه باشد که با درخواست‌های مختلف قابل تغییر باشد، استفاده از یک سرور فورک‌کننده (forking server) منطقی نیست، زیرا تغییرات ایجادشده در فرایند فرزند هرگز به وضعیت اولیه‌ای که در فرایند والد نگهداری می‌شود و به هر فرایند فرزند منتقل می‌شود، نمی‌رسند. در این حالت، می‌توانید از یک سرور نخی (threading server) استفاده کنید، اما احتمالاً مجبور خواهید بود برای محافظت از یکپارچگی داده‌های مشترک از قفل‌ها استفاده کنید.

از سوی دیگر، اگر در حال ساخت یک سرور HTTP هستید که تمام داده‌ها به‌صورت خارجی ذخیره می‌شوند (برای نمونه، در سامانه فایل‌بندی)، یک کلاس همگام عملاً سرویس را در حین رسیدگی به یک درخواست «کر» می‌کند — که اگر کلاینت در دریافت تمام داده‌هایی که درخواست کرده است کند باشد، ممکن است بسیار طول بکشد. در اینجا یک سرور نخ‌بندی (threading) یا فورک‌کننده (forking) مناسب است.

در برخی موارد، ممکن است مناسب باشد بخشی از یک درخواست به‌صورت همگام پردازش شود، اما تکمیل پردازش در یک فرزند forkشده، بسته به داده‌های درخواست، انجام شود. این کار را می‌توان با استفاده از یک سرور همگام و انجام یک fork صریح در متد handle() کلاس مدیریت درخواست پیاده‌سازی کرد.

رویکردی دیگر برای رسیدگی به چندین درخواست همزمان در محیطی که از هیچ‌کدام از نخ‌ها و fork() پشتیبانی نمی‌کند (یا در شرایطی که این موارد بیش از حد پرهزینه یا برای سرویس نامناسب باشند)، نگه‌داری از یک جدول آشکار از درخواست‌های نیمه‌تمام و استفاده از selectors برای تصمیم‌گیری درباره‌ی این‌که بعداً روی کدام درخواست کار شود (یا این‌که آیا باید به یک درخواست ورودی جدید رسیدگی کرد) است. این موضوع به‌ویژه برای سرویس‌های جریانی اهمیت دارد که در آن‌ها هر کلاینت می‌تواند به‌طور بالقوه برای مدت طولانی متصل بماند (اگر نتوان از نخ‌ها یا زیرفرآیندها استفاده کرد).

اشیای سرور

class socketserver.BaseServer(server_address, RequestHandlerClass)

این ابرکلاس تمام اشیای Server در ماژول است. این کلاس رابطی را که در ادامه آمده است تعریف می‌کند، اما بیشتر متدها را پیاده‌سازی نمی‌کند؛ این کار در زیرکلاس‌ها انجام می‌شود. دو پارامتر به‌ترتیب در ویژگی‌های server_address و RequestHandlerClass ذخیره می‌شوند.

fileno()

یک توصیف‌گر پرونده از نوع عدد صحیح برای سوکتی که سرور به آن گوش می‌دهد، برمی‌گرداند. این تابع معمولاً به selectors داده می‌شود تا امکان پایش چندین سرور در یک فرایند فراهم شود.

handle_request()

یک درخواست واحد را پردازش می‌کند. این تابع متدهای زیر را به‌ترتیب فراخوانی می‌کند: get_request()، verify_request() و process_request(). اگر متد handle() ارائه‌شده توسط کاربر در کلاس هندلر استثنایی پرتاب کند، متد handle_error() سرور فراخوانی خواهد شد. اگر هیچ درخواستی ظرف timeout ثانیه دریافت نشود، handle_timeout() فراخوانی خواهد شد و handle_request() بازگشت خواهد کرد.

serve_forever(poll_interval=0.5)

درخواست‌ها را تا دریافت یک درخواست صریح shutdown() رسیدگی می‌کند. هر poll_interval ثانیه، وضعیت خاموش شدن را پایش می‌کند. ویژگی timeout را نادیده می‌گیرد. همچنین service_actions() را فراخوانی می‌کند، که ممکن است توسط یک زیرکلاس یا میکس‌این برای ارائه اقدامات خاص یک سرویس مشخص استفاده شود. برای مثال، کلاس ForkingMixIn از service_actions() برای پاک‌سازی فرآیندهای فرزند زامبی استفاده می‌کند.

تغییر یافته در نسخه‌ی 3.3: فراخوانی service_actions به متد serve_forever افزوده شد.

service_actions()

این متد در حلقه‌ی serve_forever() فراخوانی می‌شود. زیرکلاس‌ها یا کلاس‌های mixin می‌توانند این متد را برای انجام اقدامات خاص یک سرویس مشخص، مانند اقدامات پاک‌سازی، بازنویسی کنند.

اضافه شده در نسخه‌ی 3.3.

shutdown()

به حلقه‌ی serve_forever() دستور توقف بدهید و صبر کنید تا متوقف شود. shutdown() باید در حالی فراخوانی شود که serve_forever() در یک نخ دیگر در حال اجرا باشد؛ در غیر این صورت بن‌بست رخ خواهد داد.

server_close()

سرور را پاک‌سازی می‌کند. ممکن است بازنویسی شود.

address_family

خانواده پروتکل‌هایی که سوکت سرور به آن تعلق دارد. نمونه‌های رایج عبارت‌اند از socket.AF_INET، socket.AF_INET6 و socket.AF_UNIX. اگر کلاس‌های سرور IPv6 می‌خواهید، در این ماژول از کلاس‌های سرور TCP یا UDP با تنظیم ویژگی کلاس address_family = AF_INET6 زیرکلاس بسازید.

RequestHandlerClass

کلاس مدیریت درخواست ارائه‌شده توسط کاربر؛ برای هر درخواست، یک نمونه از این کلاس ایجاد می‌شود.

server_address

نشانی‌ای که سرور روی آن گوش می‌دهد. قالب نشانی‌ها بسته به خانواده پروتکل متفاوت است؛ برای جزئیات، مستندات ماژول socket را ببینید. برای پروتکل‌های اینترنتی، این یک تاپل حاوی یک رشته برای نشانی و یک عدد صحیح برای شماره پورت است: برای مثال ('127.0.0.1', 80).

socket

شیء سوکتی که سرور روی آن به درخواست‌های ورودی گوش خواهد داد.

کلاس‌های سرور از متغیرهای کلاس زیر پشتیبانی می‌کنند:

allow_reuse_address

این‌که سرور اجازه استفاده‌ی مجدد از یک آدرس را می‌دهد یا خیر. مقدار پیش‌فرض آن False است و می‌توان آن را در زیرکلاس‌ها برای تغییر این سیاست تنظیم کرد.

request_queue_size

اندازه‌ی صف درخواست. اگر پردازش یک درخواست زمان زیادی طول بکشد، درخواست‌هایی که در حین مشغول بودن سرور می‌رسند، در یک صف قرار می‌گیرند؛ تا سقف request_queue_size درخواست. هنگامی که صف پر شد، درخواست‌های بعدی از سمت کلاینت‌ها با خطای «Connection denied» مواجه می‌شوند. مقدار پیش‌فرض معمولاً ۵ است، اما زیرکلاس‌ها می‌توانند این مقدار را بازنویسی کنند.

socket_type

نوع سوکتی که سرور از آن استفاده می‌کند؛ socket.SOCK_STREAM و socket.SOCK_DGRAM دو مقدار رایج هستند.

timeout

مدت‌زمان مهلت، که بر حسب ثانیه اندازه‌گیری می‌شود، یا None اگر هیچ مهلتی مدنظر نباشد. اگر handle_request() در بازه‌ی مهلت هیچ درخواست ورودی‌ای دریافت نکند، متد handle_timeout() فراخوانی می‌شود.

متدهای سرور گوناگونی وجود دارند که زیرکلاس‌های کلاس‌های پایه‌ی سرور مانند TCPServer می‌توانند آن‌ها را بازنویسی کنند؛ این متدها برای کاربران خارجی شیء سرور مفید نیستند.

finish_request(request, client_address)

در واقع درخواست را با نمونه‌سازی از RequestHandlerClass و فراخوانی متد handle() آن پردازش می‌کند.

get_request()

باید یک درخواست را از سوکت بپذیرد و یک تاپل دوتایی شامل شیء سوکت جدید برای برقراری ارتباط با کلاینت و نشانی کلاینت را برگرداند.

handle_error(request, client_address)

این تابع در صورتی فراخوانی می‌شود که متد handle() از نمونه‌ای از RequestHandlerClass استثنایی را پرتاب کند. رفتار پیش‌فرض این است که ردگیری پشته در خطای استاندارد چاپ شود و رسیدگی به درخواست‌های بعدی ادامه یابد.

تغییر یافته در نسخه‌ی 3.6: اکنون فقط برای استثناهای مشتق‌شده از کلاس Exception فراخوانی می‌شود.

handle_timeout()

این تابع زمانی فراخوانی می‌شود که ویژگی timeout به مقداری غیر از None تنظیم شده باشد و مدت مهلت بدون دریافت هیچ درخواستی سپری شده باشد. اقدام پیش‌فرض برای سرورهای فورک‌کننده (forking servers) جمع‌آوری وضعیت فرایندهای فرزندی است که خارج شده‌اند، در حالی که در سرورهای نخی (threading servers) این متد هیچ کاری انجام نمی‌دهد.

process_request(request, client_address)

finish_request() را فراخوانی می‌کند تا نمونه‌ای از RequestHandlerClass ایجاد کند. در صورت تمایل، این تابع می‌تواند یک فرایند یا نخ جدید برای رسیدگی به درخواست ایجاد کند؛ کلاس‌های ForkingMixIn و ThreadingMixIn این کار را انجام می‌دهند.

server_activate()

توسط سازنده‌ی سرور فراخوانی می‌شود تا سرور را فعال کند. رفتار پیش‌فرض برای یک سرور TCP، تنها listen() را روی سوکت سرور فراخوانی می‌کند. ممکن است بازنویسی شود.

server_bind()

توسط سازنده‌ی سرور فراخوانی می‌شود تا سوکت را به نشانی دلخواه مقید کند. ممکن است بازنویسی شود.

verify_request(request, client_address)

باید یک مقدار بولی برگرداند؛ اگر مقدار True باشد، درخواست پردازش خواهد شد، و اگر False باشد، درخواست رد خواهد شد. می‌توان این تابع را بازنویسی کرد تا کنترل‌های دسترسی برای یک سرور پیاده‌سازی شود. پیاده‌سازی پیش‌فرض همیشه True را برمی‌گرداند.

تغییر یافته در نسخه‌ی 3.6: پشتیبانی از پروتکل context manager افزوده شد. خروج از مدیر زمینه معادل فراخوانی server_close() است.

اشیای هندلر درخواست

class socketserver.BaseRequestHandler

این ابرکلاس تمام اشیای هندلر درخواست است. این کلاس رابطی را که در ادامه آمده است تعریف می‌کند. یک زیرکلاس عینی از هندلر درخواست باید یک متد handle() جدید تعریف کند و می‌تواند هر یک از متدهای دیگر را بازنویسی کند. برای هر درخواست، یک نمونه جدید از زیرکلاس ایجاد می‌شود.

setup()

پیش از متد handle() فراخوانی می‌شود تا هرگونه عملیات مقداردهی اولیه مورد نیاز را انجام دهد. پیاده‌سازی پیش‌فرض هیچ کاری انجام نمی‌دهد.

handle()

این تابع باید تمام کارهای مورد نیاز برای رسیدگی به یک درخواست را انجام دهد. پیاده‌سازی پیش‌فرض هیچ کاری انجام نمی‌دهد. چندین ویژگی نمونه در دسترس آن است؛ درخواست به‌صورت request، نشانی کلاینت به‌صورت client_address و نمونه سرور به‌صورت server در دسترس است، در صورتی که به اطلاعات به‌ازای هر سرور نیاز داشته باشد.

نوع request برای سرویس‌های دیتاگرام یا جریانی متفاوت است. برای سرویس‌های جریانی، request یک شیء سوکت است؛ برای سرویس‌های دیتاگرام، request جفتی متشکل از یک رشته و یک سوکت است.

finish()

پس از متد handle() فراخوانی می‌شود تا هرگونه عملیات پاک‌سازی لازم را انجام دهد. پیاده‌سازی پیش‌فرض هیچ کاری انجام نمی‌دهد. اگر setup() استثنایی پرتاب کند، این تابع فراخوانی نخواهد شد.

request

شیء جدید socket.socket که برای برقراری ارتباط با کلاینت استفاده می‌شود.

client_address

نشانی کلاینت برگردانده‌شده توسط BaseServer.get_request().

server

شیء BaseServer که برای رسیدگی به درخواست به کار می‌رود.

class socketserver.StreamRequestHandler
class socketserver.DatagramRequestHandler

این زیرکلاس‌های BaseRequestHandler، متدهای setup() و finish() را بازنویسی می‌کنند و ویژگی‌های rfile و wfile را فراهم می‌کنند.

rfile

یک شیء پرونده که درخواست از آن خوانده می‌شود. از رابط خواندنی io.BufferedIOBase پشتیبانی می‌کند.

wfile

یک شیء پرونده که پاسخ در آن نوشته می‌شود. از رابط قابل‌نوشتن io.BufferedIOBase پشتیبانی می‌کند

تغییر یافته در نسخه‌ی 3.6: wfile همچنین از رابط نوشتنی io.BufferedIOBase پشتیبانی می‌کند.

مثال‌ها

مثال socketserver.TCPServer

این سمت سرور است:

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    """
    The request handler class for our server.

    It is instantiated once per connection to the server, and must
    override the handle() method to implement communication to the
    client.
    """

    def handle(self):
        # self.request is the TCP socket connected to the client
        pieces = [b'']
        total = 0
        while b'\n' not in pieces[-1] and total < 10_000:
            pieces.append(self.request.recv(2000))
            total += len(pieces[-1])
        self.data = b''.join(pieces)
        print(f"Received from {self.client_address[0]}:")
        print(self.data.decode("utf-8"))
        # just send back the same data, but upper-cased
        self.request.sendall(self.data.upper())
        # after we return, the socket will be closed.

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999

    # Create the server, binding to localhost on port 9999
    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        # Activate the server; this will keep running until you
        # interrupt the program with Ctrl-C
        server.serve_forever()

یک کلاس جایگزین برای مدیریت درخواست که از جریان‌ها استفاده می‌کند (اشیاء پرونده‌مانندی که با ارائه رابط پرونده استاندارد، ارتباط را ساده‌تر می‌کنند):

class MyTCPHandler(socketserver.StreamRequestHandler):

    def handle(self):
        # self.rfile is a file-like object created by the handler.
        # We can now use e.g. readline() instead of raw recv() calls.
        # We limit ourselves to 10000 bytes to avoid abuse by the sender.
        self.data = self.rfile.readline(10000).rstrip()
        print(f"{self.client_address[0]} wrote:")
        print(self.data.decode("utf-8"))
        # Likewise, self.wfile is a file-like object used to write back
        # to the client
        self.wfile.write(self.data.upper())

تفاوت این است که فراخوانی readline() در هندلری دوم، recv() را چندین بار فراخوانی می‌کند تا به یک نویسه‌ی خط جدید برسد، در حالی که هندلری اول مجبور بود برای انباشت داده‌ها تا رسیدن خودش به یک نویسه‌ی خط جدید از یک حلقه‌ی recv() استفاده کند. اگر فقط از یک فراخوانی recv() بدون حلقه استفاده می‌کرد، تنها آنچه را که تا آن لحظه از کلاینت دریافت شده بود بازمی‌گرداند. TCP مبتنی بر جریان است: داده‌ها به همان ترتیبی که ارسال شده‌اند می‌رسند، اما هیچ همبستگی بین فراخوانی‌های send() یا sendall() کلاینت و تعداد فراخوانی‌های recv() مورد نیاز در سرور برای دریافت آن‌ها وجود ندارد.

این سمت کلاینت است:

import socket
import sys

HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])

# Create a socket (SOCK_STREAM means a TCP socket)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    # Connect to server and send data
    sock.connect((HOST, PORT))
    sock.sendall(bytes(data, "utf-8"))
    sock.sendall(b"\n")

    # Receive data from the server and shut down
    received = str(sock.recv(1024), "utf-8")

print("Sent:    ", data)
print("Received:", received)

خروجی مثال باید چیزی شبیه به این باشد:

سرور:

$ python TCPServer.py
127.0.0.1 wrote:
b'hello world with TCP'
127.0.0.1 wrote:
b'python is nice'

کلاینت:

$ python TCPClient.py hello world with TCP
Sent:     hello world with TCP
Received: HELLO WORLD WITH TCP
$ python TCPClient.py python is nice
Sent:     python is nice
Received: PYTHON IS NICE

مثال socketserver.UDPServer

این سمت سرور است:

import socketserver

class MyUDPHandler(socketserver.BaseRequestHandler):
    """
    This class works similar to the TCP handler class, except that
    self.request consists of a pair of data and client socket, and since
    there is no connection the client address must be given explicitly
    when sending data back via sendto().
    """

    def handle(self):
        data = self.request[0].strip()
        socket = self.request[1]
        print(f"{self.client_address[0]} wrote:")
        print(data)
        socket.sendto(data.upper(), self.client_address)

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    with socketserver.UDPServer((HOST, PORT), MyUDPHandler) as server:
        server.serve_forever()

این سمت کلاینت است:

import socket
import sys

HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])

# SOCK_DGRAM is the socket type to use for UDP sockets
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# As you can see, there is no connect() call; UDP has no connections.
# Instead, data is directly sent to the recipient via sendto().
sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))
received = str(sock.recv(1024), "utf-8")

print("Sent:    ", data)
print("Received:", received)

خروجی مثال باید دقیقاً مانند مثال سرور TCP باشد.

میکس‌این‌های ناهمگام (Mixins)

برای ساخت هندلرهای ناهمگام، از کلاس‌های ThreadingMixIn و ForkingMixIn استفاده کنید.

مثالی برای کلاس ThreadingMixIn:

import socket
import threading
import socketserver

class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):

    def handle(self):
        data = str(self.request.recv(1024), 'ascii')
        cur_thread = threading.current_thread()
        response = bytes("{}: {}".format(cur_thread.name, data), 'ascii')
        self.request.sendall(response)

class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass

def client(ip, port, message):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect((ip, port))
        sock.sendall(bytes(message, 'ascii'))
        response = str(sock.recv(1024), 'ascii')
        print("Received: {}".format(response))

if __name__ == "__main__":
    # Port 0 means to select an arbitrary unused port
    HOST, PORT = "localhost", 0

    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
    with server:
        ip, port = server.server_address

        # Start a thread with the server -- that thread will then start one
        # more thread for each request
        server_thread = threading.Thread(target=server.serve_forever)
        # Exit the server thread when the main thread terminates
        server_thread.daemon = True
        server_thread.start()
        print("Server loop running in thread:", server_thread.name)

        client(ip, port, "Hello World 1")
        client(ip, port, "Hello World 2")
        client(ip, port, "Hello World 3")

        server.shutdown()

خروجی مثال باید چیزی شبیه به این باشد:

$ python ThreadedTCPServer.py
Server loop running in thread: Thread-1
Received: Thread-2: Hello World 1
Received: Thread-3: Hello World 2
Received: Thread-4: Hello World 3

کلاس ForkingMixIn به همان روش استفاده می‌شود، با این تفاوت که سرور برای هر درخواست یک فرایند جدید ایجاد می‌کند. فقط در پلتفرم‌های POSIX که از fork() پشتیبانی می‌کنند، در دسترس است.