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() پشتیبانی میکنند، در دسترس است.