PDA

View Full Version : حرفه ای: Comet چطور استفاده میشود ؟



EnKamran
دوشنبه 11 اردیبهشت 1391, 18:00 عصر
سلام دوستان من یه پروژه ای داشتم انجام میدادن مه نیاز بود اطلاعات هر چند ثانه واکشی بشه اونم فقط واسه خاطر یک فیلد که تغییر میکرد خلاصه خیلی زورم میومد که به خاطر یک فیلد کل دیتالیستم رو دوباره بایند کنم گشتم و به این کامیت و پوش رسیدم، دوستان تو StackOverFlow امر فرمودن از این تکنولوژی استفاده کنیم که خوب دقیقا همونی هیت که میخوام، حالااااا اصل مطلب اینه که هرکی هرچی داره رو کنه لطفا ببینیم چیزی از توش در میاد یا نه.
تو تالار PHP که یه دوستمون آموزش داده بودن.
اولشم خودم میگم.
یا بذارین همونو کپی کنم خوب توضیح داده بود :


خب حالا این کامیت اصلا برای چی بوجود آمد.
ببینید، ما در وب استاندارد و مرورگرها یک روش و امکانات محدود و ساده ای داریم به این صورت که اگر ما/مرورگر به چیزی/اطلاعاتی نیاز داره، درخواستی (Request) برای اون چیز به سرور سایت مورد نظر ارسال میکنه و بعد اگر جواب (Response) درست وجود داشته باشه (منبع داده ای موجود باشه و ما اجازهء دسترسی بهش رو داشته باشیم) یک پاسخ حاوی دیتای مورد نظر به مرورگر فرستاده میشه و اگر هم جواب درست وجود نداشته باشه، یک کد و پیام خطا برمیگرده و ارتباط HTTP جاری در همین نقطه پایان یافته تلقی میشه.
اگر اون اطلاعات ممکنه در هر زمانی بر روی سرور قرار بگیره که پیشبینی دقیق اون ممکن نیست (بطور مثال پیغامهای طرف مقابل در یک چت)، پس ما مجبوریم مدام در فواصل زمانی معین این کار رو انجام بدیم؛ یعنی درخواست بفرستیم تا ببینیم اطلاعاتی برای ما ارسال شده یا نه و پاسخ موفق یا ناموفق دریافت کنیم.

این عملیات شبیه این هست که مدام منتظر دریافت نامه یا نامه های مهم و فوری از ادارهء پست باشیم و مجبور باشیم برای چک کردن دریافت اون نامه ها مدام در فواصل زمانی کوتاه به ادارهء پست مراجعه کنیم یا باهاشون تماس بگیریم و بگیم «اگر برای من نامه ای اومده لطفا اونو بهم بدید/بگید».

این عملیات ارسال درخواست و دریافت پاسخی که فقط گاهی پاسخ موفق هست، موجب اتلاف منابع سخت افزاری سرور و کلاینت و مصرف پهنای باند بیشتر و نیز تاخیر زمانی بیشتر میشه. یعنی این وسط کلی کار تکراری و بیهوده انجام شده. ضمنا اگر ما بخوایم از ایجاد یا تغییر اطلاعات مورد نظر در روی سرور خیلی سریع مطلع بشیم، نیاز داریم تا این عملیات رو در فواصل زمانی کوتاه انجام بدیم که این میتونه این وضعیت رو بدتر کنه و حتی مشکلات فنی در هر دو سمت کلاینت و سرور ایجاد کنه. فرضا ما هر یک ثانیه بخوایم یک درخواست بفرستیم و پاسخ بگیریم، این هم مصرف منابع و فشار پردازشی روی کلاینت داره و هم روی سرور و هم سرورهای استاندارد وب برای چنین درخواست و پاسخهای سریع و دائمی با یک کلاینت درنظر گرفته نشدن و اولویت بندی خودشون رو دارن و ممکنه درخواست ما رو توی صف انتظار بذارن و با تاخیر خیلی بیشتری از اون چیزی که مد نظر ما بوده بهش جواب بدن، و شاید حتی ممکن باشه بعضی نرم افزارهای امنیتی و فایروالها نسبت به این ارتباطهای دائمی و سریع مشکوک بشن و مشکلی پیش بیاد (این مورد بنظر خودم رسید). خلاصه این روش بخصوص وقتی هدف مطلع شدن از هر تغییر در هر زمان نامعین در سرور با حداکثر سرعت ممکن باشه، مناسب بنظر نمیاد.

خب مثال رفتن یا تماس با ادارهء پست چیز خوبی بود.
ما میتونیم بجای اینکه خودمون مدام با ادارهء پست تماس بگیریم به اونا بگیم که به محض اینکه نامه ای برای ما دریافت کردن بهمون اطلاع بدن (البته برای این کار احتمالا باید آشنایی چیزی در ادارهء پست داشته باشید یا سر کیسه رو شل کنید!!).
روش Comet هم چنین چیزی رو با ترفند پیاده سازی میکنه. با ترفند این کار رو میکنه، چون در وب سرورهای معمولی و مرورگرها امکانات سر راست و استانداردی برای اینطور کارها پیشبینی نشدن.
در روش Comet ما یک درخواست برای شروع ارتباط، به سرور ارسال میکنیم، چون بعلت محدودیت های مرورگرها و سرورها و پروتکل HTTP در این زمینه امکان شروع ارتباط از سمت سرور با کلاینت وجود نداره (البته ظاهرا در آیندهء نه چندان دور شاهد استانداردها و امکانات جدیدی در این زمینه خواهیم بود).

وقتی ما درخواست رو به سرور ارسال کردیم، سرور به ما پاسخ فوری نمیده (مگر اینکه اطلاعات مورد نظر همون لحظه در دسترس باشن). سرور بجای اینکه به ما پیام عدم وجود اطلاعات رو بده، ما رو منتظر میذاره و تکلیف ارتباط HTTP رو مشخص نمیکنه. سرور و مرورگر تا هر وقت بتونن و چیزهایی مثل Timeout و غیره جلوشون رو نگیرن این ارتباط رو باز و بلاتکلیف نگه میدارن، مثلا ممکنه چند دقیقه یا حتی بصورت نامحدود این ارتباط باز بمونه و مرورگر همچنان در انتظار پاسخ از جانب سرور. اما سرور در این اثناء در پشت صحنه داره مدام ورود اطلاعات مورد نظر به سرور یا رخ دادن شرایط لازم رو چک میکنه و به محض اینکه شرایط لازم مهیا شد، اطلاعات مورد نظر رو بعنوان پاسخ درخواست ما از طریق کانکشن باز مونده بهمون ارسال میکنه.

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

این سناریویی که بنده گفتم البته یکی از روشهای پیاده سازی کامیت هست که معمولا با استفاده از AJAX در پشت صحنهء صفحه اجرا میشه و بهش Long polling گفته میشه.
روشهای دیگری هم هست و روشهایی که میشه از طریق یک ارتباط تعداد نامحدودی پاسخ رو در زمانهای مختلف دریافت کرد، ولی اونا مشکلات پیاده سازی یا مشکل ساپورت مرورگر و اینها رو دارن اکثرا.

بنابراین ما در این تاپیک به روش Long polling میپردازیم و از شرح بقیهء روشها بخاطر پرهیز از حجم و پیچیدگی و انحراف تاپیک اجتناب میکنیم.

خب در روش Long polling از هر ارتباط باز تنها برای یک پاسخ میشه استفاده کرد، چون مرورگر بلافاصله بعد از دریافت یک پاسخ کامل، ارتباط HTTP رو خاتمه میده.
در روش کامیت Long polling ما میایم و به محض اینکه یک پاسخ دریافت شد و ارتباط جاری خاتمه پیدا کرد، یک درخواست جدید میفرستیم که نتیجتا یک ارتباط جدید باز شده و دوباره دو طرف منتظر میمونن تا شرایط لازم در سرور رخ بده و پاسخ درخواست HTTP به کلاینت ارسال بشه.

امیدوارم توضیحات خوب بوده باشن.
در پستهای بعدی پیاده سازی عملی این روش رو با کد نشون میدیم.

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

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

d_derakhshani
سه شنبه 12 اردیبهشت 1391, 11:26 صبح
خوشحالم که کسی بلاخره در مورد comet پرسید. همون طور که باید بدونید comet در واقع یک پترن و روش هست و به همین دلیل پیاده سازی های مختلفی ازش وجود داره.
بعضی پیدا سازی ها خیلی کامل هستند و شما به راحتی می تونید ازشون استفاده کنید. مثل WebSync واقعا عالیه. برای خوندن مستندات WebSync به لینک زیر مراجعه کنید
http://api.frozenmountain.com/
سایت اصلی:
http://www.frozenmountain.com/websync/

اما من روش پیاده سازی رو که توضیح میدم، یک نحوه از پیاده سازی comet هست. البته به دلیل کمبود وقت صرفا توضیح میدم و کدی نمی زارم. چند روز دگه میام و کدهاش رو هم اینجا قرار میدم.
به این روش میگن long polling یعنی تا زمانی که میشه پاسخ رو به تعویق میندازیم تا در موقع لزوم جواب کلاینت رو ارسال کنیم.
یک HttpAsyncHandler ایجاد کنید.
در قسمت BeginProcessRequest با استفاده از دستور ThreadPool.QueueUserWorkItem یک نخ رو بگرید و آدرس یک تابع callbak رو بدید(مااسم تابع رو اینجا WaitForAction میزاریم)
در این تابع به اندازه کافی می تونید صبر کنید تا زمان مورد نظر برای پاسخ به کلاینت برسه.
برای مثال برنامه چت رو در نظر بگیرید.
کلاینت درخواست گرفتن پیغام جدید رو می کنه(فعلا به بخش کلاینت نرسیدیم در جلو تر توضیح داده خواهد شد).
کلاینت HttpAsyncHandler شما رو با ارسال id خود فراخوانی می کنه(از طریق query stringو یا post).
شما id رو گرفته و به تابع WaitForAction می دید. این تابع با مکانیزمی به نام sync(که خودتون باید پیاده سازی کنید) انقدر صبر می کنه تا برای id مورد نظر پیغامی دریافت شه. هروقت پیغامی رسید پاسخ رو بر میگردونید.
در بین چند حالت برای کلاینت رخ میده یا جواب انقدر طول میکشه که timeout و یا Faid میده و یا بلاخره پاسخ بر می گرده.
اگه پاسخ برگشت چون ارتباط قطع میشه در دوباره کانشکن جدید ایجاد می کنید و مکانیزم قبلی تکرار میشه(اینکار رو در Jquery در تابع success انجام میدید)
برای fail هم سناریو مثل قبله. اما می تونید دلیل failed شدن رو برسی کنید ببنید دلیل چی بوده تا اگه لازم نبود دیگه request ندید. برای مثال موقعی که آدرس handler پیدا نشد. در واقع در صورت timeout شدن باید دوباره مبادرت به ایجاد کانکشن کنید.
در کلاینت روش سادست فقط کافیه با فراخوانی یکی از تابع های ajax و یا post در JQuery از سرور درخواست اطلاعات کنید.

(من فرض کردم خواننده کار با روش های Async و AsyncHandler رو بلده)

aminghaderi
سه شنبه 12 اردیبهشت 1391, 18:03 عصر
سلام.
ممنون از توضیحاتی که دادین.
حالا ارجعیت این روش با استفاده از تایمر در جاوا اسکریپت یا جی کوئری چی هست؟؟
همون طور که قبلا بحث شده بود الان با تایمر و اجکس هم می شه این کار رو کرد و خیلی منسجم تر و مدیریتش هم دست خود طراح برنامه هست؟!
در حالی که در HttpAsyncHandler و در کل هندلر ها تا حدی می شه وارد جزئیات شد و سر کارهای پیچیده در عین باز گذاشتن دست برنامه نویسان ، جلوی یه سری از کار ها رو می گیره (همون خط قرمر یا چارچوب که می گن) ؟!
و بحث دوم این که شاید 1000 کاربر به سرور وصل شده باشند و سایت ما یه پرتال خبری باشه و با این توضیحات ما 1000 تا نخ (کانکشن باز) داریم ؟؟ این یعنی فشار بروی سرور؟! درسته؟

ممنون.

d_derakhshani
سه شنبه 12 اردیبهشت 1391, 22:55 عصر
حالا ارجعیت این روش با استفاده از تایمر در جاوا اسکریپت یا جی کوئری چی هست؟؟
فکر می کنم خیلی واضحه دیگه. اول حالتی رو فرض کنید که ما از duplex communication استفاده می کنیم. خوب همه می دونن که بهینه ترین روشه که سرور اطلاعات رو به کلاینت push می کنه. خاصیت این روش چیه؟ صرفا یک کانکشن به سرور زده میشه و در موقع نیاز اطلاعات از سرور به کلاینت فرستاده میشه. خوب comet هم همین کار میکنه. comet تضمین می کنه حداکثر یک کانکشن به سرور وجود خواهد داشت نه بیشتر.
حالت timer رو در نظر بگیرید: ممکنه بیش از یک کانکشن به سرور زده شه(مخصوصا زمانی که پاسخ سرور به طول بیانجامه و interval مربوط به timer پایین باشه). دوم اینکه کانکشن زدن به سرور هزینه زیادی داره هم برای کلاینت و هم سرور. دوم اینکه ممکنه وقتی کانکشن زده میشه به دلیل مشغول بودن سرور، زمانی طول بکشه تا شروع پروسس آغاز شه(این مهمه، اما بعدش هر چقدر که لازمه میشه صبر کرد)
سوم اینکه timer به شدت شبکه رو مشغول می کنه چون مدام داره کانکشن میزنه و اطلاعات میفرسته و میگیره. اما comet بعد زدن کانکشن هیچ ترافیکی نمی گیره تا زمانی که پاسخ از سرور بیاد. پس تا زمانی که لازم نباشه ترافیکی وجود نداره. یعنی ترافیک در حداقل ممکنه.
پس تایمر هم مشکل چند کانکشنی داره هم مشکل ترافیک و هم سر بار اضافی.

همون طور که قبلا بحث شده بود الان با تایمر و اجکس هم می شه این کار رو کرد و خیلی منسجم تر و مدیریتش هم دست خود طراح برنامه هست؟!کی گفته برادر این روش منسجم و دست خود طراح برنامه نیست؟مگه ما جز استفاده از ajax و کدهای سرور کار خاص دیگه ای کردیم؟مگه در روش تایمر کار دیگه ای می کنید؟ حتما منظورتون AsyncHandler هست. اما در جلوتر توضیح میدم که این موضوع وارد نیست.


در حالی که در HttpAsyncHandler و در کل هندلر ها تا حدی می شه وارد جزئیات شد و سر کارهای پیچیده در عین باز گذاشتن دست برنامه نویسان ، جلوی یه سری از کار ها رو می گیره (همون خط قرمر یا چارچوب که می گن) ؟!
دوست عزیز منظور از اینکه تا حدی میشه وارد جزئیات شد چیه؟ باید خدمت تون عرض کنم که تمام صفحات aspx هم handler هستند. handler ها زیر بنای صفحات کاری asp.net هست. مسلما وقتی در لایه پایین تر کار می کنید دست تون خیلی باز تره. دیگه منظور از اینکه جلوی یه سری از کارها رو میگیره چیه؟
بعد می خواستم بدونم وقتی یک ajax زده میشه به سرور مگه شما از چیزی جز Handler استفاده می کنید؟(دقت کنید که صفحات Aspx و وب سرویس ها هم هندلر هستند)

و بحث دوم این که شاید 1000 کاربر به سرور وصل شده باشند و سایت ما یه پرتال خبری باشه و با این توضیحات ما 1000 تا نخ (کانکشن باز) داریم ؟؟ این یعنی فشار بروی سرور؟! درسته؟
نه اینطور نیست به هیچ وجه. این نظریه که شما می گید از اینجا ناشی میشه که با نحوه عملکرد نخ ها در IIS آشنایی کافی ندارید.
هیچ وقت 1000 thread توسط iis ایجاد نمیشه. تعداد نخ ها محدوده و اگه بیش از تعداد نخ ها موجود درخواست باشه بقیه در صف می مونن تا نخ ها آزاد شن.
من به همین دلیل گفتم AsyncHandler استفاده کنید.(البته بهتره از ThreadPool استفاده نشه در این صورت) وقتی از async استفاده می کنید کنترل جریان رو به یک thread دیگه خارج از WorkerProccess می دید.(اگه ThreadPool استفاده نکنید)در نتیجه دیگه باری روی iis نیست و کانکشن به حالت pending در میاد و در نتیجه iis از thread آزاد شده برای دریافت کانکشن های جدید استفاده می کنه و دیگه فشاری روی IIS نیست.
در مورد استفاده از thread بهتره که صرفا چند thread اضافی داشته باشید(برای 1000 تا مثلا حداکثر 10 تا) و تمامی کارها رو به اون ها محول کنید.

aminghaderi
چهارشنبه 13 اردیبهشت 1391, 00:08 صبح
ممنون بابت توضیحات مفیدتون.
با مکانیزم کار Asp.net و هندلر ها تا حد زیادی اشنا هستم ، این رو می گم دست ما رو می بنده چون به کلاس های خودش مقیدیت می کنه و باید در کار های حرفه ای از همون ها استفاده کنیم.
یادمه در یه پروژه ای که فقط از هندلر های اصلی استفاده می شد مثل PageFactory و HttpAsyncHandler و یکی دوتا دیگه بود که یادم نمی یاد ولی تو ارشیو کارهام دارم بروی سشن گیر کردم ؟! سشن رو نمی شناخت و عملا من از اون بی بهره بودم ، البته شاید بگید باید از هندلر x استفاده می کردم ولی بنا به ماهیت پروژه فقط باید از چند تا استفاده می شد و خیلی هم سر مدیریت برنامه ازیت شدم و اینجوری بگم کار به جایی رسید که اون روش رو رها کردم و از یه راه دیگه مسئله رو حل کردم.

البته پاراگراف آخر شما تغریبا منو قانع کرد :

نه اینطور نیست به هیچ وجه. این نظریه که شما می گید از اینجا ناشی میشه که با نحوه عملکرد نخ ها در IIS آشنایی کافی ندارید.
هیچ وقت 1000 thread توسط iis ایجاد نمیشه. تعداد نخ ها محدوده و اگه بیش از تعداد نخ ها موجود درخواست باشه بقیه در صف می مونن تا نخ ها آزاد شن.
من به همین دلیل گفتم AsyncHandler استفاده کنید.(البته بهتره از ThreadPool استفاده نشه در این صورت) وقتی از async استفاده می کنید کنترل جریان رو به یک thread دیگه خارج از WorkerProccess می دید.(اگه ThreadPool استفاده نکنید)در نتیجه دیگه باری روی iis نیست و کانکشن به حالت pending در میاد و در نتیجه iis از thread آزاد شده برای دریافت کانکشن های جدید استفاده می کنه و دیگه فشاری روی IIS نیست.
در مورد استفاده از thread بهتره که صرفا چند thread اضافی داشته باشید(برای 1000 تا مثلا حداکثر 10 تا) و تمامی کارها رو به اون ها محول کنید.
درسته در باره نخ ها و مدیریت اونها توسط iis زیاد چیزی نمی دونستم.

d_derakhshani
چهارشنبه 13 اردیبهشت 1391, 00:36 صبح
در یه پروژه ای که فقط از هندلر های اصلی استفاده می شد مثل PageFactory و HttpAsyncHandler و یکی دوتا دیگه بود که یادم نمی یاد ولی تو ارشیو کارهام دارم بروی سشن گیر کردم ؟! سشن رو نمی شناخت و عملا من از اون بی بهره بودم

من فقط اینکه از سشن بی بهره بودید رو نمی فهمم. یعنی چه؟ به راحتی با دستور
HttpContext.Current.Session
به سشن دسترسی دارید دیگه(مگه میشه هندلر سشن و ... رو نشناسه). جز سشن با HttpContext.Current به هرچی لازمه دسترسی دارید.

EnKamran
جمعه 15 اردیبهشت 1391, 12:45 عصر
یک نمونه بنده آماده کردم البته از جای دیگه ای این سمپل رو یرداشتم، توی اون مثال فقط زمان رو نشون میداد یعنی فقط Response.Write رو انجام میداد بنده برای چت درست کردم و از بانک اطلاعاتی استفاده کردم میتونید دانلود کنید، فقط دیتابیسش SQL 2008 هست اسکریپتش رو براتون میذارم

نمونه برنامه (http://enkamran.persiangig.com/ChatApp.zip)
اسکریپت دیتابیس (http://enkamran.persiangig.com/DBScript.sql)

فقط با IE اررور میده

d_derakhshani
جمعه 15 اردیبهشت 1391, 21:54 عصر
من کدها رو دیدم. البته اجرا نکردم(لازم نبود). لطفا منبع ای را که استفاده کردید نیز ذکر کنید.
باید گفت که این کدها صرفا جهت آموزش مفهوم هست و قابلیت اجرایی نداره. برای نمونه کد زیر:

while (true)
{
using (var context = new TestModel())
{
var testmessage = (from t in context.TBL_Chat orderby t.ID descending select t).FirstOrDefault();
if(testmessage != null)
{
Response.Write(Delimiter + testmessage.Message);
Response.Flush();
}
else
{
Response.Write(Delimiter + DateTime.Now.ToString("HH:mm:ss.FFF"));
Response.Flush();
}
}
// Suspend the thread for 1/2 a second
System.Threading.Thread.Sleep(1000);
}

در این کد در هر ثانیه یکبار در یک حلقه بی نهایت یک کانکشن به دیتابیس زده میشه و یک کوئری روی اون اجرا میشه. خوب این روش بسیار نا مناسبیه.
برای sync کردن روشهای بسیار بهتر و اصولی تری هست که هیچ احتیاجی هم به دیتابیس نداره. دوم دقت کنید که برنامه های چت برنامه های دیتابیس گرایی نیستند و اصولا منطق اصلی شون جدا از دیتابیس هست. دیتابیس صرفا برای این استفاده میشه که در صورت آنلاین نبود فرد گیرنده پیغام و یا پشتیبان گیری از از دست رفتن داده ها هنگام مشکلات سروری استفاده بشن. در واقع به هیچ و جه نباید در قسمت sync به این شکل از دیتابیس استفاده بشه.
قسمت بعدی که موجب میشه کدهای مورد نظر قابلیت اجرایی نداشته باشه همین استفاده از Response.write هست. و از آن مهمتر تک کاربره بودن اونه که گیرنده پیغام و فرستنده پیغام رو ردگیری نمی کنه.
نکته آخر هم از XHR استفاده شده که دیگه استفاده از اون لزومی نداره وقتی فریم ورک هایی مثل JQuery هست.
اگه اجازه بدید من در پست بعدی کدهای اصولی مربوط به این روش رو قرار میدم.

aminghaderi
شنبه 16 اردیبهشت 1391, 03:50 صبح
من فقط اینکه از سشن بی بهره بودید رو نمی فهمم. یعنی چه؟ به راحتی با دستور
HttpContext.Current.Session
به سشن دسترسی دارید دیگه(مگه میشه هندلر سشن و ... رو نشناسه). جز سشن با HttpContext.Current به هرچی لازمه دسترسی دارید.

والا من هم نمی دونم؟! با VS2008 نسخه EXPRESS کار می کردم و نتونستم.
از همین HttpContext.Current.Session که فرموید هم استفاده کردم ولی جواب نداد ، یا اکسپرس قابلیتش رو نداره یا من جایی رو اشتباه کرده بودم ؟!(به دلیل پیچیدگی که داشت)


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