1. مهمان گرامی، جهت ارسال پست، دانلود و سایر امکانات ویژه کاربران عضو، ثبت نام کنید.
    بستن اطلاعیه

برنامه نویسی شبکه ( راهنمای گام به گام Indy ) - بخش اول

شروع موضوع توسط hector2141 ‏11/9/12 در انجمن Delphi

  1. کاربر ارشد

    تاریخ عضویت:
    ‏6/9/12
    ارسال ها:
    14,318
    تشکر شده:
    2,702
    امتیاز دستاورد:
    0
    حرفه:
    daneshjo
    Indy TCP/IP server Component
    شاید فکر کنید که از قسمت سخت Indy Component شروع میکنیم ، در واقع درست فکر میکنید چون وقتی ساختن یک client را (در مقالات بعدی ) ببنید متوجه میشوید که ساخت یک client چقدر ساده است .

    برای شروع ، یک پروژه جدید ( New Project ) ایجاد کرده ویک IdTcpServer component در آن قرار دهید .( این کامپوننت در برگه Indy Server قرار دارد و اولین کامپوننت است)
    تنظیمات زیر را روی آن اعمال نمایید :
    Property Name : Property Value
    Active : True
    DefaultPort : 1001
    Greeting Text : hello​

    خوب ... Tcp server شما آماده کار است! بدون اینکه حتی یک خط هم کد نوشته باشید . اگر برنامه خود را اجرا کنید فقط یک صفحه خالی نشان میدهد. برنامه را در حالت Run نگه دارید. برای تست سرور از telnet استفاده میکنیم ( خیلی ساده است ...! اگر مقاله را دنبال کنید) ​

    کافیست در Command Prompt تایپ کنید :​
    c:\> telnet 127.0.0.1 1001​
    که عدد 127.0.0.1 ip محلی شماست و 1001 هم شماره پورتی است که قبلا ست کرده اید.​

    بعد از زدن کلید Enter پیغام hello از طرف سرور برای شما ارسال میشود .(پس ارتباط شما با سرور از پورت 1001 به درستی برقرار شده است .)
    در ضمن فراموش نکنید که برنامه ای که برای سرور نوشته اید حتما باید run شده باشد ( یا بهتر بگویم باید در حال اجرا باشد .)​

    پیغام hello که برای شما ارسال شده است در واقع همانGreeting Text property است که شما ست کرده اید پس هر پیغام دیگری میتواند باشد .

    حالا میخواهم برنامه سروررا کمی توسعه دهم. میخواهیم سرور در برابر دستورات ما عکس-العمل نشان دهد .مثلا با تایپ دستور " Responde "، سرور پاسخ دهد که :
    " Ok ! I have Responded "

    این در واقع اولین اجرای دستورات است .

    برای این کار ابتدا مقدار CommandHandlersEnabled را برابر True قرار دهید . سپس روی CommandHandlers property دوبار کلیک کنید و در پنجره ای که باز میشود روی AddNew کلیک کنید . روی Item ای که اضافه میشود کلیک کرده و تنظیمات زیر را روی Property های آن اعمال کنید :

    Command : Responde
    ReplyNormalText : ok! I have Responded​

    برنامه را کامپایل و اجرا کنید ... سپس مجدد telnet را مانند قبل اجرا کنید و بعد از دریافت پیغام اولیه ، Respode را تایپ کرده و Enter را بزنید .خواهید دید که پیغام مورد نظر بعنوان پاسخ برگردانده میشود .
     
  2. کاربر ارشد

    تاریخ عضویت:
    ‏6/9/12
    ارسال ها:
    14,318
    تشکر شده:
    2,702
    امتیاز دستاورد:
    0
    حرفه:
    daneshjo
    پاسخ : برنامه نویسی شبکه ( راهنمای گام به گام Indy ) - بخش اول

    [h=2]برنامه نویسی شبکه ( راهنمای گام به گام Indy ) - بخش دوم[/h]
    اجازه دهید قبل از اینکه برنامه سرور را توسعه دهیم ، نگاهی دقیقتر داشته باشیم به معماری داخلی Indy server . ( البته فرض می شود که خوانندگان عزیز با مفهوم MultiThreading آشنایی مقدماتی را دارند. ) بعد از این بخش شما تقریبا تمامی جنبه های رفتاری یک Indy Server را خواهید فهمید .​

    تمام نود های فرزند یک TIDTcpServer دارای رفتار زیر هستند :
    وقتی یک client به سرور متصل میشود ، thread ای که listener نام دارد (و کار آن هم دقیقا همان است که از نام آن بر می آید یعنی دایما در حال گوش دادن است تا ایجاد یک ارتباط جدید که توسط یک client ایجاد شده است را تشخیص دهد ) یک thread جدید ایجاد کرده و وظیفه Handle کردن تمام رخدادهای مربوط به این ارتباط را به آن thread واگذار میکند . پس در معماری داخلی یک سرور دو نوع thread وجود دارد : یکی Listener است که دایم در حالت انتظار برای ایجاد یک ارتباط جدید است و دیگری که بعد از ایجاد Connection بوجود می آید و کلیه وظایف ارتباطی بعهده اوست .​

    این معماری زیاد جالب نیست چون client ها هر وقت که بخواهند (بدون توجه به اینکه ممکن است سرور مشغول رسیدگی به دهها درخواست رسیده دیگر باشد ) میتوانند به سرور متصل شوند .
    زیاد نگران نباشید .... چون برای این که بتوان تعداد thread های ایجاد شده را کنترل کرد راه حلهایی وجو د دارد ...
    thread های نوع دوم ( آنهایی که listener نیستند ) را به دو شکل میتوان مدیریت کرد :​

    TIdThreadMgrDefault and TIdThreadMgrPool


    قبل از اینکه در باره این دو مدل مدیریت thread ها و نحوه استفاده آنها توضیح دهم میخواهم خود IDThreadDefault و IDThreadPool را خیلی خلاصه معرفی کنم ...

    IDThreadDefault همان حالت پیش فرضی است که اگر ما هیچ تنظیمی را اعمال نکنیم ، اعمال میشود یعنی برای هر Connection یک thread بطور مجزا ایجاد میشود و هنگام خاتمه ارتباط (Disconnect) شدن از بین میرود .(Destroy)

    اما دومی (IDThreadPool) برای سرور هایی طراحی شده است که میخواهند بازدهی بالاتری داشته باشند . حافظه گرفتن و آزاد کردن آن عملی وقت گیر است به همین دلیل در حالت دوم استخری از thread ها (هرتعداد thread ای که شما تعیین کنید ) حافظه allocate میشود و در طول برنامه دیگر عمل گرفتن و آزاد کردن حافظه نخواهیم داشت .

    این کل مفاهیم Thread ها در Indy بود .(ساده و سریع)

    حتما میخواهید بدانید که چطور می توانید مدل thread ای را که میخواهند استفاده کنید را انتخاب کنید. خیلی ساده ... در برگه ی (Tab ( Indy misc شما هر دو نوع

    TIdThreadMgrDefault and TIdThreadMgrPool

    را مشاهده میکنید . کافیست هر کدام را که میخواهید روی فرم خود قرار داده و در
    ThreadMgr Property ی سرور ( IdTCPServer1 ) آن را بعنوان مقدار وارد کنید .
     
  3. کاربر ارشد

    تاریخ عضویت:
    ‏6/9/12
    ارسال ها:
    14,318
    تشکر شده:
    2,702
    امتیاز دستاورد:
    0
    حرفه:
    daneshjo
    پاسخ : برنامه نویسی شبکه ( راهنمای گام به گام Indy ) - بخش اول

    [h=2]برنامه نویسی شبکه ( راهنمای گام به گام Indy ) - بخش سوم[/h]
    بعد از آشنایی کلی با مفاهیم Threading در Indy ، در این بخش می خواهم کمی بیشتر در باره TIDTcpServer Component صحبت کنم .​

    یک سرور را میتوانیم در دو مد مختلف برنامه ریزی کنیم :

    مد اول :
    مد اول استفاده از CommandHandlers است . نوشتن سرور در این حالت بسیار ساده است . این مد را با مثال توضیح میدهم تا فهم آن ساده تر و سریعتر باشد :
    فرض کنید که می خواهیم سروری داشته باشیم که در برابر دو دستور Hello و Add عکس العمل مناسب را انجام دهد . در برابر کلمه " Hello " ، کلمه " Hi " را برگرداند. اما دستور " Add " کمی متفاوت است چون میخواهیم با فرستادن چندین عدد به همراه دستور add حاصل جمع آنها را بدست آوریم .فرمت کلی دستور ارسالی ما به سرور به این شکل است :

    add no1 no2 .... noN ​


    و سرور حاصل جمع " no1 + no2 + .... + noN " را برخواهد گرداند .​

    مانند مثال اول یک TIDTcpServer روی فرم خود قرار دهید ( یا یک پروژه جدید باز کنید ویا از همان سرور قبلی استفاده کنید یعنی روی یک فرم دو کامپوننت TIDTcpServer نباشد ) . تنظیمات سرور دقیقا همانهایی است که در بخش اول ذکر شده است . حالا روی CommandHandlers دو بار کلیک کرده و در پنجره جدید دو Item جدید ایجاد کنید . برای Item اول مقادیر زیر را ست کنید :​

    Command : Hello
    ReplyNormal : Hi​

    برای Item دوم هم مقدار Command را برابر کلمه " Add " قرار دهید و ...
    حالا باید یه چند خطی کد نویسی کنید ! ​

    در قسمت Event های Item دوم روی OnCommand دوبار کلیک کنید تا وارد محیط کدنویسی شوید :​




    procedure TForm1.IdTCPServer1CommandHandlers1Command(ASender : TIdCommand);
    var
    i:integer;
    f:float;
    begin
    f:=0;
    for i:=0 to ASender.Params.Count-1 do
    f:=f+strtofloat(ASender.Params);
    ASender.Thread.Connection.Writeln(floattostr(f));
    end;





    به همین سادگی !
    حالا برنامه را کامپایل و اجرا کنید . ( در حالی که برنامه شما Run است ) Telnet را با همان دو مقدار قبل ( یعنی با IP ی 127.0.0.1 و پورت 1001 ) فراخوانی کرده و سرور را تست کنید .

    ( ما تا حالا هیچ فکری برای Handle کردن error های احتمالی نکرده ایم ... چون فعلا هدف ما آموزش مفاهیم است نه نوشتن یک برنامه بی عیب و ایراد )

    اگر مجدد به کدی که نوشته اید نگاه کنید مشاهده خواهید کرد که :
    1- ما از یک Property ی ناآشنا به نام Asender استفاده کردیم .
    2- Asender ،دارای یکسری Property است مثل Connection و یا Params .
    3- نهایتا ما برای ارسال string به Tcp/Ip connection از متد Writeln استفاده کردیم .

    منظور ؟ :
    Asender در واقع همان Client ای است که به ما connect شده است . مطلبی که در باره Thread ها گفتم را که هنوز فراموش نکرده اید ؟ برای هر Client بلافاصله بعد از Connect شدن یک Thread جدید ایجاد میشود . در مورد بالا هم Asender در واقع نمایانگر client ای است که به سرور ما متصل شده است . Asender دارای Property های زیادی است که مهمترین آنها Connection است که درواقع پل ارتباطی ما با کاربر است .توسط آن ما میتوانیم یک String یا Integer ویا حتی یک Record را برای client ارسال کنیم .
     
  4. کاربر ارشد

    تاریخ عضویت:
    ‏6/9/12
    ارسال ها:
    14,318
    تشکر شده:
    2,702
    امتیاز دستاورد:
    0
    حرفه:
    daneshjo
    پاسخ : برنامه نویسی شبکه ( راهنمای گام به گام Indy ) - بخش اول

    [h=2]برنامه نویسی شبکه ( راهنمای گام به گام Indy ) - بخش پایانی[/h]
    سلام

    همانطور که در انتهای بخش قبل قول داده بودم ، میخواستم درباره حالتی از سرور صحبت کنم که به طور اتومات دربرابر Command های کاربر عکس العمل نشان نمیدهد (مد دوم) ولی بنا به دلایلی این بحث را به آینده موکول میکنم .

    امروز درباره کامپوننت TIDTcpClient توضیح خواهم داد :

    اگر سه بخش قبل را فهمیده باشید ، فهمیدن این قسمت زیاد وقت شما را نمیگیرد.کافیست یک کامپوننت TIDTcpclient (در برگهIndy Client ، اولین کامپوننت است ) را روی فرم جدیدی که ایجاد کرده اید قرار دهید تا 80 درصد راه ساخت client را طی کرده باشید ..!
    20 درصد باقیمانده موضوع این بخش از مقاله است .

    سروری که در بخش قبل ایجاد کردیم را به خاطر بیاورید .. دو دستور داشت به نامهای Hello و Add . میخواهیم client خود را برای برقراری ارتباط با این سرور تنظیم کنیم.


    روی فرم خود علاوه بر کامپوننت TIDTcpClient دو Button ویک Tlistbox هم قرار دهید . در این Listbox شما مقادیری که برای عمل جمع به سرور ارسال خواهید کرد را قرار خواهید داد . حالا باید مقادیر پورت ( Port ) و Host را برای TIDTcpClient ست کنید .

    Port همان است که برای سرور قرار داده اید ( در مثال ما 1001 است ).

    اما Host :
    اگر می خواهید برنامه client را در همان کامپیوتری اجرا کنید که سرور هم روی آن اجرا میشود مقدار Host را برابر 127.0.0.1 قرار دهید وگرنه مقدار آن را برابر با IP کامپیوتر سرور قرار دهید .

    ابتدا دستور Hello :

    روی اولین button دوبار کلیک کرده و کد زیر را وارد کنید :


    [TABLE]
    [TR]
    [TD="class: gutter"]1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [/TD]
    [TD="class: code"]<b>procedure</b> TForm1.Button1Click(Sender:TObject);
    begin
    with IdTcpClient1 do
    begin
    connect;
    Writeln('Hello');
    ShowMessage(Readln);
    Disconnect;
    end;

    end;

    [/TD]
    [/TR]
    [/TABLE]






    حالا برنامه را اجرا کنید ( فراموش نکنید که همواره قبل از اجرای برنامه Client حتما باید برنامه سرور را Run کرده باشید وگرنه با پیغام " Socket Error #10061" مواجه خواهید شد ). اگر تمام مراحل را بدرستی طی کرده باشید باید پیغام "Ok, I have responded" دریافت کنید .



    حالا نوبت دومین دستور است . اینبار روی دومین button دوبار کلیک کرده و قطعه کد ساده زیر را وارد کنید :​


    [TABLE]
    [TR]
    [TD="class: gutter"]1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    [/TD]
    [TD="class: code"]<font xmlns="XHTML namespace"><font xmlns="XHTML namespace">
    </font><font xmlns="XHTML namespace"><b>procedure TForm1.Button2Click(Sender:TObject); </b></font><font xmlns="XHTML namespace">
    var


    </font><font xmlns="XHTML namespace">command: string;
    </font><font xmlns="XHTML namespace">i:integer;
    begin
    command:='Add'; </font><font xmlns="XHTML namespace">

    for i:=0 to ListBox1.Items.Count -1 do </font><font xmlns="XHTML namespace">command:=command+' '+ListBox1.Items;
    </font><font xmlns="XHTML namespace">with IdTcpClient1 do
    begin
    connect;
    Writeln(command);
    ShowMessage(Readln);
    Disconnect;
    end;
    end;</font><font xmlns="XHTML namespace">
    </font></font>

    [/TD]
    [/TR]
    [/TABLE]










    تنها تفاوت آن با قبلی این است که در اینجا ما دستور add را با پارامترهایی که در listbox وارد کرده اید ترکیب کرده و حاصل را که یک String است برای سرور ارسال میکنیم. ​









    نکته : ​


    هنگام ارسال و دریافت یک پیغام بین Client و Server حتما به ترتیب متد های ارسال دقت لازم را داشته باشید .. مثلا اگر در سمت سرور چیزی مانند زیر داشتید :​

    [TABLE]
    [TR]
    [TD="class: gutter"]1
    2
    3
    4
    5
    [/TD]
    [TD="class: code"]WriteStream (...);
    Writeln(...);
    ReadStream(...);

    [/TD]
    [/TR]
    [/TABLE]





    در سمت کلاینت باید به این ترتیب باشد :​



    [TABLE]
    [TR]
    [TD="class: gutter"]1
    2
    3
    4
    [/TD]
    [TD="class: code"]ReadStream(...);

    ReadLn;
    WriteStram(...);

    [/TD]
    [/TR]
    [/TABLE]





    در حالات دیگر ( اگر ترتیب را رعایت نکنید ) قادر به برقراری درست ارتباط نخواهید بود.​




    خوب ... کار Client هم تمام شد . در واقع مطالب مقدماتی ای که فکر میکردم برای شروع کار لازم دارید (تا حد امکان ساده و سریع ) تمام شد .