# Native Code > برنامه نویسی در Delphi > توسعه نرم افزارهای تحت شبکه >  ساخت یک برنامه ی  مسنجر تحت شبکه

## ghasemshami

من یک نمونه برنامه نوشتم که چت بین کلاینت و سرور هست که براساس شماره آی پی کار می کنه که میشه توش هم شماره کلاینت آی پی در اینترنت فرد رو داد و تحت اینترنت با هم دیگه چت کنند که من در این مورد دارم باهاش کار می کنم

این برنامه وقتی کلاینت 1 دکمه Connect رو میزنه و به سرور اتصال پیدا می کنه مشکلی نداره ولی وقتی کلاینت 2 یا 3 یا هر چند تا دیگه دکمه Connect رو میزنن سرور به کلاینت آخری که دکمه Connect رو زده به اون می تونه فقط پیام ارسال کنه و دیگر کلاینت ها نمی تونه مشکل این برنامه کجاست

نمونه ای از برنامه رو براتون می زارم لطف کنید و راهنمائیم کنید

و از دلفی 7 استفاده می کنم و از ابزار Indy استفاده می کنم

لطف کنید و مرا راهنمایی کنید

----------


## nilidelphi

با سلام خدمت دوستان
من قرار یک برنامه ی تحت شبکه با دلفی بسازم که توش باید هم از socket استفاده کنم و هم از multithread استفاده کنم به این شکل :
باید یک برنامه برای سرویس گیرنده بسازم که این چیزی نیست
و یک برنامه برای سرویس دهنده بسازم که باید از یک ip و فقط از یک port استفاده کند یعنی به تعداد client  هایی که به برنامه ی سرور کانکت می شوند از یک پورت باید به هر کدام یک ترد اختصاص بدهد.سوال اینجاست که برنامه ی طرف client چطوئ باید ip سرور رو بصورت پویا پیدا کنه و کانکت بشه؟ 
و برنامه ی سرور چطور برای هر برنامه client که بهش کانکت میشه یک ترد اختصاص بده؟
حالا اگه کسی بتونه کمک کنه خیلی ممنون می شم.

----------


## nilidelphi

دوست گرامی فکر کنم باید از multithreading استفاده کنی من خودم در حال تحقیقم اگه به نتیجه رسیدم برات میفرستم

----------


## vcldeveloper

> برنامه ی طرف client چطوئ باید ip سرور رو بصورت پویا پیدا کنه و کانکت بشه؟


برنامه های چت موجود (مثل یاهو مسنجر) آدرس سرور را بطور پویا پیدا نمی کنند، بلکه آدرس سرور ثابت هست، یا لیستی از آدرس های سرور دارند که اگر یکی کار نکرد، به سرور بعدی وصل میشند. البته در اینگونه برنامه های تجاری بجای IP Address از نام دامین استفاده می کنند، که اگر روزی IP Address تغییر کرد، نیاز نباشه کل کلاینت ها تغییر کنند، مثل آدرس وب سایت که همیشه برای کاربر استفاده کننده ثابت هست، حتی اگر IP سرور سایت تغییر کنه.




> و برنامه ی سرور چطور برای هر برنامه client که بهش کانکت میشه یک ترد اختصاص بده؟


برنامه سرور یک پورت را باز میکنه، و یک Thread که اصطلاحا به آن Listener گفته میشه، را به آن اختصاص میده تا ترافیک پورت را تحت نظر داشته باشه. هر زمانی که کلاینت خاصی با سرور ارتباط برقرار میکنه، این Thread به سرور اطلاع میده، سرور هم یک Thread جدید میسازه، و پیام کلاینت را در اون Thread جدید پردازش میکنه.
لزومی نداره شما درگیر اینگونه جزئیات بشید، این کارهای سطح پایین بطور خودکار توسط Indy مدیریت میشند

----------


## ghasemshami

آقای کشاورز سخن شما درست اما در یکی از تاپیک ها بود که با حلقه For پیام ارسال می کردید که به تمامی Client ها می رفت اما من نمی خوام اینطوری باشه بلکه سرور باید بدونه که کدوم Client درخواستی رو کرده که به همون Client پاسخ بده ولی کار شما برای همه می فرسته

----------


## nilidelphi

سلام آقای کشاورز 
می تونید در مورد thread هایی که گفتین یهکم کاربردی تر توضیح بدین
چون من باید یه همچین برنامه ای بنویسم که چند تا کلاینت از یک پورت به سرور متصل شوند(حالا ip خیلی مهم نیست)
ولی من می خوام کلاینت ها از طریق سرور با هم چت کنند یا ......
البته اگه در مورد پایگاه داده در Indy هم بتونین توضیح بدین ممنون میشم.

----------


## vcldeveloper

> آقای کشاورز سخن شما درست اما در یکی از تاپیک ها بود که با حلقه For پیام ارسال می کردید که به تمامی Client ها می رفت اما من نمی خوام اینطوری باشه بلکه سرور باید بدونه که کدوم Client درخواستی رو کرده که به همون Client پاسخ بده ولی کار شما برای همه می فرسته


برای چه کاری بود؟ قرار نیست هر کدی هر جا کاربرد داشته باشه. یکی میخواد از طرف سرور یک پیامی را برای همه کلاینت هایی که به اون سرور لاگین کردند، ارسال کنه. یکی میخواد هر زمان که کلاینت درخواستی به سرور ارسال کرد، سرور جوابش را بده.
اونی که شما دیدید، برای ارسال پیام از سرور به همه کلاینت ها، بدون آنکه کلاینتی درخواستی ارسال کرده باشه، بود. مثلا سرور میخواد به همه کلاینت های خودش بگه که باید لیست خودشان را آپدیت کنند.
وقتی یک کلاینت برای سرور درخواستی ارسال میکنه، سرور بطور خودکار برای آن درخواست یک Thread اختصاصی درست میکنه، و جوابی که سرور به اون کلاینت میده، از طریق همون Thread هست و تداخلی با سایر Threadها نداره.




> می تونید در مورد thread هایی که گفتین یهکم کاربردی تر توضیح بدین
> چون من باید یه همچین برنامه ای بنویسم که چند تا کلاینت از یک پورت به سرور متصل شوند(حالا ip خیلی مهم نیست)
> ولی من می خوام کلاینت ها از طریق سرور با هم چت کنند یا ......


همانطور که گفتم، نیازی نیست شما درگیر اون جزئییاتی که گفتم بشید. Indy خودش اون بخش از کار را برای شما انجام میده. کاری که شما باید انجام بدید، مواردی هست مثل چگونکی لاگین کردن کلاینت ها به سرور، قالب پیام های رد و بدل شده بین سرور و کلاینت، مدیریت کلاینت ها در سمت سرور، و مواردی از این قبیل.




> البته اگه در مورد پایگاه داده در Indy هم بتونین توضیح بدین ممنون میشم.


Indy ربطی به پایگاه داده نداره. Indy ابزاری برای انتقال داده بر روی شبکه با استفاده از Protocolهای شناخته شده ایی مثل TCP, HTTP, FTP, POP3، و غیره. اینکه داده تبادل شده رکورد بانک اطلاعاتی هست، یا یک فایل هست، یا یک پیام ساده متنی، ربطی به Indy نداره. شما می تونید از Indy به عنوان زیرساخت استفاده کنید، و بر روی آن لایه ایی برای کار با بانک اطلاعاتی بسازید که برای تبادل داده با سایر لایه ها، از Indy استفاده میکنه. در هر حال، این بحث ربطی به موضوع این تاپیک نداره. اگر مایلید نمونه برنامه ایی ببینید که داده های بانک اطلاعاتی را از طریق Indy در شبکه منتقل میکنه، یک نمونه ساده به همراه مثال های Mastering Delphi 7 هست.

----------


## ghasemshami

ببینید آقای کشاورز من طبق گفته خودتون اومدم و لیست کاربرانی که اتصال می کنن رو داخل یک آرایه میریزم اما Server وقتی که می خواد پیام ارسال کنه به آخرین کاربری که Connect شده پیام رو ارسال می کنه

میشه بگید چطوری بفهمم کدوم کاربره و چطوری براش ارسال کنم

----------


## vcldeveloper

> ببینید آقای کشاورز من طبق گفته خودتون اومدم و لیست کاربرانی که اتصال می کنن رو داخل یک آرایه میریزم اما Server وقتی که می خواد پیام ارسال کنه به آخرین کاربری که Connect شده پیام رو ارسال می کنه
> 
> میشه بگید چطوری بفهمم کدوم کاربره و چطوری براش ارسال کنم


من که کد شما را برای ذخیره IP کاربران، و چگونگی ارسال پیام ندیدم. وقتی ندیدم، چی رو راهنمایی کنم؟

----------


## ghasemshami

این هم کد من خدمت شما


  private
    FSyncObj : TMultiReadExclusiveWriteSynchronizer;
    FClients : TObjectList;
  public
    property Clients: TObjectList read FClients;
    
  end;
var
  Form1: TForm1;
  mythread : TIdPeerThread ;
implementation
 
{$R *.dfm}
procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread);
begin
  FSyncObj.BeginWrite;
  try
    FClients.Add(AThread);
  finally
    FSyncObj.EndWrite;
  end;
end;
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
begin
CDarkhast.Items.Add(AThread.Connection.ReadLn()) ;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  FSyncObj := TMultiReadExclusiveWriteSynchronizer.Create;
  FClients := TObjectList.Create(False);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(FClients);
  FreeAndNil(FSyncObj);
end;
procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread);
begin
  FSyncObj.BeginWrite;
  try
    FClients.Remove(AThread);
  finally
    FSyncObj.EndWrite;
  end;
end;


و این هم برای ارسال پیام


        FSyncObj.BeginRead;
        try
        for i := 0 to FClients.Count-1 do
        begin
          Client := FClients[i] as  TIdPeerThread;
          Client.Connection.WriteLn(Server);
            end;
        finally
        FSyncObj.EndRead;
        end;

----------


## vcldeveloper

خب، بررسی کردید که هر زمان که کلاینتی به سرور متصل میشه، آیا Thread مربوط به آن به درستی در لیست قرار میگیره یا نه؟
کلاینت های شما چطوری به سرور وصل میشند؟ بعد از هر درخواست بلافاصله disconnect میشند؟

مقدار Server در کد زیر چی هست؟
Client.Connection.WriteLn(*Server*);

----------


## ghasemshami

متن هستش

Server یک متغییر از نوع string هست

----------


## ghasemshami

نه کلاینت ها وقتی متصل می شند disconnect نمیشن و همیشه وصل هستند

----------


## فریده صادقی

تشکر از کد شما ولی اگر توضیح هم برا من بدهید ممنون می شوم چون باید کنفرانس بدهم لطفا درخواستم را پاسخ دهید

----------


## Mask

> تشکر از کد شما ولی اگر توضیح هم برا من بدهید ممنون می شوم چون باید کنفرانس بدهم لطفا درخواستم را پاسخ دهید


 میشه در مورد درخاستتون بیشتر توضیح بدید.

----------


## مهران رسا

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


جناب کشاورز میشه چگونگی انجام این کار رو شرح بدید ؟
با این روش میشه تعداد اتصالات رو بدست آورد . اما چگونگی ارسال پیام برای تک تک کلاینت ها رو نمیدونم .
i := TCPServer.Bindings.Count;
 
ضمناً یه سوال داشتم : در شرایطی که مثلاً به دلیل مشکلات Listen ، امکان استفاده از TCPServer در کلاینت ها وجود نداشه باشه استفاده از Timer برای دریافت پیام های سرور در طولانی مدت میتونه قابل اطمینان باشه ؟ امکان پیاده سازی رویدادی در این مورد وجود نداره ؟

----------


## Felony

> جناب کشاورز میشه چگونگی انجام این کار رو شرح بدید ؟
> با این روش میشه تعداد اتصالات رو بدست آورد . اما چگونگی ارسال پیام برای تک تک کلاینت ها رو نمیدونم .
> i := TCPServer.Bindings.Count;
>  
> ضمناً یه سوال داشتم : در شرایطی که مثلاً به دلیل مشکلات Listen ، امکان استفاده از TCPServer در کلاینت ها وجود نداشه باشه استفاده از Timer برای دریافت پیام های سرور در طولانی مدت میتونه قابل اطمینان باشه ؟ امکان پیاده سازی رویدادی در این مورد وجود نداره ؟


کجای کار مشکل دارید ؟
به شکل زیر میتونید به IP سیستم های متصل شده به سرور دسترسی پیدا کنید :
IdTCPServer1.Bindings.Items[i].
متد هایی برای ارسال و دریافت و ... در اختیار شما قرار میگیره که میتونید تو یک حلقه پیغامتون رو برای همه اعضای لیست بفرستید ، برای جلوگیری از وقفه در طول ارسال هم قبل از ارسال آنلاین بودن سیستم مقصد رو بررسی کنید .

برای تست کردن آنلاین بودن سیستم هم میتونید IP سیستم رو پینگ کنید ( قبلا نمونه ای قرار داده بودم )

----------


## مهران رسا

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


مرسی برادر مجتبی . همون متد ارسال رو نمیدونم . ممنون میشم راهنمایی کنی.
ضمناً راه بهتری برای بررسی برقرار بودن ارتباط از طریق خود Indy وجود نداره ؟ Ping زمان بر هست .

----------


## Felony

> مرسی برادر مجتبی . همون متد ارسال رو نمیدونم . ممنون میشم راهنمایی کنی.


با کد زیر میتونید IP سیستم ها رو تو یک حلقه بگیرید ، برای ارسال هم باید از یک TCPClient در سمت سرور استفاده کنید و سیستم های کلاینت باید TCPServer داشته باشند ، قبلا هم آقای کشاورز در یکی از پست هاشون اشاره کردن کار TCPServer گوش دادن به درخواست و کار TCPClient ارسال درخواست هست .
IdTCPServer1.Bindings.Items[SystemIndex].PeerIP;




> ضمناً راه بهتری برای بررسی برقرار بودن ارتباط از طریق خود Indy وجود نداره ؟ Ping زمان بر هست .


استفاده از Icmp سرعت خوبی هم داره :
IcmpClient.Host:= '127.0.0.1';
  IcmpClient.Ping();
  if IcmpClient.ReplyStatus.BytesReceived=0 then
    ShowMessage('False')
  else
    ShowMessage('True');

----------


## مهران رسا

> سیستم های کلاینت باید TCPServer داشته باشند


مسئله همینجاست . سیستم کلاینت اگه TCPServer داشته باشه که دیگه بهش کلاینت نمیگن . همونطور که گفتم امکان استفاده از TCPServer در کلاینت ها به هیچ وجه وجود نداره . چراکه مشکلاتی مثل فایروال ، پورت های اشغال شده و ... در رایانه کاربران از دست ما خارج هست . پس مسئله Listening از سمت کلاینت منتفی هست .
اما الآن سوال اینجاست که آیا متد خاصی برای اینکار وجود نداره ؟ اصلاً وظیفه شی Bindings چیه ؟ اگه بخوام منظورم رو واضح تر بگم . من میخوام یک سیستم چت ساده ایجاد کنم . بطوری که پیام های شما برای رسیدن به مقصد ابتدا باید از سرور عبور کنن . خب سرور پیغام کنترلی رو دریافت میکنه و با نشانه ای که در پیغام هست تصمیم میگیره متن مورد نظر رو برای کلاینت شماره 2 ارسال کنه . پیغام ها در سرور در رویداد Execute دریافت میشن و جواب هم از همین طریق ارسال میشه . اما اینبار نمیخوایم جواب رو برای کاربر کانشکن جاری ارسال کنیم . میخوایم بعد از پردازش های انجام و مشخص شدن Index کانشکن مورد نظر ، جواب رو برای کاربر دیگه ای ارسال کنیم .

----------


## Felony

:لبخند گشاده!: حالا ما هر چی میگیم بگو مشکل پیش میاد ، بابا راه اصولیش همینه ، شما 2 تا پرت برای برنامت نیاز داری ، در ضمن میتونی برنامت رو به فایروال معرفی کنی که نیازی به گرفتن اجازه از کاربر نباشه .




> ضمناً یه سوال داشتم : در شرایطی که مثلاً به دلیل مشکلات Listen ، امکان استفاده از TCPServer در کلاینت ها وجود نداشه باشه استفاده از Timer برای دریافت پیام های سرور در طولانی مدت میتونه قابل اطمینان باشه ؟


شئ تایمر از پیغام WM_Timer استفاده میکنه ، پس نمیشه بهش اطمینان داشت ممکنه پیغام در صف پردازش بمونه و برای پردازشش زمان طولانی وقت صرف بشه .

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

----------


## مهران رسا

> حالا ما هر چی  میگیم بگو مشکل پیش میاد ، بابا راه اصولیش همینه ، شما 2 تا پرت برای  برنامت نیاز داری ، در ضمن میتونی برنامت رو به فایروال معرفی کنی که نیازی  به گرفتن اجازه از کاربر نباشه .


امکان استفاده از راهی که شما میگی وجود نداره  :لبخند:  .  قبلاً در روش Non-Blocking با استفاده از رویداد ها خیلی راحت این مسائل رو حل میکردم . 




> سیستم های کلاینت باید TCPServer داشته باشند
> 			
> 		
> 
>  
> 
> 
> 
> 			
> ...


احتمالاً آقای کشاورز بتونن راه حلی ارائه کنند

----------


## vcldeveloper

> همونطور که گفتم امکان استفاده از TCPServer در کلاینت ها به هیچ وجه وجود  نداره . چراکه مشکلاتی مثل فایروال ، پورت های اشغال شده و ... در رایانه  کاربران از دست ما خارج هست . پس مسئله Listening از سمت کلاینت منتفی هست .


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




> قبلاً در روش Non-Blocking با استفاده از رویداد ها خیلی راحت این مسائل رو  حل میکردم .


در اون حالت هم عملا شما در حال Listen کردن روی یک پورت بودید. هر زمان داده ایی به اون پورت می رسید، یک Event در برنامه شما فراخوانی میشد، و شما پردازش خودتان را انجام می دادید.




> اصلاً وظیفه شی Bindings چیه ؟


خصوصیت Bindings ربطی به این بحث نداره. این خصوصیت زمانی استفاده میشه که یک سرور بخواد روی چند پورت مختلف Listen کنه، یا چند کارت شبکه روی یک سیستم وجود داشته باشه، و سرور بخواد از یکی از این کارت ها استفاده کنه، یا روی هر کارت شبکه موجود یک Socket باز کنه، و روی پورت تعیین شده Listen کنه. در واقع با استفاده از Bindings شما IP و Portایی که سرور باید روی آن Listen کنه را مشخص می کنید.




> من میخوام یک سیستم چت ساده ایجاد کنم . بطوری که پیام های شما برای رسیدن  به مقصد ابتدا باید از سرور عبور کنن . خب سرور پیغام کنترلی رو دریافت  میکنه و با نشانه ای که در پیغام هست تصمیم میگیره متن مورد نظر رو برای  کلاینت شماره 2 ارسال کنه .


از یک IdCmdTcpServer استفاده کنید. برای آن فرامین مثل Login, LogOut, SendMsg، و غیره تعریف کنید. هر زمان که کلاینتی به سرور متصل شد، و فرمان Login را ارسال کرد، مشخصات اتصالش را چک کنید، اگر صحیح بود، ارتباط را قبول کنید، و آدرس IP و Port آن کلاینت را به همراه شناسه آن در یک لیست در سرور ذخیره کنید. اگر صحیح نبود، اتصالش را قطع کنید. 
هر زمان هم کلاینتی دستور LogOut را صادر کرد، شناسه اش را از لیستی که در سرور دارید، پیدا کنید، و آن کلاینت را از لیست کاربران خارج کنید.
هر زمان کلاینتی دستور SendMsg را به سرور صادر کرد، شناسه کاربر مقصد را از پیام دریافتی استخراج کنید، و نام وی را در لیست کاربران متصل به سرور جستجو کنید. اگر شناسه اش جزو لیست کاربران متصل به سرور بود، آدرس IP و Portاش را از همان لیست استخراج کنید، و پیام مربوطه را به آن آدرس IP و Port ارسال کنید.

----------


## مهران رسا

> در اون حالت هم عملا شما در حال Listen کردن روی یک پورت بودید. هر زمان  داده ایی به اون پورت می رسید، یک Event در برنامه شما فراخوانی میشد، و  شما پردازش خودتان را انجام می دادید.


در اون حالت پیام ها از طریق مسیر کانکشنی که از قبل باز شده بود ارسال میشد و نیازی به ایجاد اتصال جدید و متعاقباً به وجود آمدن پورت اضافه ای در کلاینت ها نبود .



> آدرس IP و Portاش را از همان لیست استخراج کنید، و پیام مربوطه را به آن  آدرس IP و Port ارسال کنید.


و دیگه نیاز به این کار نبود .



> هر زمان هم کلاینتی دستور LogOut را صادر کرد، شناسه اش را از لیستی که در  سرور دارید، پیدا کنید، و آن کلاینت را از لیست کاربران خارج کنید.


 آمدیم و کلاینت هیچ وقت فرمان Logout ارسال نکرد . با قطع اتصال اینترنت کلاینت ، Connection مربوطه چطوری میخواد از این موضوع با خبر بشه ؟ احتمالاً با پینگ ؟؟! حداقل باید توسط رویدادی در سرور مشخص بشه کدوم کلاینت قطع شده . این رویداد ها در non-blocking وجود دارند . امکان پیاده سازیشون برای Indy وجود نداره ؟



> بحث فایروال و پورت اشغال شده هم در هر صورت برای یک برنامه تحت شبکه هست


اما هموطنور که میدونید در سیستم عاملی مثل ویستا فایروال فقط زمانی که برنامه بخواد Listen بشه از کاربر مجوز درخواست میکنه و در حالت اتصال به سرور ممنوعیتی اعمال نمیشه




> سایر برنامه های چت معروف هم همچین مکانیزمی را پیاده سازی می کنند


منظور برنامه هایی هست که بر پایه ی Blocking نوشته شدند ؟

----------


## vcldeveloper

> در اون حالت پیام ها از طریق مسیر کانکشنی که از قبل باز شده بود ارسال  میشد و نیازی به ایجاد اتصال جدید و متعاقباً به وجود آمدن پورت اضافه ای  در کلاینت ها نبود .


الان هم می تونید با همون Indy اون کار رو انجام بدید، با این تفاوت که باید خودتون یک Thread جداگانه ایجاد کنید، و در اون خودتون چک کردن سوکت رو برعهده بگیرید.
البته اجباری هم به استفاده از Indy نیست، دوست داشتید، می تونید از ICS استفاده کنید که Asynchronous هست، یا مستقیما از WinSock استفاده کنید.




> و دیگه نیاز به این کار نبود .


چرا نبود؟! دریافت لیست IP و Port مربوط به سرور چت شما ست، و ربطی به کلاینت ها نداره. شما در هر حال باید در سمت سرور لیستی از کلاینت های متصل شده نگهداری کنید.




> آمدیم و کلاینت هیچ وقت فرمان Logout ارسال نکرد . با قطع اتصال اینترنت  کلاینت ، Connection مربوطه چطوری میخواد از این موضوع با خبر بشه ؟  احتمالاً با پینگ ؟؟! حداقل باید توسط رویدادی در سرور مشخص بشه کدوم  کلاینت قطع شده . این رویداد ها در non-blocking وجود دارند . امکان پیاده  سازیشون برای Indy وجود نداره ؟


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




> منظور برنامه هایی هست که بر پایه ی Blocking نوشته شدند ؟


نه؛ برنامه های معروفی مثل همین یاهو مسنجر.

----------


## مهران رسا

> شما در هر حال باید در سمت سرور لیستی از کلاینت های متصل شده نگهداری  کنید.


بله . اما با روشی که عرض کردم نیازی به درگیر شدن با IP و پورت نیست . همون نگهداری Index کانکشن مربوط به کاربر لاگین شده کافی هست . همچنین در این روش عمر آنلاین بودن کاربران به اتصال کانکشن اولیه بستگی داره . یعنی کانکشنی که از زمان Login ایجاد میشه تا زمان Logout ، ضمن تعیین وضعیت آنلاین بودن ، تا پایان اتصال به عنوان یک بستر ارتباطی ، فعال باقی میمونه.




> نه؛ برنامه های معروفی مثل همین یاهو مسنجر.


جناب کشاورز از مشکلات فایروال و ... هم که چشم پوشی کنیم در روشی که  کلاینت ها باید Listen بمونن امنیت پایین هست . خرابکاران به سرور دسترسی   ندارند. در مقابل دسترسی به کلاینت ها و ایجاد تغییرات خرابکارانه کار سختی  نیست.




> یا مستقیما از WinSock استفاده کنید.


در دلفی چطوری میشه از Winsock استفاده کرد ؟ امکان داره منابع آموزشی در  این مورد معرفی کنید ؟
خیلی ممنون .

----------


## Felony

> در دلفی چطوری میشه از Winsock استفاده کرد ؟ امکان داره منابع آموزشی در این مورد معرفی کنید ؟


یک نمونه : http://www.coderprofile.com/networks...insock-example

----------


## vcldeveloper

> همچنین در این روش عمر آنلاین بودن کاربران به اتصال کانکشن اولیه بستگی  داره . یعنی کانکشنی که از زمان Login ایجاد میشه تا زمان Logout ، ضمن  تعیین وضعیت آنلاین بودن ، تا پایان اتصال به عنوان یک بستر ارتباطی ، فعال  باقی میمونه.


در Indy هم Connection شما تا زمانی که کلاینت Disconnect نکنه، همچنان باقی میمانه. حتی میشه در هنگام اتصال کلاینت، شی Thread اختصاص داده شده به آن در سمت سرور را گرفت، و هر بار لازم بود، از طریق آن با کلاینت مربوطه ارتباط برقرار کرد، اما در این صورت، داده ها در سمت کلاینت به IdTcpClient که ارتباط با سرور را برقرار کرده، ارسال میشند.




> در دلفی چطوری میشه از Winsock استفاده کرد ؟


به همون صورتی که در ++Visual C از توابع API ویندوز استفاده میشه؛ یونیت مربوطه را به لیست uses اضافه می کنید، و توابع مربوطه را فراخوانی می کنید. برای راهنمای درباره کار با این توابع هم می تونید به MSDN مراجعه کنید.

البته یک کتاب با عنوان The Tomes of Delphi: Basic 32-Bit Communications Programming از انتشارات Wordware هم وجود داره که به برنامه نویسی سوکت با استفاده از WinSock و TAPI در دلفی می پردازه.





> جناب کشاورز از مشکلات فایروال و ... هم که چشم پوشی کنیم در روشی که   کلاینت ها باید Listen بمونن امنیت پایین هست . خرابکاران به سرور دسترسی    ندارند. در مقابل دسترسی به کلاینت ها و ایجاد تغییرات خرابکارانه کار  سختی  نیست.


نه، از نظر امنیت تفاوتی ندارند. تصور نکنید Indy برای خودش یک سری کارهایی انجام میده، Indy هم در واقع در لایه های زیرین خودش از WinSock روی ویندوز برای تبادل داده استفاده میکنه.

----------


## مهران رسا

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



> اما در این صورت، داده ها در سمت کلاینت به IdTcpClient که ارتباط با سرور  را برقرار کرده، ارسال میشند.


در اونجا میشه رویدادی مثل DataArivall پیاده سازی کرد ؟



> ستفاده از Timer برای دریافت پیام های سرور در طولانی مدت میتونه قابل  اطمینان باشه ؟

----------


## vcldeveloper

> در اونجا میشه رویدادی مثل DataArivall پیاده سازی کرد ؟


خودش همچین چیزی نداره؛ باید کدهاش رو بررسی کرد، و دید که آیا میشه یک کلاس جدید از اون، یا از کلاس های والدش مشتق کرد که این امکان رو داشته باشه، یا نه. من بررسی نکردم.

----------


## مهران رسا

> حتی میشه در هنگام اتصال کلاینت، شی Thread اختصاص داده شده به آن در سمت  سرور را گرفت، و هر بار لازم بود، از طریق آن با کلاینت مربوطه ارتباط  برقرار کرد


اینکار چطور امکان پذیر هست ؟ 
راستش اصلاً حوصله درگیر شدن با پیچیدگی Winsock رو ندارم . اگه مورد بالا حل بشه در سمت کلاینت هم میشه ترفندهایی به کار برد.

در کل با توجه به مکانیزم Blocking ، میشه پروسه ارسال و دریافت Pm رو به گونه ای طراحی کرد که هر کاربر دارای یک صندوق پیام باشه و در یک بازه زمانی مشخص به صورت مداوم برای چک کردن صندوق ورودی خودش به سرور درخواست ارسال کنه و ادامه ماجرا...

----------


## vcldeveloper

> اینکار چطور امکان پذیر هست ؟


از طریق پارامتری که رویداد OnExecute سرور بهتون میده.




> راستش اصلاً حوصله درگیر شدن با پیچیدگی Winsock رو ندارم .


مجبور نیستید که مستقیما با WinSock کار کنید، می تونید از ICS استفاده کنید که Non-blocking هست، و رابط ساده تری هم نسبت به WinSock داره.

----------


## مهران رسا

> از طریق پارامتری که رویداد OnExecute سرور بهتون میده.


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

implementation

var
  Connections: array [1 .. 100000] of TIdContext;
  i: integer;

procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
begin
  i := i + 1;
  Connections[i] := AContext;
end;

آیا این راه صحیحه ؟

----------


## vcldeveloper

> آیا این راه صحیحه ؟


بله، این IdContext تا زمانی که کاربر Disconnect نشه، معتبر هست؛ البته لزومی نداره از یک آرایه استفاده کنید، بلکه بهتره از یک لیست Generic استفاده کنید:

interface
uses
  Forms, Generics.Collections;
    
type

    TForm1 = class(TForm)
        ...
    private
        Connections : TList<TIdContext>;
    public

    end;

implementation
    
procedure  TForm1.FormCreate(Sender: TObject);
begin
  Connections := TList<TIdContext>.Create;
end;

procedure  TForm1.FormDestroy(Sender: TObject);
begin
  Connections.Free;
end;

procedure  TForm1.IdTCPServer1Connect(AContext: TIdContext);
begin
  Connections.Add(AContext);
end;

----------


## مهران رسا

آقای کشاورز اگه بخوایم پروسه بالا رو برای عملیات Login شرح بدیم به این صورت خواهد بود :

کلاینت Username و Password را برای سرور ارسال میکند.سرور در رویداد Onexecute بعد از بررسی صحت نام کاربری و کلمه عبور و با توجه به AContext دریافت شده یک عضو به اعضای Connections اضافه میکند.در صورت اتصال موفقیت آمیز ، پیغام های بعدی که از سمت کلاینت ارسال میشوند نیازی به نام کاربری و کلمه عبور ندارند و  تشخیص Valid بودن کلاینت از طریق همان اعضای موجود در Connections صورت میگیرد .کلاینت یک پیغام متنی برای سرور ارسال میکند .سرور در رویداد OnExecute ـ ، AContext فعلی را با تمامی اعضای Connections بررسی کرده و در صورت یافتن آن در لیست ، اجازه میدهد ادامه عملیات انجام شود .
اما سوال اینجاست که آیا روش فوق بهترین راهی هست که میشه برای مدیریت Connection ها بکار برد ؟ ضمن اینکه ممکنه تعداد اتصالات بسیار زیاد باشن و با هر پیغامی که از طرف Client ها ارسال میشه باید برای مطمئن شدن از Valid بودن Connection جاری ، اون رو با کل Connection های ایجاد شده از قبل ، بررسی کنیم .

----------


## vcldeveloper

> اما سوال اینجاست که آیا روش فوق بهترین راهی هست که میشه برای مدیریت  Connection ها بکار برد ؟ ضمن اینکه ممکنه تعداد اتصالات بسیار زیاد باشن و  با هر پیغامی که از طرف Client ها ارسال میشه باید برای مطمئن شدن از  Valid بودن Connection جاری ، اون رو با کل Connection های ایجاد شده از  قبل ، بررسی کنیم .


در صورت پیاده سازی صحیح لیست، مشکلی بوجود نمیاد. مثلا سرور میتونه برای جستجوی سریع از یک Hash Table مناسب استفاده کنه. در ضمن، اینکه کاربر یک بار لاگین کنه، و بعدش هر پیامی خواست ارسال کنه، درست نیست؛ بلکه کاربر میتونه یک بار لاگین کنه، و سپس در صورت موفق شدن، یک session ID (به صورت Hash Code) دریافت کنه. این Session ID را باید کلاینت در هر بار تماس با سرور به سرور ارائه کنه، و سرور صحت آن را بررسی کنه، تا مطمئن بشه که کاربر ارسال کننده پیام، مجاز هست.

----------


## مهران رسا

> در صورت پیاده سازی صحیح لیست، مشکلی بوجود نمیاد. مثلا سرور میتونه برای  جستجوی سریع از یک Hash Table مناسب استفاده کنه. در ضمن، اینکه کاربر یک  بار لاگین کنه، و بعدش هر پیامی خواست ارسال کنه، درست نیست؛ بلکه کاربر  میتونه یک بار لاگین کنه، و سپس در صورت موفق شدن، یک session ID (به صورت  Hash Code) دریافت کنه. این Session ID را باید کلاینت در هر بار تماس با  سرور به سرور ارائه کنه، و سرور صحت آن را بررسی کنه، تا مطمئن بشه که  کاربر ارسال کننده پیام، مجاز هست.


بله درسته . با این تفاسیر ما علاوه بر لیست Connection ها باید برای هر کاربر لاگین شده یک Session ID هم ایجاد کنیم .

لیست کانکشن ها برای اینکه اگه نیاز شد سرور بطور دلخواه پیغامی برای کلاینت ارسال کنه بتونه از طریق کانکشن مربوطه اینکار رو انجام بدهSession ID برای بررسی صحت پیغام هایی که از سمت کلاینت به سرور ارسال میشند.
اما شرایط زیر رو در نظر بگیرید :
یک سرور با 5 هزار کاربر آنلاین که بین هر کاربر و سرور در هر ثانیه 10 پیغام رد و بدل میشه(اعم از پیغامها ، قطعات فایل و ...) . حالا اگه قرار باشه در هر بار جا به جایی پیغام ، Session ID مربوطه بررسی بشه نهایتاً در هر ثانیه باید 50 هزار Session ID توسط سرور بررسی بشه . و اینکار پردازش سرور رو خیلی بالا میبره .

ضمناً تا اونجایی که یادم هست در روش Non-Blocking دیگه نیاز به ایجاد چیزی مثل Session ID نبود و تنها از یک Index به عنوان مشخص کننده صحت کانکشن جاری استفاده میشد .

چه پیشنهاد میکنید جناب کشاورز ؟

----------


## vcldeveloper

> ضمناً تا اونجایی که یادم هست در روش Non-Blocking دیگه نیاز به ایجاد چیزی  مثل Session ID نبود و تنها از یک Index به عنوان مشخص کننده صحت کانکشن  جاری استفاده میشد .


متوجه ربط موضوع به Non-blocking یا Blocking نشدم.
درباره Session ID، اون مقدار میتونه هر چیزی باشه، مثلا یک عدد، یا یک Hash Code که بشه از طریق آن، به سرعت در Hash Table مربوطه، کانکشن مرتبط با آن را پیدا کرد.




> یک سرور با 5 هزار کاربر آنلاین که بین هر کاربر و سرور در هر ثانیه 10  پیغام رد و بدل میشه(اعم از پیغامها ، قطعات فایل و ...) . حالا اگه قرار  باشه در هر بار جا به جایی پیغام ، Session ID مربوطه بررسی بشه نهایتاً در  هر ثانیه باید 50 هزار Session ID توسط سرور بررسی بشه . و اینکار پردازش  سرور رو خیلی بالا میبره .


امنیت و Performance معمولا با هم نسبت عکس دارند. شما با توجه به نیازتون باید گزینه مناسب رو انتخاب کنید. ممکنه توی برنامه شما، فقط نیاز داشته باشید که در زمان Connect شدن کاربر، اطلاعات لاگینش را بررسی کنید، و تا زمانی که Disconnect نشده، چیزی رو چک نکنید. اون دیگه بستگی به کاربرد مورد نظر شما داره.

----------

