متن خبر

نحوه استفاده از WebSockets در Node.js برای ایجاد برنامه های بلادرنگ

نحوه استفاده از WebSockets در Node.js برای ایجاد برنامه های بلادرنگ

شناسهٔ خبر: 455803 -




این آموزش نحوه استفاده از WebSockets در Node.js را برای ارتباط دو طرفه و تعاملی بین مرورگر و سرور نشان می دهد. این تکنیک برای برنامه های سریع و بلادرنگ مانند داشبورد، برنامه های چت و بازی های چند نفره ضروری است.

فهرست مطالب

وب بر اساس پیام های HTTP درخواست-پاسخ است. مرورگر شما درخواست URL می دهد و سرور با داده پاسخ می دهد. این ممکن است منجر به درخواست های بیشتر مرورگر و پاسخ های سرور برای تصاویر، CSS، جاوا اسکریپت و غیره شود، اما سرور نمی تواند خودسرانه داده ها را به مرورگر ارسال کند.

تکنیک‌های Ajax با نظرسنجی طولانی می‌توانند برنامه‌های وب را به ظاهر در زمان واقعی به‌روزرسانی کنند، اما این فرآیند برای برنامه‌های بلادرنگ واقعی بسیار محدود است. نظرسنجی هر ثانیه در زمان‌های خاص ناکارآمد و در زمان‌های دیگر بسیار کند خواهد بود.

به دنبال یک اتصال اولیه از یک مرورگر، رویدادهای ارسال شده توسط سرور یک پاسخ HTTP استاندارد (جریان شده) است که می تواند در هر زمان از سرور پیام ارسال کند. با این حال، کانال یک طرفه است و مرورگر نمی تواند پیام ها را پس بگیرد. برای ارتباط دو طرفه سریع واقعی، به WebSockets نیاز دارید.

نمای کلی WebSockets

اصطلاح WebSocket به یک پروتکل ارتباطی TCP از طریق ws:// یا wss:// امن و رمزگذاری شده اشاره دارد. این با HTTP متفاوت است، اگرچه می تواند روی پورت 80 یا 443 اجرا شود تا اطمینان حاصل شود که در مکان هایی که ترافیک غیر وب را مسدود می کند کار می کند. اکثر مرورگرهای منتشر شده از سال 2012 از پروتکل WebSocket پشتیبانی می کنند.

در یک برنامه معمولی وب بلادرنگ، شما باید حداقل یک وب سرور برای ارائه محتوای وب (HTML، CSS، جاوا اسکریپت، تصاویر و غیره) و یک سرور WebSocket برای مدیریت ارتباطات دو طرفه داشته باشید.

مرورگر هنوز یک درخواست اولیه WebSocket را به یک سرور ارسال می کند که یک کانال ارتباطی را باز می کند. سپس مرورگر یا سرور می توانند پیامی را در آن کانال ارسال کنند، که رویدادی را در دستگاه دیگر ایجاد می کند.

برقراری ارتباط با سایر مرورگرهای متصل

پس از درخواست اولیه، مرورگر می تواند پیام هایی را به/از سرور WebSocket ارسال و دریافت کند. سرور WebSocket می تواند پیام ها را به/از هر یک از مرورگرهای مشتری متصل خود ارسال و دریافت کند.

ارتباط همتا به همتا امکان پذیر نیست . BrowserA نمی‌تواند مستقیماً به BrowserB پیام دهد، حتی زمانی که آنها در یک دستگاه در حال اجرا هستند و به همان سرور WebSocket متصل هستند! BrowserA فقط می تواند پیامی را به سرور ارسال کند و امیدوار است در صورت لزوم به مرورگرهای دیگر ارسال شود.

پشتیبانی از سرور WebSocket

Node.js هنوز پشتیبانی بومی WebSocket را ندارد، اگرچه شایعاتی وجود دارد که به زودی عرضه می شود! برای این مقاله، من از ماژول ws شخص ثالث استفاده می کنم، اما ده ها مورد دیگر وجود دارد.

پشتیبانی داخلی WebSocket در زمان اجرا Deno و Bun JavaScript در دسترس است.

کتابخانه های WebSocket برای زمان های اجرا از جمله PHP، Python و Ruby در دسترس هستند. گزینه های SaaS شخص ثالث مانند Pusher و PubNub نیز خدمات WebSocket میزبانی شده را ارائه می دهند.

شروع سریع نمایش WebSockets

برنامه های چت Hello, World هستند! از تظاهرات WebSocket، پس من بابت:

    غیر اصیل بودن گفته می شود، برنامه های چت برای توضیح مفاهیم عالی هستند.

    ناتوانی در ارائه راه حل آنلاین کاملا میزبانی شده. من ترجیح می دهم مجبور نباشم جریانی از پیام های ناشناس را نظارت و تعدیل کنم!

مخزن node-wschat را شبیه سازی کنید یا از GitHub دانلود کنید:

 git clone https://github.com/craigbuckler/node-wschat

وابستگی های Node.js را نصب کنید:

 cd node-wschat npm install

برنامه چت را شروع کنید:

http://localhost:3000/ را در تعدادی مرورگر یا برگه باز کنید (همچنین می توانید نام چت خود را در رشته پرس و جو تعریف کنید - مانند http://localhost:3000/?Craig). چیزی را در یک پنجره تایپ کنید و SEND را فشار دهید یا Enter را فشار دهید. آن را در تمام مرورگرهای متصل خواهید دید.

چت بلادرنگ Node.js

نمای کلی کد Node.js

فایل ورودی index.js برنامه Node.js دو سرور را راه اندازی می کند:

    یک برنامه اکسپرس که در http://localhost:3000/ با یک الگوی EJS اجرا می‌شود تا یک صفحه را با HTML، CSS و جاوا اسکریپت سمت مشتری ارائه دهد. مرورگر جاوا اسکریپت از WebSocket API برای ایجاد اتصال اولیه و سپس ارسال و دریافت پیام استفاده می کند.

    یک سرور WebSocket که در ws://localhost:3001/ اجرا می‌شود، که به اتصالات کلاینت ورودی گوش می‌دهد، پیام‌ها را مدیریت می‌کند و قطع ارتباط را نظارت می‌کند. کد کامل:

     import WebSocket , { WebSocketServer } from 'ws' ; const ws = new WebSocketServer ( { port : cfg . wsPort } ) ; ws . on ( 'connection' , ( socket , req ) => { console . log ( ` connection from ${ req . socket . remoteAddress } ` ) ; socket . on ( 'message' , ( msg , binary ) => { ws . clients . forEach ( client => { client . readyState === WebSocket . OPEN && client . send ( msg , { binary } ) ; } ) ; } ) ; socket . on ( 'close' , ( ) => { console . log ( ` disconnection from ${ req . socket . remoteAddress } ` ) ; } ) ; } ) ;

کتابخانه Node.js ws:

هنگامی که یک مرورگر می خواهد متصل شود، یک رویداد "connection" را افزایش می دهد. تابع handler یک شی socket مورد استفاده برای ارتباط با آن دستگاه را دریافت می کند. باید در طول عمر اتصال حفظ شود.

هنگامی که یک مرورگر پیامی ارسال می کند، یک رویداد socket "message" را افزایش می دهد. تابع کنترل کننده پیام را به هر مرورگر متصل (از جمله مرورگر ارسال کننده) باز می گرداند.

هنگامی که مرورگر قطع می‌شود، یک رویداد socket "close" را افزایش می‌دهد - معمولاً وقتی برگه بسته یا تازه‌سازی می‌شود.

مروری بر کد جاوا اسکریپت سمت کلاینت

فایل static/main.js برنامه یک تابع wsInit() است و آدرس سرور WebSocket (دامنه صفحه به اضافه یک مقدار پورت تعریف شده در قالب صفحه HTML) را ارسال می کند:

 wsInit ( ` ws:// ${ location . hostname } : ${ window . cfg . wsPort } ` ) ; function wsInit ( wsServer ) { const ws = new WebSocket ( wsServer ) ; ws . addEventListener ( 'open' , ( ) => { sendMessage ( 'entered the chat room' ) ; } ) ;

رویداد open زمانی فعال می شود که مرورگر به سرور WebSocket متصل می شود. تابع handler یک پیام وارد شده به اتاق چت را با فراخوانی sendMessage() ارسال می کند:

 function sendMessage ( setMsg ) { let name = dom . name . value . trim ( ) , msg = setMsg || dom . message . value . trim ( ) ; name && msg && ws . send ( JSON . stringify ( { name , msg } ) ) ; }

تابع sendMessage() نام و پیام کاربر را از فرم HTML واکشی می‌کند، اگرچه پیام را می‌توان با هر آرگومان ارسال شده setMsg لغو کرد. مقادیر به یک شی JSON تبدیل شده و با استفاده از روش ws.send() به سرور WebSocket ارسال می شوند.

سرور WebSocket پیام ورودی را دریافت می کند که کنترل کننده "message" فعال می کند (به بالا مراجعه کنید) و آن را به همه مرورگرها پخش می کند. این یک رویداد "message" را در هر مشتری ایجاد می کند:

 ws . addEventListener ( 'message' , e => { try { const chat = JSON . parse ( e . data ) , name = document . createElement ( 'div' ) , msg = document . createElement ( 'div' ) ; name . className = 'name' ; name . textContent = ( chat . name || 'unknown' ) ; dom . chat . appendChild ( name ) ; msg . className = 'msg' ; msg . textContent = ( chat . msg || 'said nothing' ) ; dom . chat . appendChild ( msg ) . scrollIntoView ( { behavior : 'smooth' } ) ; } catch ( err ) { console . log ( 'invalid JSON' , err ) ; } } ) ;

کنترل کننده داده های JSON ارسال شده را در ویژگی .data شی رویداد دریافت می کند. تابع آن را به یک شی جاوا اسکریپت تجزیه می کند و پنجره چت را به روز می کند.

در نهایت، هر زمان که کنترل‌کننده "submit" فرم فعال شود، پیام‌های جدید با استفاده از تابع sendMessage() ارسال می‌شوند:

 dom . form . addEventListener ( 'submit' , e => { e . preventDefault ( ) ; sendMessage ( ) ; dom . message . value = '' ; dom . message . focus ( ) ; } , false ) ;

رسیدگی به خطاها

زمانی که ارتباط WebSocket از کار بیفتد، یک رویداد "error" آغاز می شود. این می تواند در سرور انجام شود:

 socket . on ( 'error' , e => { console . log ( 'WebSocket error:' , e ) ; } ) ;

و/یا مشتری:

 ws . addEventListener ( 'error' , e => { console . log ( 'WebSocket error:' , e ) ; } )

فقط کلاینت می تواند با اجرای مجدد سازنده new WebSocket() اتصال را دوباره برقرار کند.

بستن اتصالات

هر یک از دستگاه ها می توانند WebSocket را در هر زمانی با استفاده از روش .close() اتصال ببندند. شما می توانید به صورت اختیاری یک آرگومان عدد صحیح code و رشته reason (حداکثر 123 بایت) ارائه دهید که قبل از قطع شدن آن به دستگاه دیگر منتقل می شود.

سوکت های وب پیشرفته

مدیریت WebSockets در Node.js آسان است: یک دستگاه با استفاده از متد .send() پیامی را ارسال می‌کند که یک رویداد "message" از سوی دیگر راه‌اندازی می‌کند. نحوه ایجاد و پاسخ هر دستگاه به آن پیام ها می تواند چالش برانگیزتر باشد. بخش‌های زیر مسائلی را که ممکن است لازم باشد در نظر بگیرید توضیح می‌دهند.

امنیت WebSocket

پروتکل WebSocket مجوز یا احراز هویت را کنترل نمی کند. نمی‌توانید تضمین کنید که درخواست ارتباط ورودی از یک مرورگر یا کاربری وارد شده به برنامه وب شما سرچشمه می‌گیرد - به‌ویژه زمانی که سرورهای وب و WebSocket ممکن است در دستگاه‌های متفاوتی باشند. اتصال اولیه یک هدر HTTP حاوی کوکی ها و Origin سرور دریافت می کند، اما می توان این مقادیر را جعل کرد.

تکنیک زیر تضمین می کند که ارتباطات WebSocket را به کاربران مجاز محدود می کنید:

    قبل از درخواست اولیه WebSocket، مرورگر با وب سرور HTTP تماس می گیرد (شاید با استفاده از Ajax).

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

    مرورگر در دست دادن اولیه بلیط را به سرور WebSocket ارسال می کند.

    سرور WebSocket بلیط را تأیید می کند و عواملی مانند آدرس IP، زمان انقضا و غیره را قبل از اجازه اتصال تحلیل می کند. هنگامی که یک تیکت نامعتبر باشد، متد WebSocket .close() را اجرا می کند.

    سرور WebSocket ممکن است نیاز داشته باشد که هر چند وقت یکبار رکورد پایگاه داده را دوباره تحلیل کند تا اطمینان حاصل شود که جلسه کاربر معتبر باقی می ماند.

نکته مهم، همیشه داده های ورودی را تأیید کنید :

سرور WebSocket مانند HTTP مستعد تزریق SQL و سایر حملات است.

مشتری هرگز نباید مقادیر خام را به DOM تزریق کند یا کد جاوا اسکریپت را ارزیابی کند.

جدا در مقابل چندین نمونه سرور WebSocket

یک بازی چند نفره آنلاین را در نظر بگیرید. این بازی دارای جهان‌های زیادی است که نمونه‌های جداگانه‌ای از بازی را انجام می‌دهند: universeA ، universeB و universeC . یک بازیکن به یک جهان واحد متصل می شود:

universeA : که توسط player1 ، player2 و player3 ملحق شده است

universeB : توسط player99 ملحق شد

می توانید موارد زیر را پیاده سازی کنید:

    یک سرور WebSocket جداگانه برای هر جهان.

    اکشن بازیکن در universeA هرگز توسط کسانی که در universeB هستند دیده نمی شود. با این حال، راه اندازی و مدیریت نمونه های سرور جداگانه می تواند دشوار باشد. آیا universeC به دلیل نداشتن بازیکن متوقف می‌کنید یا به مدیریت آن منبع ادامه می‌دهید؟

    از یک سرور WebSocket برای همه جهان های بازی استفاده کنید.

    این از منابع کمتری استفاده می‌کند و مدیریت آن آسان‌تر است، اما سرور WebSocket باید ثبت کند که هر بازیکن به کدام جهان می‌پیوندد. وقتی player1 عملی را انجام می دهد، باید برای player2 و player3 پخش شود اما نه player99 .

چندین سرور WebSocket

برنامه چت نمونه می تواند با صدها کاربر همزمان مقابله کند، اما زمانی که محبوبیت و استفاده از حافظه از آستانه های بحرانی بالاتر رفت، از کار می افتد. در نهایت باید با گفت ن سرورهای بیشتر، مقیاس افقی را انجام دهید .

هر سرور WebSocket فقط می تواند مشتریان متصل خود را مدیریت کند. پیامی که از یک مرورگر به serverX ارسال شده است برای کسانی که به serverY متصل هستند پخش نمی شود. ممکن است پیاده سازی سیستم های پیام رسانی ناشر-مشترک (pub-sub) ضروری باشد. مثلا:

    WebSocket serverX می خواهد برای همه مشتریان پیام ارسال کند. این پیام را در سیستم pub-sub منتشر می کند.

    همه سرورهای WebSocket مشترک در سیستم pub-sub یک رویداد پیام جدید (از جمله serverX ) دریافت می کنند. هر کدام می توانند پیام را مدیریت کرده و آن را برای مشتریان متصل خود پخش کنند.

کارایی پیام رسانی WebSocket

ارتباط WebSocket سریع است، اما سرور باید همه مشتریان متصل را مدیریت کند. شما باید مکانیزم و کارایی پیام ها را در نظر بگیرید، به خصوص هنگام ساخت بازی های اکشن چند نفره:

چگونه می‌توانید اقدامات یک بازیکن را در همه دستگاه‌های مشتری همگام‌سازی کنید؟

اگر player1 در مکانی متفاوت از player2 باشد، آیا لازم است اطلاعات player2 در مورد اقداماتی که نمی توانند ببینند ارسال شود؟

چگونه با تأخیر شبکه - یا تأخیر ارتباطی کنار می آیید؟ آیا کسی که یک ماشین و اتصال سریع دارد مزیت ناعادلانه ای خواهد داشت؟

بازی های سریع باید مصالحه کنند. آن را به عنوان بازی در دستگاه محلی خود در نظر بگیرید، اما برخی از اشیاء تحت تأثیر فعالیت های دیگران هستند. بازی‌ها به‌جای ارسال موقعیت دقیق هر شی در هر زمان، پیام‌های ساده‌تر و کمتری را ارسال می‌کنند. مثلا:

objectX در pointX ظاهر شده است

objectY جهت و سرعت جدیدی دارد

objectZ از بین رفته است

هر بازی مشتری شکاف ها را پر می کند. هنگامی که objectZ منفجر می شود، فرقی نمی کند که انفجار در هر دستگاه متفاوت به نظر برسد.

نتیجه

Node.js مدیریت WebSocket ها را آسان می کند. لزوماً طراحی یا کدنویسی برنامه‌های بلادرنگ را آسان‌تر نمی‌کند، اما این فناوری مانع شما نمی‌شود!

معایب اصلی:

WebSocket ها به نمونه سرور جداگانه خود نیاز دارند. درخواست‌های Ajax Fetch() و رویدادهای ارسال شده توسط سرور توسط وب سروری که قبلاً در حال اجرا هستید قابل رسیدگی است.

سرورهای WebSocket به تحلیل های امنیتی و مجوز خود نیاز دارند.

اتصالات WebSocket قطع شده باید به صورت دستی مجدداً برقرار شوند.

اما اجازه ندهید که شما را ناامید کند!

سوالات متداول (سؤالات متداول) در مورد برنامه های بلادرنگ با سوکت های وب و رویدادهای ارسال شده توسط سرور

WebSockets از نظر عملکرد و عملکرد چه تفاوتی با HTTP دارد؟

WebSocket ها یک کانال ارتباطی تمام دوبلکس را از طریق یک اتصال TCP فراهم می کنند، به این معنی که داده ها می توانند به طور همزمان ارسال و دریافت شوند. این یک پیشرفت قابل توجه نسبت به HTTP است، جایی که هر درخواست نیاز به یک اتصال جدید دارد. WebSocket ها همچنین امکان انتقال بلادرنگ داده را فراهم می کنند، و آنها را برای برنامه هایی که نیاز به به روز رسانی فوری دارند، مانند برنامه های چت یا به روز رسانی های ورزشی زنده، ایده آل می کند. از سوی دیگر، HTTP بدون حالت است و هر جفت درخواست-پاسخ مستقل است، که می تواند برای برنامه هایی که به روز رسانی بلادرنگ در آنها ضروری نیست، مناسب تر باشد.

آیا می توانید چرخه عمر اتصال WebSocket را توضیح دهید؟

چرخه حیات یک اتصال WebSocket با یک دست دادن شروع می شود، که اتصال HTTP را به اتصال WebSocket ارتقا می دهد. هنگامی که اتصال برقرار شد، داده ها را می توان بین مشتری و سرور به عقب و جلو فرستاد تا زمانی که هر یک از طرفین تصمیم به بستن اتصال بگیرند. اتصال را می توان با ارسال یک فریم بسته توسط کلاینت یا سرور بسته شد و به دنبال آن طرف دیگر قاب بسته را تأیید کرد.

چگونه می توانم WebSockets را در یک برنامه اندروید پیاده سازی کنم؟

پیاده سازی WebSockets در یک برنامه اندروید شامل ایجاد یک سرویس گیرنده WebSocket است که می تواند به یک سرور WebSocket متصل شود. این را می توان با استفاده از کتابخانه هایی مانند OkHttp یا Scarlet انجام داد. پس از راه‌اندازی کلاینت، می‌توانید یک اتصال به سرور را باز کنید، پیام‌ها را ارسال و دریافت کنید و رویدادهای مختلفی مانند باز کردن اتصال، دریافت پیام و بسته شدن اتصال را مدیریت کنید.

رویدادهای ارسال شده توسط سرور چیست و چگونه با WebSockets مقایسه می شوند؟

رویدادهای ارسال شده از سرور (SSE) استانداردی است که به سرور اجازه می‌دهد تا به‌روزرسانی‌ها را از طریق HTTP به مشتری ارسال کند. بر خلاف WebSockets، SSE یک طرفه هستند، به این معنی که آنها فقط اجازه می دهند تا داده ها از سرور به مشتری ارسال شوند. این باعث می شود آنها برای برنامه هایی که نیاز به ارتباط دو طرفه دارند مناسب نباشند، اما می توانند راه حل ساده تر و کارآمدتری برای برنامه هایی باشند که فقط به به روز رسانی از سرور نیاز دارند.

برخی از موارد استفاده رایج برای WebSockets و رویدادهای ارسال شده توسط سرور چیست؟

WebSockets معمولاً در برنامه‌هایی استفاده می‌شود که نیاز به ارتباط دوطرفه و بلادرنگ دارند، مانند برنامه‌های چت، بازی‌های چند نفره و ابزارهای مشارکتی. از سوی دیگر، رویدادهای ارسال شده توسط سرور، اغلب در برنامه‌هایی استفاده می‌شوند که نیاز به به‌روزرسانی در زمان واقعی از سرور دارند، مانند به‌روزرسانی اخبار زنده، به‌روزرسانی‌های قیمت سهام، یا گزارش‌های پیشرفت برای کارهای طولانی‌مدت.

چگونه می توانم اتصالات WebSocket را در یک برنامه Spring Boot مدیریت کنم؟

Spring Boot از طریق ماژول Spring WebSocket از ارتباطات WebSocket پشتیبانی می کند. می‌توانید از حاشیه‌نویسی @EnableWebSocket برای فعال کردن پشتیبانی WebSocket استفاده کنید و سپس یک WebSocketHandler برای مدیریت چرخه عمر اتصال و مدیریت پیام تعریف کنید. همچنین می توانید از SimpMessagingTemplate برای ارسال پیام به مشتریان متصل استفاده کنید.

ملاحظات امنیتی هنگام استفاده از WebSockets چیست؟

مانند هر فن آوری وب دیگر، WebSockets می تواند در برابر تهدیدات امنیتی مختلف، مانند حملات ربایش وب سایت (CSWSH) و حملات انکار سرویس (DoS) آسیب پذیر باشد. برای کاهش این خطرات، همیشه باید از اتصالات ایمن WebSocket (wss://) استفاده کنید و تمام داده های دریافتی را تأیید و پاکسازی کنید. همچنین باید از مکانیسم‌های احراز هویت و مجوز برای کنترل دسترسی به سرور WebSocket خود استفاده کنید.

آیا می توانم از WebSockets با REST API استفاده کنم؟

بله، می‌توانید از WebSockets در ارتباط با REST API استفاده کنید. در حالی که API های REST برای ارتباط درخواست-پاسخ بدون حالت عالی هستند، WebSockets را می توان برای ارتباطات دوطرفه و بلادرنگ استفاده کرد. این می تواند به ویژه در برنامه هایی که نیاز به به روز رسانی فوری دارند، مانند برنامه های چت یا به روز رسانی های زنده ورزشی مفید باشد.

چگونه می توانم سرور WebSocket را تست کنم؟

ابزارهای مختلفی برای آزمایش سرورهای WebSocket در دسترس هستند، مانند WebSocket.org's Echo Test یا Postman. این ابزارها به شما امکان می دهند اتصال WebSocket را به سرور باز کنید، پیام ارسال کنید و پاسخ دریافت کنید. همچنین می‌توانید با استفاده از کتابخانه‌هایی مانند Jest یا Mocha، آزمایش‌های خودکار را برای سرور WebSocket خود بنویسید.

محدودیت های WebSocket ها و رویدادهای ارسال شده توسط سرور چیست؟

در حالی که WebSockets و رویدادهای ارسال شده از سرور قابلیت‌های قدرتمندی را برای ارتباط بلادرنگ ارائه می‌کنند، اما محدودیت‌های خود را نیز دارند. برای مثال، همه مرورگرها و شبکه‌ها از این فناوری‌ها پشتیبانی نمی‌کنند و در صورت عدم مدیریت صحیح می‌توانند مقدار قابل توجهی از منابع را مصرف کنند. علاوه بر این، پیاده سازی و مدیریت آنها در مقایسه با ارتباطات سنتی HTTP پیچیده تر است.

خبرکاو

ارسال نظر

دیدگاه‌ها بسته شده‌اند.


تبليغات ايهنا تبليغات ايهنا

تمامی حقوق مادی و معنوی این سایت متعلق به خبرکاو است و استفاده از مطالب با ذکر منبع بلامانع است