صفحه 1 از 2 12 آخرآخر
نمایش نتایج 1 تا 40 از 61

نام تاپیک: ارسال فايل حجيم با indy

  1. #1

    ارسال فايل حجيم با indy

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

  2. #2

    نقل قول: ارسال فايل حجيم با indy


    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;


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

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

  3. #3

    نقل قول: ارسال فايل حجيم با indy

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

  4. #4

    نقل قول: ارسال فايل حجيم با indy

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

  5. #5

    نقل قول: ارسال فايل حجيم با indy

    راستش من اون راه 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 کنید و همون لحظه در فایل جدید بنویسید .


  6. #6

    نقل قول: ارسال فايل حجيم با indy

    راستش من اون راه 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


    وَ سَيَعْلَمُ الَّذِينَ ظَلَمُوا [آل محمد حقهم] أَيَّ مُنْقَلَبٍ يَنْقَلِبُونَ - الشعراء (227)
    و ظالمین [حق آل محمد (ص) ] به زودی خواهند دانست که به کدام بازگشتگاه بازخواهند گشت.

  7. #7

    نقل قول: ارسال فايل حجيم با indy

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

  8. #8

    نقل قول: ارسال فايل حجيم با indy

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


    وَ سَيَعْلَمُ الَّذِينَ ظَلَمُوا [آل محمد حقهم] أَيَّ مُنْقَلَبٍ يَنْقَلِبُونَ - الشعراء (227)
    و ظالمین [حق آل محمد (ص) ] به زودی خواهند دانست که به کدام بازگشتگاه بازخواهند گشت.

  9. #9

    نقل قول: ارسال فايل حجيم با indy

    الآن داشتم روی پروسه ارسال فایل به روشی که ذکر شد فکر میکردم . زمانی که همزمان 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 نمیدونه کدام بسته مربوط به کدام فایل هست !

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

  10. #10

    نقل قول: ارسال فايل حجيم با indy

    دیگه اینجا 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 جداگانه بنویسید.


    وَ سَيَعْلَمُ الَّذِينَ ظَلَمُوا [آل محمد حقهم] أَيَّ مُنْقَلَبٍ يَنْقَلِبُونَ - الشعراء (227)
    و ظالمین [حق آل محمد (ص) ] به زودی خواهند دانست که به کدام بازگشتگاه بازخواهند گشت.

  11. #11

    نقل قول: ارسال فايل حجيم با indy

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

    بازهم ممنون .

  12. #12

    نقل قول: ارسال فايل حجيم با indy

    آقای کشاورز به نظر میرسه 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;

  13. #13

    نقل قول: ارسال فايل حجيم با indy

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

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

  14. #14

    نقل قول: ارسال فايل حجيم با indy

    به نظر میرسه IdCmdTcpServer برای مواردی که جواب ثابتی برای کلاینت قرار هست ارسال بشه استفاده میشه ! از راهنمای خود Indy هم چیز زیادی نفهمدیم .
    فکر نمیکنید استفاده از TIdCmdTCPServer چندان تفاوتی با استفاده از TCPServer نداره ؟ چون به هر حال ما بازم با یک رویداد سرو کار داریم .
    در IdCmdTcpServer، شما برای امور مختلف در سرور دستوراتی تعریف می کنید، مثلا فرض کنیم شما سه دستور زیر را تعریف می کنید:
    UploadFile
    SendMsg
    GetTime

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

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

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


    وَ سَيَعْلَمُ الَّذِينَ ظَلَمُوا [آل محمد حقهم] أَيَّ مُنْقَلَبٍ يَنْقَلِبُونَ - الشعراء (227)
    و ظالمین [حق آل محمد (ص) ] به زودی خواهند دانست که به کدام بازگشتگاه بازخواهند گشت.

  15. #15

    نقل قول: ارسال فايل حجيم با indy

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

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

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

  16. #16

    نقل قول: ارسال فايل حجيم با indy

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

    آیا IdCmdTcpServer باید در کنار TCPServer استفاده بشه ؟
    نه، خودش یک سرور کامل هست.


    وَ سَيَعْلَمُ الَّذِينَ ظَلَمُوا [آل محمد حقهم] أَيَّ مُنْقَلَبٍ يَنْقَلِبُونَ - الشعراء (227)
    و ظالمین [حق آل محمد (ص) ] به زودی خواهند دانست که به کدام بازگشتگاه بازخواهند گشت.

  17. #17

    نقل قول: ارسال فايل حجيم با indy

    نیازی نیست که حتما این کار رو بکنید، می تونید فایل رو در بسته های جداگانه ارسال کنید، و هر بسته خودش ارسال یک دستور باشه، مثلا دستوری با نام FilePart که یکی از پارامترهای آن هم شماره قطعه مربوطه از فایل باشه.
    با این اوصاف پس مهم نیست که از IdCmdTcpServer استفاده بشه یا از TCPServer ؛ چون نهایتاً مدیریت قطعات فایل رو خودمون باید بر عهده بگیریم و در واقع IdCmdTcpServer به خودی خود کار خاصی برای ما انجام نمیده .
    در نتیجه :
    یعنی هر پیغامی که دریافت میشه دارای شماره شناسایی باشه . حالا اگه 100 نفر هم همزان در حال ارسال فایل باشند ، Indy از روی شماره شناسایی میفهمه که کدوم بسته رو در کدوم فایل ذخیره کنه . اما مشکلش پردازش بالای کارهست
    درسته ؟

  18. #18

    نقل قول: ارسال فايل حجيم با indy

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

  19. #19

    نقل قول: ارسال فايل حجيم با 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`0
    After Compression : 0!10
    البته این روش همیشه بهینه نیست و قطعاً روش های بهتری باید وجود داشته باشه . من در این مورد زیاد مطمئن نیستم اما یک روش دیگه اینه که کد بالا رو به این صورت بنویسیم :
    for j := 1 to 256 do
    begin
    D2Send := D2Send + chr(inttostr(B[j]));
    end;



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


    نميشه يك ارايه از بايت رو ارسال كرد؟
    اگه اشتباه نکنم با WriteLn فقط رشته میشه ارسال کرد .


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

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



  20. #20

    نقل قول: ارسال فايل حجيم با indy

    با این اوصاف پس مهم نیست که از 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 بسازید که اساسا کارش همین هست.


    وَ سَيَعْلَمُ الَّذِينَ ظَلَمُوا [آل محمد حقهم] أَيَّ مُنْقَلَبٍ يَنْقَلِبُونَ - الشعراء (227)
    و ظالمین [حق آل محمد (ص) ] به زودی خواهند دانست که به کدام بازگشتگاه بازخواهند گشت.

  21. #21

    نقل قول: ارسال فايل حجيم با indy

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

  22. #22

    نقل قول: ارسال فايل حجيم با indy

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


    وَ سَيَعْلَمُ الَّذِينَ ظَلَمُوا [آل محمد حقهم] أَيَّ مُنْقَلَبٍ يَنْقَلِبُونَ - الشعراء (227)
    و ظالمین [حق آل محمد (ص) ] به زودی خواهند دانست که به کدام بازگشتگاه بازخواهند گشت.

  23. #23

    نقل قول: ارسال فايل حجيم با indy

    بلآخره با راهنمایی های آقای کشاورز یک کد نمونه برای ارسال فایل با استفاده از کنترل 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 : اضافه کردن توضیحات .


    آخرین ویرایش به وسیله مهران رسا : سه شنبه 08 تیر 1389 در 12:22 عصر دلیل: اضافه کردن توضیحات

  24. #24

    نقل قول: ارسال فايل حجيم با indy

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

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

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

  25. #25

    نقل قول: ارسال فايل حجيم با indy

    در مورد نظریه حجیم کردن پکت ها برای بالا بردن سرعت هنوز مطمئن نیستم
    حجمشون رو زیاد کنید که چی بشه؟! میشه بسته های 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;


    وَ سَيَعْلَمُ الَّذِينَ ظَلَمُوا [آل محمد حقهم] أَيَّ مُنْقَلَبٍ يَنْقَلِبُونَ - الشعراء (227)
    و ظالمین [حق آل محمد (ص) ] به زودی خواهند دانست که به کدام بازگشتگاه بازخواهند گشت.

  26. #26

    نقل قول: ارسال فايل حجيم با indy

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

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


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

  27. #27

    نقل قول: ارسال فايل حجيم با indy

    سلام؛

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

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

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

    1. 32 بار اتفاق افتادن رویداد OnExecute
    2. 32 بار فراخوانی متد Write شی MemoryStream
    3. 32 بار اتفاق افتادن حلقه ای که در سرور اصل و نسب پکت هارو بررسی میکنه
    4. و...

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

    در کل آیا در مورد اولین سوال این تاپیک توضیحی دارید ؟
    دارم يك برنامه FileSharing با استفاده از indy مينويسم.
    اگر فايل كوچك و معمولي باشه 2-3 مگابايت به راحتي ارسال ميشه.
    اما اگر بيشتر باشه گيرنده ميبينه يك فايل بزرگ در حد گيگابايت دريافت كرده .
    مثلا يك فايل 7 مگابايت ميشه چند گيگابايت . كه البته اين فايل هم درست كار نمي كنه

             fStream := TFileStream.Create(fileName, fmOpenRead);
    ASender.Thread.Connection.WriteStream(fStream, True, True);
    .

  28. #28

    نقل قول: ارسال فايل حجيم با indy

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

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

    1. 32 بار اتفاق افتادن رویداد OnExecute
    2. 32 بار فراخوانی متد Write شی MemoryStream
    3. 32 بار اتفاق افتادن حلقه ای که در سرور اصل و نسب پکت هارو بررسی میکنه
    4. و...

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

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

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


    وَ سَيَعْلَمُ الَّذِينَ ظَلَمُوا [آل محمد حقهم] أَيَّ مُنْقَلَبٍ يَنْقَلِبُونَ - الشعراء (227)
    و ظالمین [حق آل محمد (ص) ] به زودی خواهند دانست که به کدام بازگشتگاه بازخواهند گشت.

  29. #29

    نقل قول: ارسال فايل حجيم با indy

    همونطور که در روش قبلی مشاهده میکنید کاراکترهای فایل به کد اسکی تبدیل میشند و بعد از جداسازی توسط کاراکتر "`" برای ارسال آماده میشند که متاسفانه اینکار حجم فایل رو 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 تبدیل میشه و نهایتاً فایل مخدوش میشه .

    چی پیشنهاد میکنید ؟
    آخرین ویرایش به وسیله مهران رسا : یک شنبه 06 تیر 1389 در 11:24 صبح

  30. #30

    نقل قول: ارسال فايل حجيم با indy

    سلام
    اما مشکل اینجاست که برخی کاراکتر ها وقتی به سرور میرسند به کاراکترهای جدیدی تبدیل میشند .
    مثلاً کاراکتر ے به 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;



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

  31. #31

    نقل قول: ارسال فايل حجيم با indy

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

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

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

  32. #32

    نقل قول: ارسال فايل حجيم با indy


    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 درست گفتم ؟؟؟؟؟

  33. #33

    نقل قول: ارسال فايل حجيم با indy

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

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

  34. #34

    نقل قول: ارسال فايل حجيم با indy

    نقل قول:
    در کل آیا در مورد اولین سوال این تاپیک توضیحی دارید ؟
    یک نمونه برنامه بزارید که این مشکلی که بهش اشاره کردید رو داشته باشه، تا من تست کنم.
    توي نمونه اي كه گذاشتم فكر ميكنم فهميدم چرا اندازه فايل در حد گيگابات ميشه . البته فقط فهميدم از كجاست دليلش رو نه .

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

  35. #35

    نقل قول: ارسال فايل حجيم با indy

    ميشه بگيد با ابزار هاي 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;

  36. #36

    نقل قول: ارسال فايل حجيم با indy

    چون من از دلفي 7 استفاده ميكنم.
    از دلفی 2010 و Indy 10 استفاده کنید .
    خوب اگر از TFileStream استفاده كنيم و مستقيم دستوارت رو در اون بنويسيم چي؟ مگر نه اينكه ما هرچي توي يك FileStream مينويسيم مستقيم تو فايل نوشته ميشه ؟!
    خوب پس ....
    استفاده از FileStream به جای MemoryStream برای نوشتن فایل به صورت بایت بایت زمان زیادی میگیره . برای همین ما اطلاعات رو ابتدا در Memory مینویسیم که سرعت بیشتری داره و بعد طی زمان های مشخص شده قسمتی از اطلاعات ذخیره شده در حافظه رو بر روی فایل می نویسیم و حافظه اختصاص یافته رو پس میدیم . این کار تا رسیدن به پایان فایل ادامه پیدا میکنه .

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

    در مورد كد زير هم من چيزي نفهميدم :
    اول یک سری اطلاعات در مورد فایل برای سرور ارسال میکنه و نهایتاً خود فایل با متد WriteFile ارسال میشه . البته این کد در عمل و مخصوصاً زمانیکه سرور بخواد از یک یا چند کلاینت به طور همزمان یک یا چند فایل دریافت کنه قابل استفاده نیست .
    آخرین ویرایش به وسیله مهران رسا : یک شنبه 06 تیر 1389 در 11:27 صبح

  37. #37

    نقل قول: ارسال فايل حجيم با indy

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

  38. #38

    نقل قول: ارسال فايل حجيم با indy

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

  39. #39

    نقل قول: ارسال فايل حجيم با indy

    اما خوب فعلا باز یک مشکل جدید دارم .
    فردا هم قراره تحویل بدم پروژم رو . البته فعلا یادگیری مهمتره .
    میخوام برنامه من قابلیت چت مثل یاهو رو داشته باشه به عبارتی بتونه در یک زمان با 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;

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

  40. #40

    نقل قول: ارسال فايل حجيم با indy

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

صفحه 1 از 2 12 آخرآخر

قوانین ایجاد تاپیک در تالار

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