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

## bmanfy

سلام دوستان .
دارم يك برنامه FileSharing  با استفاده از indy  مينويسم. 
فقط يك مشكل دارم .
اون هم اينكه زماني كه قراره يك فايل ارسال بشه :
اگر فايل كوچك و معمولي باشه 2-3 مگابايت به راحتي ارسال ميشه.
اما اگر بيشتر باشه گيرنده ميبينه يك فايل بزرگ در حد گيگابايت دريافت كرده .
مثلا يك فايل 7 مگابايت ميشه چند گيگابايت . كه البته اين فايل هم درست كار نمي كنه .
اگر تكراريه سوالم ببخشيد . اخه هر چي گشتم چيزي پيدا نكردم .
ممنون از همگي

----------


## bmanfy

procedure TForm1.IdTCPServer1TIdCommandHandler4Command(
  ASender: TIdCommand);
var
  fileName : String;
  fStream : TFileStream;
begin
  fileName := ASender.Params[0];
  if not FileExists(fileName) then
     begin
         ASender.Thread.Connection.WriteLn('file not found');
     end
  else
     begin
         fStream := TFileStream.Create(fileName, fmOpenRead);
         ASender.Thread.Connection.WriteStream(fStream, True, True);
         fStream.Free;
     end;
end;


اين كدي هست كه براي اين كار نوشتم .
بعد ميشه براي يك سوكت سرور هم اون دستورات خواص گذاشت (مثلا اگه كلاينت سلام كرد سرور پاسخ بده عليك سلام) و از طرفي براي كاري مثل چت هم ازش استفاده كرد ؟
چون من اين كار رو انجام دادم نشد . يا چت يا دستوارت .

يا شايد هم بايد براي اين كار از دو سوكت محتلف استفاده بشه ؟؟؟؟؟؟

----------


## مهران رسا

برام سواله چرا همه از متد WriteStream استفاده می کنند ؟ آیا دلیل خاصی دارید یا فقط برای راحتی کار ازش استفاده میکنید ؟
در کارهای جدی تر باید خودتون مدریت رد و بدل شدن اطلاعات رو بر عهده بگیرید . مثلاً استریم ها رو به قطعات کوچکتر تقسیم بندی کنید و به صورت مدیریت شده ارسالشون کنید . یا حتی اگه نیاز باشه اطلاعات به صورت بایت بایت منتقل بشند . نظر شما چیه ؟

----------


## bmanfy

> برام سواله چرا همه از متد WriteStream استفاده می کنند ؟ آیا دلیل خاصی دارید یا فقط برای راحتی کار ازش استفاده میکنید ؟
> در کارهای جدی تر باید خودتون مدریت رد و بدل شدن اطلاعات رو بر عهده بگیرید . مثلاً استریم ها رو به قطعات کوچکتر تقسیم بندی کنید و به صورت مدیریت شده ارسالشون کنید . یا حتی اگه نیاز باشه اطلاعات به صورت بایت بایت منتقل بشند . نظر شما چیه ؟


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

----------


## مهران رسا

راستش من اون راه OverWrite کردن متد Write توی Indy 10 رو بلد نیستم . اما به عنوان یک روش ابتکاری میتونید از این ایده بگیرید :

   B: array [1 .. 256] of byte;

  File1 := TMemoryStream.Create;
  File1.LoadFromFile(Edit1.text);
  S := File1.Size;
  i := 0;

  //ProgressBar
  P.Max := S;

  while i <= S do
  begin
    File1.Position := i;

    File1.Read(B, 256);

    for j := 1 to 256 do
    begin
      D2Send := D2Send + '`' + inttostr(B[j]);
    end;

    IdTCPClient1.IOHandler.WriteLn(D2Send);
    D2Send := '';

    i := i + 256;
    P.Position := i;
  end;

  File1.free;که در این صورت هر بسته حاوی 512 بایت اطلاعات میشه . 255 تا واسه محتویات اون قسمتی از فایل که داخل آرایه ریختیم و 255 تا هم رشته های جداکننده برای میسر کردن عملیات جداسازی در سمت سرور (گیرنده فایل) . از سمت گیرنده هم باید اطلاعات دریافتی رو تا زمان رسیدن نشانه ی پایان  فایل دریافت کرده و بر اساس رشته جداکننده ، اونها رو Split کنید و همون  لحظه در فایل جدید بنویسید .

----------


## vcldeveloper

> راستش من اون راه OverWrite کردن متد Write توی Indy 10 رو بلد نیستم .


overwrite نه، overload. لازم نیست شما کار خاصی انجام بدید، منظور از overload شدن این هست که چند متد با نام یکسان، ولی پارامترهای مختلف در اون کلاس تعریف شدند. شما اگر به تعریف متد write رجوع کنید، می بینید که چندین متد write وجود دارند که هر کدام پارامترهای خاص خودشان را دریافت می کنند (مثلا یکی آرایه دریافت میکنه، یکی stream، یکی دیگه string، و غیره). اینکه از کدومشون استفاده میشه، بستگی به پارامتری داره که شما بهش میدید؛ اگر پارامتر شما stream باشه، اون متد writeایی که stream دریافت میکنه، اجرا میشه، اگر آرایه باشه، اونی که آرایه دریافت میکنه، اجرا میشه.

overload بودن یک متد را می تونید در زمان فراخوانی آن در IDE هم ببینید، وقتی میخواید برای متد write پارامترها را وارد کنید، IDE یک Hint چند خطی به شما نمایش میده، که در هر خط، پارامترهای مربوط به یکی از متدهای overload شده با نام write درج شده.

برای آشنایی با مفهوم method overloading در دلفی:
http://www.delphibasics.co.uk/RTL.asp?Name=Overload

----------


## مهران رسا

خیلی ممنون . پس در واقع میشه گفت overload مفهوم چند ریختی رو میرسونه ؟

----------


## vcldeveloper

> پس در واقع میشه گفت overload مفهوم چند ریختی رو میرسونه ؟


البته نه به اون معنایی که در شی گرایی مطرح شده، برای اون کار از method overriding برای متدهای virtual استفاده میشه.

----------


## مهران رسا

الآن داشتم روی پروسه ارسال فایل به روشی که ذکر شد فکر میکردم . زمانی که همزمان 2 نفر بخوان از طریق سرور با هم فایل ردوبدل کنند به نظر میاد کار یه خورده پیچیده بشه .

B: array [1 .. 256] of byte;

  File1 := TMemoryStream.Create;
  File1.LoadFromFile(Edit1.text);
  S := File1.Size;
  i := 0;

  //ProgressBar
  P.Max := S;

  while i <= S do
  begin
    File1.Position := i;

    File1.Read(B, 256);

    for j := 1 to 256 do
    begin
      D2Send := D2Send + '`' + inttostr(B[j]);
    end;

    IdTCPClient1.IOHandler.WriteLn(D2Send);
    D2Send := '';

    i := i + 256;
    P.Position := i;
  end;

  File1.free;
 
ببینید ما یک کنترل TCPServer در سمت سرور داریم که میخواد در رویداد OnExecute تمامی پیغام های دریافتی (اعم از پیغام های مدیریتی یا بسته های تکه تکه شده ی فایل) از سایر کلاینت ها رو پردازش و مدیریت کنه . از طرفی ارسال فایل از طریق پروتکل TCP یعنی ارسال رشته های قطعه قطعه شده و پی در پی . خب با روشی که ذکر کردم فایل به قطعات 256 بایتی تقسیم شده و به سمت سرور ارسال میشه . روی قسمت سرور زیاد دقت نکرده بودم . عمده کاری که باید انجام بشه همینجاست . در واقع اینکه چطور این بایت ها دریافت بشند خیلی مهمه .

نظریه ی من در مورد نوشتن کد قسمت سرور برای دریافت فایل به صورت Multi User این هست :

کلاینت قبل از ارسال فایل یک نشانه برای سرور ارسال کنه .سرور در اسارت بلاک Try باید یک فایل با نامی که از طریق "نشانه" دریافت شده ، ایجاد کنه .تا زمانی که پیغام "اتمام عملیات" از سمت کلاینت دریافت نشده ، سرور به Write کردن فایل ادامه بده .وقتی پیغام "اتمام عملیات" دریافت شد . فایل بسته بشه .
نکته ای که در موراد بالا وجود داره اینه که استفاده از بلاک Try - Except برای ادامه روند برنامه ضروری هست . به این دلیل که شما فرض کنید در زمانی که سرور مشغول دریافت محتویات فایل و نوشتن اونهاست ممکنه یک پیغام متفرقه به رویداد Onexecute برسه . در این صورت در حالی که هنوز فایلی ایجاد نشده (چون فایل جدید فقط زمانی ایجاد و آماده ی نوشتن میشه که پیغام مخصوص "ایجاد فایل" از سمت کلاینت رسیده باشه) فرمان Write فراخوانی میشه و به یک Exception بر میخوریم(چون Write در فایلی میخواد اتفاق بیفته که هنوز ساخته نشده) . این Exception به ما میگه که فرمان یا بسته ی دریافت شده قرار نیست در فایل نوشته بشه و جز فرمان های متفرقه محسوب میشه .

شاید این روش زیاد جالب به نظر نرسه اما تنها چیزی بود که برپایه ی Blocking-mode به ذهنم خورد . ضمن اینکه این روش در زمانی که فایل بدون ذخیره سازی بخواد بواسطه سرور از کلاینتی به کلاینت دیگه ارسال بشه عملاً غیر قابل پیاده سازی هست .

اگه بخوام واضح تر بگم ، Indy یک رویداد OnExecute در اختیار ما قرار میده و گفته خیالتون از بابت مدیریت Thread ها راحت باشه . در اینجا مدیریت پیغام هایی که حالت پرسش/پاسخ دارند (یعنی بعد از دریافت هر پیغام در همان رویداد OnExecute پاسخی ارائه میشه) آسون هست و بدون هیچ مشکلی پیاده سازی میشه . اما در زمان دریافت فایل ، دیگه قضیه فرق میکنه . چون این فایل ممکنه 1000 بسته رو تشکیل بده که همگی توسط ReadLn دریافت میشند. تا اینجا ، تا زمانیکه تبادل فقط بین 1 فرستنده(کلاینت) و گیرنده(سرور) صورت میگیره مشکلی پیش نمیاد. مشکل از اونجایی شروع میشه که در حالی که یک فایل 100 بسته ای در حال دریافت شدن هست و دقیقاً در زمانی که 60 تا از این بسته ها در رویداد OnExecute دریافت شدند ، یک کلاینت جدید برای ارسال یک فایل مثلاً 100 بسته ای دیگه اقدام میکنه . 

دیگه اینجا Indy نمیدونه کدام بسته مربوط به کدام فایل هست !

چه راه حلی پیشنهاد میکنید ؟

----------


## vcldeveloper

> دیگه اینجا Indy نمیدونه کدام بسته مربوط به کدام فایل هست !


Indy درخواست های هر کلاینت را در Thread مخصوص اتصال همان کلاینت پردازش میکنه، پس اگر در هنگام دریافت فایل از یک کلاینت، کلاینت دیگه ایی اقدام به ارسال فایل کنه، متد OnExecute برای آن کلاینت در Thread همان کلاینت فراخوانی میشه، و تداخلی با دریافت فایل از کلاینت اول نداره.

مشکل شما این هست که Multi-thread بودن کد نوشته شده برای OnExecute را در نظر نگرفتید. ممکنه در یک لحظه از زمان، چندین بار کد مربوط به OnExecute در Threadهای مختلف در حال اجرا باشه. کد شما فرض کرده که برای هر اتصال، همون کد OnExecute اجرا میشه، برای همین هم فرضا رفتید یک ProgressBar گذاشتید که میزان دریافت فایل را نمایش بده. حالا اگر یک کلاینت اقدام به ارسال فایل کنه، و مثلا 50 درصد از فایل ارسال بشه، و در این لحظه یک کلاینت دیگه شروع به ارسال فایل کنه، مقدار ProgressBar شما صفر میشه. حالا هر وقت یک بسته از هر یک از کلاینت ها برسه، مقدار ProgressBar به شکل عجیبی تغییر میکنه، مثلا یک بار میشه 55، یک بار میشه 10، یک بار میشه 70، یک بار میشه 40، و غیره؛ چون Threadهای مختلف دارند سعی می کنند مقدار ProgressBar را تغییر بدند. البته این در شرایطی هست که به خاطر تغییر رابط کاربر در Threadهای مختلف، برنامه تون دچار مشکل نشه!



> نکته ای که در موراد بالا وجود داره اینه که استفاده از بلاک Try - Except  برای ادامه روند برنامه ضروری هست . به این دلیل که شما فرض کنید در زمانی  که سرور مشغول دریافت محتویات فایل و نوشتن اونهاست ممکنه یک پیغام متفرقه  به رویداد Onexecute برسه . در این صورت در حالی که هنوز فایلی ایجاد نشده  (چون فایل جدید فقط زمانی ایجاد و آماده ی نوشتن میشه که پیغام مخصوص  "ایجاد فایل" از سمت کلاینت رسیده باشه) فرمان Write فراخوانی میشه و به یک  Exception بر میخوریم(چون Write در فایلی میخواد اتفاق بیفته که هنوز  ساخته نشده) . این Exception به ما میگه که فرمان یا بسته ی دریافت شده  قرار نیست در فایل نوشته بشه و جز فرمان های متفرقه محسوب میشه .


شما وقتی دستورات مختلف از کلاینت دارید، که باید جداگانه پردازش بشند، باید از IdCmdTcpServer استفاده کنید، و برای هر دستور یک Command handler جداگانه بنویسید.

----------


## مهران رسا

از توجهتون ممنونم.
آقای کشاورز حق با شماست . Indy در هر صورت همه پیغام ها رو بدون تداخل دریافت میکنه . اما مشکل سر مدیریت بهینه این پیغام هاست . مثلاً در حالتی میشه برای هر پیغام (اعم از "مدیریتی" یا "تکه های فایل") یک نشانه وجود داشته باشه . یعنی هر پیغامی که دریافت میشه دارای شماره شناسایی باشه . حالا اگه 100 نفر هم همزان در حال ارسال فایل باشند ، Indy از روی شماره شناسایی میفهمه که کدوم بسته رو در کدوم فایل ذخیره کنه . اما مشکلش پردازش بالای کارهست . پس بهینه نیست . نهایتاً بنابر گفته ی شما دارم در مورد  IdCmdTcpServer تحقیق میکنم .

بازهم ممنون .

----------


## مهران رسا

آقای کشاورز به نظر میرسه IdCmdTcpServer برای مواردی که جواب ثابتی برای کلاینت قرار هست ارسال بشه استفاده میشه ! از راهنمای خود Indy هم چیز زیادی نفهمدیم .
تنها چیزی که کشف کردم : :لبخند گشاده!:  


> کنترل TIdCmdTCPServer جزء یکی از نوادگان TIdTCPServer محسوب میشود که پیاده سازی یک سرور Multi-thread بر پایه پروتکل TCP-IP با پشتیبانی از دستورات گرداننده را فراهم میسازد .دستورات گرداننده(Command handlers) مکانیزم توسعه پذیری را فراهم میکنند که از آنها برای تعریف کردن دستوراتی به عنوان پروتکل ارتباطی میان کلاینت و سرور استفاده میشود .


این هم یک مثال که برای Indy های ورژن پایین تر هست و همچنین رویدادی در TIdCmdTCPServer  نسخه 10 وجود نداره :

procedure TformMain.IdCmdTCPServer1cmdhLookupCommand(ASender  : TIdCommand);
var
  i: integer;
  LCity: string;
  LPostCode: string;
begin
  LPostCode := '';
  if ASender.Params.Count > 0 then begin
    for i := 0 to ASender.Params.Count - 1 do begin
      LPostCode := ASender.Params[i];
      LCity := FZipCodeList.Values[LPostCode];
      if LCity <> '' then begin
        Inc(TUserData(ASender.Context.Data).FRequestCount)  ;
        ASender.Response.Add(LPostCode + ': ' + LCity);
      end;
    end;
  end;
end;

----------


## مهران رسا

فکر نمیکنید استفاده از TIdCmdTCPServer چندان تفاوتی با استفاده از TCPServer نداره ؟ چون به هر حال ما بازم با یک رویداد سرو کار داریم . اگه TCPServer رویداد OnExecute داره که تمامی پیغام های دریافتی در اونجا بررسی میشند TIdCmdTCPServer هم یک رویداد برای اینکار داره . و بازهم همون قضیه تداخل پیش میاد . البته منظور از تداخل ، اون چیزی نیست که پیغام ها درست دریافت نشند بلکه در این روش هم پیغام ها دریافت میشن ولی نمیدونیم فلان تکه از فایل رو در کجا ذخیره کنیم .

اگر در این مورد دچار اشتباه شدم لطفاً راهنمایی بفرمایید.
ممنون.

----------


## vcldeveloper

> به نظر میرسه IdCmdTcpServer برای مواردی که جواب ثابتی برای کلاینت قرار  هست ارسال بشه استفاده میشه ! از راهنمای خود Indy هم چیز زیادی نفهمدیم .





> فکر نمیکنید استفاده از TIdCmdTCPServer چندان تفاوتی با استفاده از  TCPServer نداره ؟ چون به هر حال ما بازم با یک رویداد سرو کار داریم .


در IdCmdTcpServer، شما برای امور مختلف در سرور دستوراتی تعریف می کنید، مثلا فرض کنیم شما سه دستور زیر را تعریف می کنید:
UploadFile
SendMsg
GetTime

هر کدوم از این دستورات می تونند پارامترهایی هم داشته باشند. در سمت سرور شما برای هر سرور، یک Command-handler می نویسید، که وظیفه اش دریافت و پردازش آن دستور خاص هست. حالا کلاینت های شما می تونند با این دستورات با سرور ارتباط برقرار کنند. مثلا یک کلاینت اگر یک دستورUploadFile ارسال کنه، این دستور توسط Command-handler مربوط به UploadFile در سرور پردازش میشه. اگر همون کلاینت دستور GetTime را ارسال کنه، درخواستش توسط Command-handler مربوط به GetTime پردازش میشه. پس پردازش هر نوع درخواستی، در روتین مربوط به همان درخواست انجام میشه. 

در واقع Indy محتوای هر بسته دریافتی را بررسی میکنه، و اگر اون بسته با هر یک از دستورات تعریف شده توسط شما مطابقت داشته باشه، آن را به Command-handler مربوط به آن ارسال میکنه.

اگر IdCmdTcpServer نبود، شما باید همین کار را در داخل کدهای مربوط به رویداد OnExecute انجام می دادید، یعنی هر بسته ایی که می رسید، محتوای آن را بررسی می کردید، و با توجه به نوع محتوای بسته، پردازش مناسب را انجام می دادید، اما IdCmdTcpServer این کار رو براتون راحت کرده.

----------


## مهران رسا

> مثلا یک کلاینت اگر یک دستورUploadFile ارسال کنه، این دستور توسط  Command-handler مربوط به UploadFile در سرور پردازش میشه


یعنی ارسال دستور UploadFile ، سوای دستورات معمولی که با WriteLn ارسال میشند هست ؟ 

اگه اینطور نباشه بازهم تفاوتی نمیکنه که دستور رو OnExecute پردازش کنه یا Command handler چون وقتی میخوایم فایل ارسال کنیم اول یک نشانه (مثلاً اسم فایل) رو میفرستیم به سرور و وقتی سرور OK رو داد دیگه نشانه ای در پکت ها ارسال نمیشه و فقط بسته های تکه تکه شده فایل رو دریافت میکنه که هیچ نشانه ای ندارند . 

اگه امکان داره ممنون میشم پروسه دریافت فایل در سمت سرور با استفاده از IdCmdTcpServer رو توضیح بدید . و یک سوال دیگه اینکه آیا IdCmdTcpServer باید در کنار TCPServer استفاده بشه ؟

----------


## vcldeveloper

> اول یک نشانه (مثلاً اسم فایل) رو میفرستیم به سرور و وقتی سرور OK رو داد  دیگه نشانه ای در پکت ها ارسال نمیشه و فقط بسته های تکه تکه شده فایل رو  دریافت میکنه که هیچ نشانه ای ندارند .


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




> آیا IdCmdTcpServer باید در کنار TCPServer استفاده بشه ؟


نه، خودش یک سرور کامل هست.

----------


## مهران رسا

> نیازی نیست که حتما این کار رو بکنید، می تونید فایل رو در بسته های  جداگانه ارسال کنید، و هر بسته خودش ارسال یک دستور باشه، مثلا دستوری با  نام FilePart که یکی از پارامترهای آن هم شماره قطعه مربوطه از فایل باشه.


با این اوصاف پس مهم نیست که از IdCmdTcpServer استفاده بشه یا از TCPServer ؛ چون نهایتاً مدیریت قطعات فایل رو خودمون باید بر عهده بگیریم و در واقع IdCmdTcpServer به خودی خود کار خاصی برای ما انجام نمیده .
در نتیجه :



> یعنی هر پیغامی که دریافت میشه دارای شماره شناسایی باشه . حالا اگه 100  نفر هم همزان در حال ارسال فایل باشند ، Indy از روی شماره شناسایی میفهمه  که کدوم بسته رو در کدوم فایل ذخیره کنه . _اما مشکلش پردازش بالای کارهست_


درسته ؟

----------


## bmanfy

ممنون از همگي .
خيلي جالب بود و لذت بردم .
برنامه اي كه من قصد دارم بنويسم كلاينت ها از سرور فايل دريافت ميكنند . و سرور تنها فايل رو ارسال ميكنه.
از همين روشي كه بيان شد استفاده كنم ؟
نميشه يك ارايه از بايت رو ارسال كرد؟
حتما بايد از جدا كننده استفاده بشه ؟ اخه اين جوري دقيقا حجم فايل ارسالي دو برابر خواهد شده . :متفکر:  
و حتي بيشتر اخه باز داره تبديل ميشه با كاركتر .
درسته ؟
ميخوام يك برنامه كاملا بهينه بنويسم .

----------


## مهران رسا

> ممنون از همگي .
> خيلي جالب بود و لذت بردم .
> حتما بايد از جدا كننده استفاده بشه ؟ اخه اين جوري دقيقا حجم فايل ارسالي دو برابر خواهد شده . 
> و حتي بيشتر اخه باز داره تبديل ميشه با كاركتر .
> درسته ؟
> ميخوام يك برنامه كاملا بهينه بنويسم .


با استفاده از روشی که ذکر شد :

for j := 1 to 256 do
begin
D2Send := D2Send + '`' + inttostr(B[j]);
end;

میشه عملیات فشرده سازی رو  هم بر روی قطعات فایل انجام داد . به عنوان مثال فرض کنید یک Part از فایل در این قالب برای ارسال آماده شده :
69`12`32`0`0`0`0`0`0`0`0`0`0`65به دلیل اینکه در فایل های باینری از اسکی (0) زیاد استفاده میشه به عنوان مثال میتونیم تعداد 10 تا اسکی (0) که با استفاده از جدا کننده ها معادل 19 بایت میشه رو در 4 بایت خلاصه کنیم :
Before Compression : 0`0`0`0`0`0`0`0`0`0After Compression : 0!10البته این روش همیشه بهینه نیست و قطعاً روش های بهتری باید وجود داشته باشه . من در این مورد زیاد مطمئن نیستم اما یک روش دیگه اینه که کد بالا رو به این صورت بنویسیم :
for j := 1 to 256 do
begin
     D2Send := D2Send + chr(inttostr(B[j]));
end;


در اینجا دیگه جداکننده ای در کار نیست و کاراکترهای (0 تا 255) جدول اسکی با امید اینکه در سمت کلاینت راهی برای نوشتن صحیح اونها در فایل باینری وجود داشته باشه ، به صورت رشته ای ارسال میشند . در کل از نظر من یا حجم بیشتر باید پرداش کمتر رو توجیه کنه یا پردازش  بیشتر حجم کمتر رو .





> نميشه يك ارايه از بايت رو ارسال كرد؟


اگه اشتباه نکنم با WriteLn فقط رشته میشه ارسال کرد .





> برنامه اي كه من قصد دارم بنويسم كلاينت ها از سرور فايل دريافت ميكنند . و  سرور تنها فايل رو ارسال ميكنه.


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

اگه کدهای مربوط به کلاینت سروری که نوشتید رو اینجا قرار بدید بهتر میشه نظر داد .

----------


## vcldeveloper

> با این اوصاف پس مهم نیست که از IdCmdTcpServer استفاده بشه یا از  TCPServer ؛ چون نهایتاً مدیریت قطعات فایل رو خودمون باید بر عهده بگیریم و  در واقع IdCmdTcpServer به خودی خود کار خاصی برای ما انجام نمیده .


IdCmdTcpserver یک Command Server هست، وظیفه اش هم دریافت دستورات از کلاینت ها، پردازش آنها، و تحویل هر دستور خاص، به Command-handler مربوط به آن، برای پردازش، و ارسال جواب هست. کاری نداره که شما دارید براش فایل می فرستید، یا یک متن ساده، یا یک داده باینری. شما اگر فایل را به قطعات کوچک تقسیم می کنید، در سمت سرور، وظیفه سر هم کردن قطعات فایل هم با خودِ شما ست. IdCmdTcpServer فقط کار شما در دریافت و پردازش دستورات ساده میکنه، مثلا می تونید بهش بگید که من یک دستور با نام SendFile دارم، که دو پارامتر داره، یکی شماره قطعه فایل، یکی هم محتوای آن قطعه. می تونید در سمت سرور یک Command-handler برای این SendFile بنویسید. هر وقت که کلاینتی درخواستی حاوی دستور SendFile بفرسته، سرور شما Command-handler شما را اجرا میکنه، و پارامترهای دریافتی را هم در اختیارش قرار میده. حالا command-handler شما هر کاری میخواد با این پارامترها انجام بده، ربطی به IdCmdTcpServer نداره. شما ممکنه command-handlerهای مختلفی برای کارهای مختلف در یک سرور داشته باشید، مثلا یکی برای login، یکی برای شروع ارسال فایل، یکی برای لغو ارسال فایل، یکی برای خاتمه ارسال فایل، یکی برای دریافت لیستی از فایل ها، یکی برای Log-out، و غیره. مثل یک FTP Server که برای امور مختلف، دستورات مختلفی ارائه میکنه، و شما از آنها استفاده می کنید.

البته اینجا این رو هم بگم، که اگر کار اصلی سرور شما ارسال و دریافت فایل هست، بهتره به جای اختراع مجدد چرخ، و پیاده سازی همه چیز با استفاده از TcpServer، از  IdFtpServer استفاده کنید، و با آن یک سرور FTP بسازید که اساسا کارش همین هست.

----------


## مهران رسا

خیلی ممنون . آقای کشاورز فکر میکنید استفاده از IdCmdTcpserver ، جز ترو تمیز تر شدن کار مزیت دیگه نسبت به TCPServer داره ؟ مثلاً سرعت بیشتر ؟

----------


## vcldeveloper

> میکنید استفاده از IdCmdTcpserver ، جز ترو تمیز تر شدن کار مزیت دیگه نسبت  به TCPServer داره ؟ مثلاً سرعت بیشتر ؟


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

----------


## مهران رسا

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

سرور :


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
  Forms,
  Dialogs, IdContext, IdBaseComponent, IdComponent, IdCustomTCPServer,
  IdTCPServer, StdCtrls, StrUtils, IdScheduler, IdSchedulerOfThread,
  IdSchedulerOfThreadPool;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Label1: TLabel;
    Label2: TLabel;
    TCPServer: TIdTCPServer;
    IdSchedulerOfThreadPool1: TIdSchedulerOfThreadPool;
    procedure TCPServerExecute(AContext: TIdContext);
    procedure FormShow(Sender: TObject);
  private

    function GetSign(Str: string; Encode: boolean): string;

    procedure Split(Delimiter: string; Input: string;
      Strings: TStringList);

    function InStr(Start: integer; MainStr, SubStr: string): integer;

  public
    {Public declarations}
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

var
  Fs: array of TFileStream;
  Ms: array of TMemoryStream;

  ClientIP, FileName: string;
  SomeBytes: array of byte;
  Ix: integer;

procedure TForm1.TCPServerExecute(AContext: TIdContext);
Var
  CMd, Tok: string;
  RecSign, SenSign, SignStr, FileSign, IpSign: string;
  FilePart: string;
  Det, AChar: string;
  AByte: byte;
  IPos, JPos, KPos, i, jj: integer;
  TsL: TStringList;
begin

  Det := chr(0);
  SignStr := 'sign:';

  CMd := AContext.Connection.IOHandler.ReadLn();
  ClientIP := AContext.Connection.Socket.Binding.PeerIP;

  if copy(LowerCase(CMd), 1, 4) = 'file' then
  begin

    Tok := copy(CMd, 5, 3);

    //
    //{ ******* Create new files **********}
    //

    if Tok = (Det + ':' + Det) then
    begin
      Ix := Ix + 1;
      SetLength(Fs, Ix);
      SetLength(Ms, Ix);

      FileName := copy(CMd, 8, length(CMd));
      //Create new FileStream and MemoryStream
      Fs[Ix - 1] := TFileStream.Create(FileName, fmCreate);
      Ms[Ix - 1] := TMemoryStream.Create;

      //generate a new sign for current transfer
      SenSign := GetSign(FileName + '*' + ClientIP, true);
      AContext.Connection.IOHandler.WriteLn(SignStr + SenSign);
    end;

    //
    //{ ******* Save the any part of any file **********}
    //

    if Tok = (Det + '#' + Det) then
    begin
      IPos := PosEx(SignStr, CMd, 1) + length(SignStr);
      JPos := PosEx(Det + '=' + Det, CMd, IPos + 1);

      //Get file sign
      RecSign := copy(CMd, IPos, JPos - IPos);
      RecSign := GetSign(RecSign, false);
      KPos := pos('*', RecSign);
      //Split the RecSign to find the FileSign and IpSign
      FileSign := copy(RecSign, 1, KPos - 1);
      IpSign := copy(RecSign, KPos + 1, length(RecSign));

      if (IpSign = ClientIP) then
      begin

        try
          TsL := TStringList.Create;

          for i := low(Fs) to high(Fs) do
          begin
            if Fs[i].FileName = FileSign then
            begin

              //Get any part of file
              FilePart := copy(CMd, JPos + 3, length(CMd));
              Split('`', FilePart, TsL);
              //Write the file as bytes
              for jj := 0 to TsL.Count - 1 do
              begin
                AChar := TsL[jj];
                AByte := strtoint(AChar);
                Ms[i].Write(AByte, 1);
              end;

            end;
          end;

        finally
          TsL.Free;
        end;

      end;

    end;

  end;

  //
  //{ ******* Write the compeleted files **********}
  //

  if Tok = (Det + '!' + Det) then
  begin
    IPos := PosEx(SignStr, CMd, 1) + length(SignStr);
    JPos := PosEx(Det + '=' + Det, CMd, IPos + 1);

    //Get file sign
    RecSign := copy(CMd, IPos, JPos - IPos);
    RecSign := GetSign(RecSign, false);
    KPos := pos('*', RecSign);
    //Split the RecSign to find the FileSign and IpSign
    FileSign := copy(RecSign, 1, KPos - 1);
    IpSign := copy(RecSign, KPos + 1, length(RecSign));

    if (IpSign = ClientIP) then
    begin
      for i := low(Fs) to high(Fs) do
      begin
        if Fs[i].FileName = FileSign then
        begin

          Fs[i].Free;
          Ms[i].SaveToFile(FileSign);
          Ms[i].Free;
          Ix := Ix - 1;

        end;
      end;
    end;

  end;

end;

end;

procedure TForm1.FormShow(Sender: TObject);
begin
  TCPServer.Active := true;
end;

function TForm1.GetSign(Str: string; Encode: boolean): string;
var
  i, j: integer;
  Ch, OutP: string;
begin

  if Encode = true then
  begin
    j := 5
  end
  else
  begin
    j := -5
  end;

  for i := 1 to length(Str) do
  begin
    Ch := copy(Str, i, 1);
    OutP := OutP + chr(ord(Ch[1]) + j);
  end;
  GetSign := OutP;

end;

//------------------

procedure TForm1.Split(Delimiter: string; Input: string;
  Strings: TStringList);
var
  i, x: integer;
  Item: string;
begin

i := 1;

  while i <= length(Input) do
  begin
    x := InStr(i, Input, Delimiter) - 1;
    if x <> 0 then
    begin
      Item := copy(Input, i, x);
      Strings.Add(Item);
      i := i + (length(Delimiter) + length(Item));
    end
    else
    begin
      i := i + 1;
    end;
  end;

end;

//--------------
function TForm1.InStr(Start: integer; MainStr, SubStr: string): integer;
var
  StrTmp: string;
begin
  StrTmp := copy(MainStr, Start, length(MainStr));
  InStr := pos(SubStr, StrTmp);
end;

end.

کلاینت :
 
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
  Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  StdCtrls;

type
  TForm1 = class(TForm)
    TCPClient: TIdTCPClient;
    Button1: TButton;
    Edit1: TEdit;
    procedure TCPClientConnected(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    {Private declarations}
  public
    {Public declarations}
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  i, j, S: integer;
  Cmd, D2Send: string;
  File1: TMemoryStream;
  B: array [1 .. 512] of byte;

begin
  //Begin Transmit signal
  TCPClient.IOHandler.WriteLn('file' + chr(0) + ':' + chr(0) + Edit1.text);

  //Get a new unique sign for current transfer
  Cmd := TCPClient.IOHandler.ReadLn();

  if copy(Cmd, 1, 5) = 'sign:' then
  begin

    //Send a File
    File1 := TMemoryStream.Create;
    File1.LoadFromFile(Edit1.text);
    S := File1.Size;

    while i <= S do
    begin
      File1.Position := i;
      File1.Read(B, 512);
      for j := low(B) to high(B) do
      begin
        D2Send := D2Send + '`' + inttostr(B[j]);
      end;

      sleep(1);

      //Send Parts
      TCPClient.IOHandler.WriteLn('file' + (chr(0) + '#' + chr(0))
          + 'sign:' + copy(Cmd, 6, length(Cmd)) + chr(0) + '=' + chr(0)
          + D2Send + '`');

      D2Send := '';
      i := i + 512;

    end;

  end;

  //End Transmit signal
  TCPClient.IOHandler.WriteLn('file' + (chr(0) + '!' + chr(0))
      + 'sign:' + copy(Cmd, 6, length(Cmd)) + chr(0) + '=' + chr(0) + Cmd);
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  TCPClient.Connect;
end;

procedure TForm1.TCPClientConnected(Sender: TObject);
begin
  Button1.Enabled := true;
end;

end.*توضیحات :*
برای ارسال فایل از طریق شبکه و با استفاده از پروتکل TCP ، لازم هست تا ابتدا فایل به قطعات کوچک تر تقسیم بندی شده و سپس ارسال بشه . پیاده سازی این روش تا زمانی که ارسال فایل تنها بین یک سرور و یک مشتری صورت میگیره ساده هست . اما در روش دریافت فایل به صورت چند کاربره ، کار سرور کمی پیچیده میشه ضمن اینکه با توجه به ماهیت برنامه نویسی سوکت ، هیچ بسته ای بدون مشخصات و اصل و نسب برای ما قابل قبول نیست . برای همین قطعات فایل رو قبل از ارسال نشانه گذاری میکنیم تا در سمت سرور و در حالی که ممکنه هزاران قطعه فایل در حال دریافت شدن باشه ، بدونیم کدوم قطعه مربوط به کدوم فایل و کدوم فرستنده هست . من برای حل این مشکل از یک روش نشانه/امضا گذاری استفاده کردم که با استفاده از اون همه کلاینت ها موظف اند قبل از ارسال فایل ، یکبار اطلاعات مربوط به فایل(نام فایل) رو به سرور ارسال کرده و پس از دریافت "نشانه" ای از جانب سرور ، قطعات فایل رو با پیشوند گذاری و هویت دادن توسط اون نشانه ارسال کنند .
اما در سمت سرور چه اتفاقی میفته !؟ پیغام اولیه برای درخواست نشانه از سرور رو A و پارت های نشانه گذاری شده فایل رو Bx در نظر بگیرید . 

زمانی که پیغام A به سرور رسید، سرور کاربر جدیدی که قصد ارسال فایل داره رو در لیست(آرایه) قرار داده و نشانه مربوطه  رو براش ارسال میکنه . کلاینت پس از دریافت نشانه شروع به ارسال Bx میکنه (هر پارت نشانه گذاری و ارسال میشه)سرور پیغام هایی که حاوی نشانه هستند رو در قسمتی دیگر پردازش میکنه . اطلاعاتی که از نشانه استخراج میشند میتونند شامل هر سرنخ مشخص کننده ای از  هویت کلاینت ها باشند . (که در کد بالا ما از IP کلاینت و نام فایل ارسالی به  عنوان نشانه استفاده میکنیم)با توجه به آرایه ای که در هر بار درخواست کاربران برای ارسال فایل ابعادش بروزرسانی میشه ، در قسمت دریافت Bx تشخیص میدیم که کدوم پارت رو در کدوم فایل (Fs , Ms[]) ذخیره سازی کنیم .و نهایتاً بعد از اتمام عملیات دریافت فایل ، ضمن Free کردن Object های مربوط به اون Transfer ، ابعاد آرایه Global رو هم به نسبت کاهش میدیم .

و اما اینجا مشکلی به وجود میاد که در ادامه عرض خواهم کرد ...


_ ویرایش 1 : برطرف کردن مشکل تابع Split ._
_ویرایش 2 : اضافه کردن توضیحات_ .

----------


## مهران رسا

_در مورد نظریه حجیم کردن پکت ها برای بالا بردن سرعت هنوز مطمئن نیستم_ . اگر کسی اطلاعاتی در مورد "حداکثر اطلاعاتی که هربار میشه توسط WriteLn نوشت" داره لطفاً راهنمایی کنه . 

چند مورد باید در کد بالا اصلاح بشند :

در سمت سرور قطعات دریافتی فایل در Memory ذخیره میشند و نهایتاً در فایل نوشته میشند . این روش در کارهای بزرگتر و در مواقعی که حافظه محدوده خطرناک هست و باید طوری اصلاح بشه که مثلاً بعد از دریافت هر 20 پارت ، یک بار حافظه اختصاص یافته پس داده بشه و 20 پارت جدید در فایل نوشته بشه . البته سروکله زدن با دیسک هم خودش زمان بر هست . درنتیجه در مواردی که سرعت در اولویت قرار داره به جای 20 میتونیم از عدد بزرگتری استفاده کنیم .هنوز روش ارسال پارت ها بهینه نیست . با این روش حجم هر فایل 2 برابر میشه . دارم دنبال راه بهتری میگردم . در هر صورت خوشحال میشم از ایده بقیه دوستان هم مطلع بشم .

----------


## vcldeveloper

> _در مورد نظریه حجیم کردن پکت ها برای بالا بردن سرعت هنوز مطمئن نیستم_


حجمشون رو زیاد کنید که چی بشه؟! میشه بسته های TCP را با اندازه مشخص فرستاد، و اگر اندازه بسته ها با مقدر MTU شبکه مطابقت نداشته باشه، کارایی کاهش پیدا میکنه، ولی اینها در لایه های پایین هست، و شما با این مسائل خودتان درگیر نیستید.




> اگر کسی اطلاعاتی در مورد "حداکثر اطلاعاتی که هربار میشه توسط WriteLn  نوشت" داره لطفاً راهنمایی کنه .


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

دقت کنید که متدهای Write مربوط به Indy خودش داده های بزرگ را Chunk میکنه (به طور پیش فرض 32 کیلوبایت، ولی می تونید مقدارش را تغییر بدید)، و اینطور نیست که یک حجم بزرگ از فایل را همینطوری بفرسته، بلکه داده های بزرگ خرد میشند، و قطعه قطعه ارسال میشند.

اگر کلاینت شما نیازی نداشته باشه که در هنگام ارسال فایل برای سرور، داده های دیگه ایی هم وسط کار، به سرور ارسال کنه، اون وقت دیگه برای ارسال فایل نیاز به این کدها نبود، و به راحتی متد WriteFile از خصوصیت IOHandler مربوط به ClientDataset، می تونست یک فایل را خودش به بخش های کوچک تقسیم و ارسال کنه، مثلا:

    try
      FClient.Connect;
      FClient.IOHandler.LargeStream := True;
      FClient.IOHandler.WriteLn(cmdFile);
      FClient.IOHandler.WriteLn(ExtractFileName(FFileNam  e));
      FClient.IOHandler.WriteLn(IntToStr(FileSize));
      FClient.IOHandler.WriteLn(FileHash);
      FClient.IOHandler.WriteFile(FFileName);
    finally
      FClient.Disconnect;
    end;

----------


## bmanfy

با سلام. 
ممنون از پاسخ هاتون.
هنوز مطالبي جديدي كه نوشتيد رو مطالعه نكردم . به هر حال وقت امتحانات هست و هزار گرافتاري .

فكر كنم يه توضيح در باره اين پروژه بدم بد نيست.
هدف نوشتن يك پروژه با استفاده از سوكت هست كه در اون برنامه بر روي كامپيوتر ها ي مختلف نصب ميشه و هر شخص ميتونه فايل هاشو بر روي همان كامپيوتر به اشتراك بگذاره . و مشخص كنه چه كسايي اين فايل ها رو بتونن بردارن و چه كسايي نتونن و ....
به طور كل هر كامپيوتر هم سرور هستش(در زماني كه كسي ميخواد فايل ها شو برداره) و هم كلاينت (هر وقت كه ميخواد فايل ها رو از يك كامپيوتر ديگه دريافت كنه)
عملا فايل ها در يك محل جمع نميشن و نقل و انتقال فابلها هم زماني ميسر ميشه كه اون كسي هم كه فايلي رو به اشتراك گذاشته برنامش باز باشه .


در انتها تشكر ميكنم از كمكاتون . مطالب نوشته شده رو در اولين فرصت مطالعه مبكنم .

----------


## مهران رسا

سلام؛




> به هر حال وقت امتحانات هست و هزار گرافتاري


منم در حال پشت سر گذاشتن امتحانات هستم ولی تنها با دو گرفتاری : 1-پاس کردن درس مزخرف مبانی الکترونیک 2-به نتیجه رسیدن در مورد این تاپیک 

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




> حجمشون رو زیاد کنید که چی بشه؟!


فرض کنید ما یک فایل 16 کیلوبایتی رو آماده ارسال کردیم . اگه بخوایم فایل در قالب قطعات 512 بایتی ارسال بشه ، 32 قطعه خواهیم داشت .  32 قطعه یعنی :

32 بار اتفاق افتادن رویداد OnExecute32 بار فراخوانی متد Write شی MemoryStream32 بار اتفاق افتادن حلقه ای که در سرور اصل و نسب پکت هارو بررسی میکنهو...
خب حالا اگه دغدغه ای از بابت سرعت اینترنت یا سرعت شبکه نداشته باشیم و بدونیم که حجیم کردن پکت ها (اینجا منظور از پکت چیزی بیشتر از پیغام هایی که با WriteLn ارسال میشند نیست) تا جایی که کارایی پایین نیاد و  اون عدد 32 رو کمتر کنه ، متعاقباً با کم کردن عملیات پردازشی میتونه سرعت رو چند برابر کنه . فرض کنید ما مطمئن هستیم که قطعات 2048 بایتی String بدون رسیدن به CRLF میتونند بدون محدودیت منتقل بشند . در این صورت اینبار به جای 32 قطعه(پکت) ، 8 قطعه برای ارسال خواهیم داشت که موارد اتفاق افتادن عملیات پردازشی در سرور(حلقه ها و غیره) رو به یک چهارم کاهش میده .



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


برای بنده فعلاً هدف یادگیری هست .

در کل آیا در مورد اولین سوال این تاپیک توضیحی دارید ؟



> دارم يك برنامه FileSharing  با استفاده از indy  مينويسم. 
>  اگر فايل كوچك و معمولي باشه 2-3 مگابايت به راحتي ارسال ميشه.
>  اما اگر بيشتر باشه گيرنده ميبينه يك فايل بزرگ در حد گيگابايت دريافت كرده  .
>  مثلا يك فايل 7 مگابايت ميشه چند گيگابايت . كه البته اين فايل هم درست كار  نمي كنه 
> 
>          fStream := TFileStream.Create(fileName, fmOpenRead);
>          ASender.Thread.Connection.WriteStream(fStream, True, True);.

----------


## vcldeveloper

> آقای کشاورز متد WriteFile انعطاف پذیر نیست و آپشن بخصوصی هم در اختیار ما  قرار نمیده .


مثلا چه Optionایی باید در اختیار شما قرار بده؟ یک بحث کنترل بافر هست، که اون هم می تونید اندازه اش را خودتون با تغییر مقدار WriteBufferSize انجام بدید.




> فرض کنید ما یک فایل 16 کیلوبایتی رو آماده ارسال کردیم . اگه بخوایم فایل  در قالب قطعات 512 بایتی ارسال بشه ، 32 قطعه خواهیم داشت .  32 قطعه یعنی :
> 
> 32 بار اتفاق افتادن رویداد  OnExecute32 بار فراخوانی متد Write شی MemoryStream32 بار اتفاق افتادن حلقه ای که در سرور اصل و نسب پکت هارو بررسی  میکنهو...
> خب حالا اگه دغدغه ای از بابت سرعت اینترنت یا سرعت شبکه نداشته باشیم  و بدونیم که حجیم کردن پکت ها (اینجا منظور از پکت چیزی بیشتر از پیغام  هایی که با WriteLn ارسال میشند نیست) تا جایی که کارایی پایین نیاد و  اون  عدد 32 رو کمتر کنه ، متعاقباً با کم کردن عملیات پردازشی میتونه سرعت رو  چند برابر کنه . فرض کنید ما مطمئن هستیم که قطعات 2048 بایتی String بدون  رسیدن به CRLF میتونند بدون محدودیت منتقل بشند . در این صورت اینبار به  جای 32 قطعه(پکت) ، 8 قطعه برای ارسال خواهیم داشت که موارد اتفاق افتادن  عملیات پردازشی در سرور(حلقه ها و غیره) رو به یک چهارم کاهش میده .


ببینید، اولا شما نمی تونید همینطوری اندازه داده ارسالی را زیاد کنید، و انتظار افزایش سرعت داشته باشید. این افزایش کارایی به MTU شبکه شما بستگی داره، افزایش حجم بسته اگر از مقدار خاصی بیشتر بشه، به جای افزایش کارایی، موجب کاهش کارایی میشه.
از طرف دیگه،  Indy همینطوری داده شما رو ارسال نمیکنه، بلکه اگر به کد متد Write دقت کنید، اگر حجم داده شما از حجم WriteBuffer بیشتر باشه، Indy خودش داده را به اندازه مقدار WriteBuffer تقسیم میکنه، و هر بار WriteBuffer را با بخشی از داده ارسالی پر میکنه، و هر وقت WriteBuffer پر شد، آن را برای سرور ارسال میکنه، پس Indy کار تقسیم داده های بزرگ به بخش های کوچکتر را براتون انجام میده، و برای کنترل حجم این قسمت های کوچکتر هم می تونید از خصوصیت WriteBufferSize آن استفاده کنید.




> برای بنده فعلاً هدف یادگیری هست .


اگر ارتباط چندانی بین داده ارسالی به عنوان فایل، و سایر فرامینی که یک کلاینت به سرور ارسال میکنه، وجود نداشته باشه، یکی از راه های موثر و مناسب این هست که شما در برنامه کلاینت خودتون چند IdTcpClient داشته باشید، که هر کدام از Port جداگانه ایی استفاده می کنند. اینطوری از نظر سرور، این دو کلاینت های متفاوتی هستند، و در حالی که یکی داره به سرور فایل ارسال میکنه، اون یکی میتونه فرامین دیگه ایی رو به سرور ارسال کنه. 




> در کل آیا در مورد اولین سوال این تاپیک توضیحی دارید ؟


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

----------


## مهران رسا

همونطور که در روش قبلی مشاهده میکنید کاراکترهای فایل به کد اسکی تبدیل میشند و بعد از جداسازی توسط کاراکتر "`" برای ارسال آماده میشند که متاسفانه اینکار حجم فایل رو 2 تا 4 برابر میکنه . چراکه ضمن تبدیل کردن کاراکترها به کد اسکی اونهارو separate هم میکنیم .

به هر حال با این روش رشته هایی که به عنوان File-Part به سرور ارسال میشند خارج از محدوده (0123456789`) نیستند .

اما الآن داشتم سعی میکردم محتویات فایل رو بدون اعمال تغییرات بالا و بدون واسطه روی خط بنویسم .
که این کد :
D2Send := D2Send + '`' + inttostr(B[j]);به این کد تغییر داده شد :

D2Send := D2Send + chr(B[j]);و در سمت سرور هم کد به این صورت تغییر داده شد:
              for jj := 1 to length(FilePart) do
              begin
                AChar := copy(FilePart, jj, 1);
                AByte := ord(AChar[1]);
                Ms[i].Write(AByte, 1);
              end;اما مشکل اینجاست که برخی کاراکتر ها وقتی به سرور میرسند به کاراکترهای جدیدی تبدیل میشند .
مثلاً کاراکتر ے به y تبدیل میشه و نهایتاً فایل مخدوش میشه .

چی پیشنهاد میکنید ؟

----------


## bmanfy

سلام



> اما مشکل اینجاست که برخی کاراکتر ها وقتی به سرور میرسند به کاراکترهای جدیدی تبدیل میشند .
> مثلاً کاراکتر ے به y تبدیل میشه و نهایتاً فایل مخدوش میشه .


اره دقيقا من هم ديروز همچين كاري انجام دادم يك فايل دقيقا با همان اندازه مبدا منتقل ميشد اما درست نبود و كاركتراش عوض ميشد !!!!!
چرا ؟؟؟؟؟؟؟؟؟

البته اين كد رو هم به صورت زير نوشتم در اين حالت تغييري نميكنه :
كد زير براي كپي معمولي يك فايله :

procedure TForm1.Button2Click(Sender: TObject);
var f1 , f2: TFileStream;
     b: array [1.. 1024] of byte;
     s , i, j : integer;
     str :string;
begin
    f1 := TFileStream.Create('d:\03.mp3', fmOpenRead);
    f2 := TFileStream.Create('d:\04.mp3', fmCreate);
    i := 0;
    s := f1.Size;
    ProgressBar1.Max := s ;
    t := 0;
    Timer1.Enabled := true;
    while(i < s) do
       begin
             f1.Position := i;
             f1.Read(b , 1024);
             //********************
             str := '';
             for j:=1 to 1024 do
                 begin
                     str := str + chr(b[j]);
                 end ;
             for j:=1 to 1024 do
                 begin
                     b[j] := ord(str[j]);
                 end ;
             //**********************
             f2.Write(b, 1024);
             i := i + 1024;
             ProgressBar1.Position := i;
    end;
    Timer1.Enabled := false;
    f1.Free;
    f2.Free;
    ProgressBar1.Position := 0;
end;
 


شابد وقتي ميديمش دست ايندي يه بلايي سرش مياره؟!!!!!

----------


## bmanfy

راتش من براي نوشتن اين برنامه از دلفي 7 استفاده ميكنم . كه البته روي اون ايندي 9 قرار  داره ظاهرا كد هايي كه شما گفتيد همگي با ايندي 10 است .

متدي به اسم ioHandler وجود نداره براي IdTcpClient البته هست اما بعدش متدي به اسم write  نيست . البته يك تبي هست به اسم ioHandler كه چنت كامپوننت داره فكر كنم بايد از اونها استفاده بشه كه البته بلد نيستم .

و تو ايندي 9 idcmdTcpServer  هم تو خود idTcpServer قرار داره . درسته ؟

----------


## bmanfy

if Fs[i].FileName = FileSign then

درباره كد بالا هم بايد بگم اين رو نميشناسه . به عبارتي FileName. رو نميشناسه . شايد چون من از دلفي 7 استفاده ميكنم.





> در سمت سرور قطعات دریافتی فایل در Memory ذخیره میشند و نهایتاً در فایل نوشته میشند . این روش در کارهای بزرگتر و در مواقعی که حافظه محدوده خطرناک هست و باید طوری اصلاح بشه که مثلاً بعد از دریافت هر 20 پارت ، یک بار حافظه اختصاص یافته پس داده بشه و 20 پارت جدید در فایل نوشته بشه . البته سروکله زدن با دیسک هم خودش زمان بر هست . درنتیجه در مواردی که سرعت در اولویت قرار داره به جای 20 میتونیم از عدد بزرگتری استفاده کنیم .


خوب اگر از TFileStream استفاده كنيم و مستقيم دستوارت رو در اون بنويسيم چي؟ مگر نه اينكه ما هرچي توي يك FileStream مينويسيم مستقيم تو فايل نوشته ميشه ؟!
خوب پس ....

var fs:TfileStream;
begin 
   fs := TFileStream.Create('c:\ali.txt', fmCreate);
   fs.write('ali', 3);
   fs.free;
end;


و دقيقا زماني كه يك فايل با استفاده از fileStream باز شده باشه تا زماني كه بسته نشه نميشه ازش استفاده كرد.

در باره fileStream درست گفتم ؟؟؟؟؟

----------


## bmanfy

> overwrite نه، overload. لازم نیست شما کار خاصی انجام بدید، منظور از overload شدن این هست که چند متد با نام یکسان، ولی پارامترهای مختلف در اون کلاس تعریف شدند. شما اگر به تعریف متد write رجوع کنید، می بینید که چندین متد write وجود دارند که هر کدام پارامترهای خاص خودشان را دریافت می کنند (مثلا یکی آرایه دریافت میکنه، یکی stream، یکی دیگه string، و غیره). اینکه از کدومشون استفاده میشه، بستگی به پارامتری داره که شما بهش میدید؛ اگر پارامتر شما stream باشه، اون متد writeایی که stream دریافت میکنه، اجرا میشه، اگر آرایه باشه، اونی که آرایه دریافت میکنه، اجرا میشه.


با توجه به گفته هاي اقاي كشاورز در پست ها ي اوليه تاپيك :
ما داريم الان بايت ها رو نبديل به يك رشته ميكنيم و بعد ميفرستيم . اما با توجه به نقل قول باشه ميشه ارايه رو هم فرستاد . 
(البته شايد اين باز هم تو ايندي 10 باشه . فعلا كه من با 9 كار ميكنم . )
حالا ميمونه اون نشانه ها كه بايد شناسايي بشه خوب ميتونيم در همون چند بايت از ارايه رو هم به همين اختصاص بديم .

خوب حالا چه طور ارايه ارسال كنيم ؟

----------


## bmanfy

> نقل قول:
> در کل آیا در مورد اولین سوال این تاپیک توضیحی دارید ؟ 
> یک نمونه برنامه بزارید که این مشکلی که بهش اشاره کردید رو داشته باشه، تا من تست کنم.


توي نمونه اي كه گذاشتم فكر ميكنم فهميدم چرا اندازه فايل در حد گيگابات ميشه . البته فقط فهميدم از كجاست دليلش رو نه .

زماني كه فايل رو تبديل به استريم ميكنيم و با writeStream ميفرستيم در اون طرف هم (سرور) فايل رو دريافت ميكنه و مينويسه رو  ديسك . 
كه الان اندازه فايل در حد 1گيگابايت هست . بعد از اينكه كلاينت disconnect ميكنه اندازه فايل مياد سر جاي اصليش (كم ميشه البته گاهي يكي دو سه بايت بيشتر ميشه )
اما خوب باز هم يه مشكل براي فايل هاي نسبتا بزرگتر داره . من يك فايل فيلم 10 مگابايتي رو تست كردم فايل رو تست كردم اما نميتونه پخش كنه !!!!!!!

----------


## bmanfy

ميشه بگيد با ابزار هاي TcpServer , TcpClient كه در تب intenet قرار گرفته چه طور بايد كار كرد.

با idFtpServer هم چهطور؟
در مورد كد زير هم من چيزي نفهميدم :

    try
      FClient.Connect;
      FClient.IOHandler.LargeStream := True;
      FClient.IOHandler.WriteLn(cmdFile);
      FClient.IOHandler.WriteLn(ExtractFileName(FFileNam  e));
      FClient.IOHandler.WriteLn(IntToStr(FileSize));
      FClient.IOHandler.WriteLn(FileHash);
      FClient.IOHandler.WriteFile(FFileName);
    finally
      FClient.Disconnect;
    end;

----------


## مهران رسا

> چون من از دلفي 7 استفاده ميكنم.


از دلفی 2010 و Indy 10 استفاده کنید .



> خوب اگر از TFileStream استفاده كنيم و مستقيم دستوارت رو در اون بنويسيم  چي؟ مگر نه اينكه ما هرچي توي يك FileStream مينويسيم مستقيم تو فايل نوشته  ميشه ؟!
>  خوب پس ....


استفاده از FileStream به جای MemoryStream برای نوشتن فایل به صورت بایت بایت زمان زیادی میگیره . برای همین ما اطلاعات رو ابتدا در Memory مینویسیم که سرعت بیشتری داره و بعد طی زمان های مشخص شده قسمتی از اطلاعات ذخیره شده در حافظه رو بر روی فایل می نویسیم و حافظه اختصاص یافته رو پس میدیم . این کار تا رسیدن به پایان فایل ادامه پیدا میکنه .




> ما داريم الان بايت ها رو نبديل به يك رشته ميكنيم و بعد ميفرستيم


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



> اما با  توجه به نقل قول باشه ميشه ارايه رو هم فرستاد .


به احتمال فراوان با متد Write آرایه هم میشه فرستاد . اما ما سعی داریم تا جای ممکن کد بدون واسطه بنویسیم . یعنی ارسال مستقیم رشته ها با استفاده از متد WriteLn




> در مورد كد زير هم من چيزي نفهميدم :


اول یک سری اطلاعات در مورد فایل برای سرور ارسال میکنه و نهایتاً خود فایل با متد WriteFile ارسال میشه . البته این کد در عمل و مخصوصاً زمانیکه سرور بخواد از یک یا چند کلاینت به طور همزمان یک یا چند فایل دریافت کنه قابل استفاده نیست .

----------


## bmanfy

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

----------


## bmanfy

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

----------


## bmanfy

اما خوب فعلا باز یک مشکل جدید دارم .
فردا هم قراره تحویل بدم پروژم رو . البته فعلا یادگیری مهمتره . 
میخوام برنامه من قابلیت چت مثل یاهو رو داشته باشه به عبارتی بتونه در یک زمان با n نفر (که البته n تو برنامه من 255 تفر است) چت کنه .

به این صورت عمل کردم :
ابتدا یک ارایه از نوع فرم چت ساخته شده ، ساختم :

_ChatForms : array[1 .. 255] of TFrmChat;


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

function createChatForm(DestUid :int64; DestFormId:integer; DefoultMsg:string):integer;
var i : integer;
begin
    for i:=1 to 255 do
        begin
            if _ChatForms[i]=nil then
               begin
                   Application.CreateForm(TFrmChat, _ChatForms[i]);
                   _ChatForms[i].DestUid := DestUid ;
                   _ChatForms[i].DestFormId := DestFormId;
                   _ChatForms[i].SourceFormId := i;
                   _ChatForms[i].DefoultMeg := DefoultMsg;
                   _ChatForms[i].Show;
                   Result := 1;
                   exit;
               end;
            if i=255 then
               Result := 0;
        end;//for
end;


حالا سعی میکنه یه پیامی ارسال کنه به شکل زیر :

var Lst : TStrings;
begin
    try
        IdTcpClientChat.Connect();
        try
            lst := TStringList.Create;
            Lst.Add(IntToStr(MdlData.QryUsers['Uid'])) ;
            Lst.Add(IntToStr(SourceFormId));
            Lst.Add(IntToStr(DestFormId));
            Lst.Add(EdtMsg.Text);
            IdTcpClientChat.WriteStrings(Lst);
            IdTcpClientChat.Disconnect;
        finally
            Lst.Free;
        end;
    except
        ShowMessage('You can not connect now.');
    end;
end;

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

var Lst :TStrings;
    DestUid:int64;
    DestformId, SourceFormId : Integer;
    Msg : string;
begin
    try
        Lst := TStringList.Create;
        AThread.Connection.ReadStrings(Lst, 4);
        DestUid := StrToInt(Lst.Strings[0]);
        DestformId := StrToInt(Lst.Strings[1]);
        SourceFormId := StrToInt(Lst.Strings[2]);
        Msg := Lst.Strings[3];
        if (SourceFormId = 0 ) or (_ChatForms[SourceFormId]=nil) then
           begin
               createChatForm(DestUid, DestformId, Msg);
           end
        else
           begin
           end;
    finally
        Lst.Free;
    end;
end;

که البته فعلا کامل نیست اما در همین حد یه مشکلی داره .
خوب حالا اندیس فرمش رو میخونه اگر صفر باشه یا اون خونه از ارایه خالی باشه باید یک فرم جدید براش ساخته بشه . 
که اینجا ساخته میشه اما نشون داده نمیشه دیگه .
بعد از بستن برنامه هم یه خطای داغون میده . 
چرا ؟

----------


## bmanfy

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

----------


## مهران رسا

کد خوبی بود ! اما من همچنان روی ارسال با استفاده از WriteLn (رشته ای) تاکید دارم . دلیلش هم اینه که با استفاده از WriteLn میدونیم دقیقاً چه چیزی داره به سرور ارسال میشه . ضمن اینکه ما میخوایم تنها با یک سرور و از طریق یک پورت همه پیغام ها(اعم از لاگین ، پی ام ، ارسال فایل) رو پیاده سازی کنیم .




> میخوام برنامه من قابلیت چت مثل یاهو رو داشته باشه


از موضوع تاپیک خارج هست . میتونید در یک تاپیک جدید مطرح کنید .

----------


## bmanfy

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

----------


## bmanfy

> کد خوبی بود ! اما من همچنان روی ارسال با استفاده از WriteLn (رشته ای) تاکید دارم . دلیلش هم اینه که با استفاده از WriteLn میدونیم دقیقاً چه چیزی داره به سرور ارسال میشه .


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

----------


## مهران رسا

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


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




> خوب در عوض شما دارید اطلاعات رو در حد چند برابر بیشتر میفرستید


این مشکل هم بهه زودی برطرف میشه . ضمناً نکته ای که در مورد پشتیبانی از ارسال چند کاربره وجود داره اینه که به دلیل مشکلاتی که در اثر Shift دادن پیش میاد نباید از آرایه و یا موارد مشابه برای کنترل کاربران استفاده کرد .

----------


## bmanfy

ok.
یه پست تاپیک جدید ساختم . حالا لطفا اگر اطلاعاتی در اون زمینه دارید بفرمایید .
ممنون.
https://barnamenevis.org/showthread.php?t=230715

----------


## مهران رسا

خب ؛ ابتدا از شما تقاضا دارم توضیحات موجود در این پست رو مطالعه بفرمایید :
https://barnamenevis.org/showpo...1&postcount=23و اما مشکل جدید : وقتی کار Transfer یکی از مشتری ها تموم بشه ، در حالی که ممکنه 10 کلاینت دیگه هم زمان در حال ارسال فایل باشند ، باید آرایه Global تغییر بعد پیدا کنه و اینکار باعث ایجاد اختلال در عملکرد بقیه کلاینت ها میشه . چون برای اینکه بفهمیم قطعه های فایل مربوط به کدوم مشتری هستند از آرایه ای استفاده کردیم که در زمان درخواست به طولش یک واحد اضافه میشد و در زمان اتمام عملیات یک واحد از طولش کم میشد . اما تصویر کنید حلقه ی بررسی در حال اجراست و همون لحظه کار یکی از مشتری ها به پایان میرسه . در این صورت Object مربوطه Free میشه و متعاقباً باید طول آرایه هم یک واحد کمتر بشه . که نهایتاً با اینکار عملیات ارسال فایل بقیه کلاینت ها به دلیل تغییر تعداد خانه های آرایه با مشکل مواجه میشه .

 for i := low(Fs) to high(Fs) do

چی پیشنهاد میکنید ؟

----------


## bmanfy

بله دقیقا این مشکل هست .
که من برای این کار از این روش استفاده کردم .
یک ارایه با طور ثابت(مثلا به طور حداکثر کانکشن هایی که میخوایم با TcpSrver باشه) در نظر میگیریم  که  قاعدتا هر کدوم یک اندیش دارن . ارایه میتونه از نوع TFileStream   و یا MemoryStream باشه . و حتی میتونه اشاره گری باشه  به یکی از این دو ساختار . 
در مرحله اول که کلاینت اعلام میکنه میخوام برات فایل بفرستم  داخل ارایه میگریده و اولین خانه nil رو پیدا میکنه و اندیس اون رو به کلاینت میفرسته . از این به بعد کلاینت در پکت های خود این اندیس رو جای میده و نتیجه سرور میفهمه مال کجاست.
در انتها هم که کارش تمام شد اون محلی که اختصاص داده شده رو free میکنیم. و خانه ارایه رو هم برابر nil قرار میدهیم .
این جوری طول ارایه کم نمیشه دیگه .
البته اگر هم بخواهیم خیلی نگران حافظه باشیم . میتونیم در هر بار که درخواست شد شی حافظه رو بسازیم و اشاره گر به اون رو به کلاینت پس بفرستیم .

----------


## bmanfy

کسی درباره پست های 40 - 41 نظری نداره ؟

----------


## مهران رسا

> بله دقیقا این مشکل هست .
> که من برای این کار از این روش استفاده کردم .
> یک ارایه با طور ثابت(مثلا به طور حداکثر کانکشن هایی که میخوایم با TcpSrver باشه) در نظر میگیریم  که  قاعدتا هر کدوم یک اندیش دارن . ارایه میتونه از نوع TFileStream   و یا MemoryStream باشه . و حتی میتونه اشاره گری باشه  به یکی از این دو ساختار . 
> در مرحله اول که کلاینت اعلام میکنه میخوام برات فایل بفرستم  داخل ارایه میگریده و اولین خانه nil رو پیدا میکنه و اندیس اون رو به کلاینت میفرسته . از این به بعد کلاینت در پکت های خود این اندیس رو جای میده و نتیجه سرور میفهمه مال کجاست.
> در انتها هم که کارش تمام شد اون محلی که اختصاص داده شده رو free میکنیم. و خانه ارایه رو هم برابر nil قرار میدهیم .
> این جوری طول ارایه کم نمیشه دیگه .
> البته اگر هم بخواهیم خیلی نگران حافظه باشیم . میتونیم در هر بار که درخواست شد شی حافظه رو بسازیم و اشاره گر به اون رو به کلاینت پس بفرستیم .


نه تفاوتی نداره .چون نهایتاً ما از خصوصیت FileName مربوط به شی FileStream برای مطلع شدن از صحت نام فایل جاری استفاده میکنیم . حالا حتی اگه تعداد اعضای آرایه هم ثابت باشه ، در زمانی که یکی از اشیاء Free بشه در حلقه زیر در قسمتی که مشخص کردم یک استثنا به وجود میاد . علتش هم اینه که شی []FS وجود نداره چون قبلاً Free شده :

          for i := low(Fs) to high(Fs) do
          begin
            if Fs[i].FileName = FileSign then
            begin

              //Get any part of file
              FilePart := copy(CMd, JPos + 3, length(CMd));
              Split('`', FilePart, TsL);
              //Write the file as bytes
              for jj := 0 to TsL.Count - 1 do
              begin
                AChar := TsL[jj];
                AByte := strtoint(AChar);
                Ms[i].Write(AByte, 1);
              end;

            end;
          end;




> کسی درباره پست های 40 - 41 نظری نداره ؟


خواهش میکنم نظم تاپیک رو رعایت کنید .

----------


## bmanfy

> نه تفاوتی نداره .چون نهایتاً ما از خصوصیت FileName مربوط به شی FileStream برای مطلع شدن از صحت نام فایل جاری استفاده میکنیم . حالا حتی اگه تعداد اعضای آرایه هم ثابت باشه ، در زمانی که یکی از اشیاء Free بشه در حلقه زیر در قسمتی که مشخص کردم یک استثنا به وجود میاد . علتش هم اینه که شی []FS وجود نداره چون قبلاً Free شده :


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




> خواهش میکنم نظم تاپیک رو رعایت کنید .


؟؟؟؟؟!!!!!!!!!!!

----------


## مهران رسا

> نه دیگه قرار نیست ما بیایم از ابتدا تا انتها ارایه رو بررسی کنیم و  ببینیم نام فایلش چیه .
>  گفتم در مرحله اول که کلاینت میگه میخوام برات فایل بفرستم اندیس ارایه  براش فرستاده میشه و از این به بعد بسته های اطلاعاتی رو که به سمت سرور  خواهد فرستاد هم این اندیس به ابتداش اضافه خواهد شد. و سرور هم با دریافت  هر بسته ابتدا اندیس رو بدست مباره و بعد مستقیم به اون اندیس مراجعه میکنه  . که البته خوب میشه قبلش هم چک کرد که nil نباشه تا یهو خطا رخ نده .
>  اینجوری دیگه یه حلقه For اجر نمیشه که در زمانن صرفه جویی میشه .


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

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

----------


## bmanfy

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

البته من تا اینجایی که پیش روفتم به این شکله که یک شخص در ان واحد تنها میتونه یک فایل دریافت کنه .
و هر کلاینت بتونه برای چند نفر ارسال کنه .
از نظر کد نویسی هم پیاده سازی کردم این رو اما یه مشکلی هست اون هم این که زمانی که کلاینت داره فایلی رو ارسال میکنه برنامه کلاینت به یه حالت هنگ میره تا زمانی که ارسالش تمام بشه (فکر کنم اصطلاحش هم Freez باشه ، درسته؟)
و همچنین زمانی که دو شخص یهش میگن فایل بفرست به شخص اول که پاسخ داده میشه برنامه به همون حالتی که گفتم میره و حتی درخواست های طرف ها رو هم نشون نمیده .

یا مثلا حتی زمانی که داره فایل رو ارسال میکنه progressbar مربوط به اون هنگ میکنه . از رفرش هم استفاده کردم اما نشده .

در این باره چی ؟


البته این سوالم هم در باره ارسال فایله دیگه.(سوال اصلی همون یخ زدن برنامست) :چشمک:

----------


## مهران رسا

از کنترل IdAntiFreeze استفاده کنید . این کنترل مخصوص همین کار هست .

----------


## bmanfy

> از کنترل IdAntiFreeze استفاده کنید . این کنترل مخصوص همین کار هست .


 نکته اینجاست که استفاده کردم . 
فرمی که باز میشه تنها شامل یک IdTcpClient هست . و یکی هم از همین IdAntiFreeze  ایا باید تنظیم خاصی براش انجام بدم ؟

این کامپوننت رو باید به ازاری TcpSrver ها یگذاریم یا هم TcpServer , TcpClient ؟ یکی برای کل برنامه کافیه  یا برای هم کدام یکی ؟

ممنون.

----------


## مهران رسا

IdAntiFreeze در سمت کلاینت استفاده میشه . 



> باید تنظیم خاصی براش انجام بدم ؟


فقط روی فرم قرار بدید و Active رو برابر True قرار بدید . ضمناً برای اینکه برنامه کمترین حالت Freeze رو داشته باشه میتونید مقدار خاصیت IdleTimeOut رو کمتر کنید . 




> یکی برای کل برنامه کافیه


بله

----------


## bmanfy

خوب پس با این وجود من نکات اصلی رو رعایت کردم.
پس چه دلیی میتونه داشته باشه ؟

----------


## مهران رسا

> خوب پس با این وجود من نکات اصلی رو رعایت کردم.
> پس چه دلیی میتونه داشته باشه ؟


ما که کدی ندیدیم ! لطفاً نمونه برنامه ای که نوشتید رو قرار بدید تا بشه در موردش نظر داد .

----------


## bmanfy

بسيار خوب. 
خود برنامه يك مقدار ميشه گفت كد زياد داره . يك نمونه كد در اولين فرصت اماده ميكن و قرار ميدم . 
البته بدم هم نمياد در باره برنامه اي كه نوشتم نظري بدين . بعد از اين كه كامل شد اون رو هم اينجا قرار ميدم . 
ممنون .

----------


## Reza_A7b

سلام دوستان ، من تو برنامه ام میخواستم با استفاده از کامپوننت Indy فایل هامو send و recive کنم اما الان که دیدم اینقد سخت و ... بخیال شدم ، و با مراجعه به مخم و SQL سرور اینکارو انجام میدم مدیریت ترافیک و همروندیشم به عهده SSMS میافته 
یعنی جدولی می سازم رو دیتا بیس سرور و فایلهارو با کلاینها تو اون مینویسم بعد با یه برنامه رو سرور فایل هارو از روی دیتابیس ورمیدارم و خلاص ، برعکسش هم امکان پذیره  :متفکر: 
منتظر نظر دوستان در مورد این روش هستم ؟؟؟؟؟؟؟؟؟

----------


## Felony

> سلام دوستان ، من تو برنامه ام میخواستم با استفاده از کامپوننت Indy فایل هامو send و recive کنم اما الان که دیدم اینقد سخت و ... بخیال شدم ، و با مراجعه به مخم و SQL سرور اینکارو انجام میدم مدیریت ترافیک و همروندیشم به عهده SSMS میافته 
> یعنی جدولی می سازم رو دیتا بیس سرور و فایلهارو با کلاینها تو اون مینویسم بعد با یه برنامه رو سرور فایل هارو از روی دیتابیس ورمیدارم و خلاص ، برعکسش هم امکان پذیره 
> منتظر نظر دوستان در مورد این روش هستم ؟؟؟؟؟؟؟؟؟


نظر چي ؟ اين كار از بنيان مشكل داره ٠٠٠

----------


## Mask

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

----------

