การเชื่อมต่อไม่หมดเวลาขณะดาวน์โหลดไฟล์จากอินเทอร์เน็ต

เกี่ยวข้องกับโพสต์ของฉัน ( วิธีดึงไฟล์จากอินเทอร์เน็ต ผ่าน HTTP? ) เกี่ยวกับวิธีดาวน์โหลดไฟล์จากอินเทอร์เน็ตอย่างง่ายดายและมีประสิทธิภาพ ฉันพบวิธีแก้ไขที่เป็นไปได้ - อย่างไรก็ตามไม่ทำงานอย่างที่ควรจะเป็น

ตามเอกสารของ MS รหัสด้านล่างควรจะหมดเวลา 500 มิลลิวินาที หลังจากที่ฉันตัดการเชื่อมต่อจากอินเทอร์เน็ต อย่างไรก็ตาม ดูเหมือนว่าจะละเว้นการตั้งค่า 'INTERNET_OPTION_RECEIVE_TIMEOUT' โดยสิ้นเชิง แอปพลิเคชันค้างระหว่างการดาวน์โหลด ฟังก์ชันนี้ใช้เวลาประมาณ 20-30 นาทีจึงจะทราบว่าการเชื่อมต่ออินเทอร์เน็ตขัดข้อง และเพื่อให้การควบคุมกลับสู่ GUI

มีใครรู้บ้างว่าทำไม?

function GetBinFileHTTP (const aUrl: string; const pStream: TStream; wTimeOut: Word= 500; wSleep: Word= 500; wAttempts: Word= 10): Integer;
CONST
  BufferSize = 1024;
VAR
  hSession, hService: HINTERNET;
  Buffer     : array[0..BufferSize-1] of Char;
  dwBytesRead, dwBytesAvail: DWORD;
  lSucc        : LongBool;
  lRetries, dwTimeOut: Integer;   
begin
 Result:= 0;
 if NOT IsConnectedToInternet then
  begin
   Result:= -1;
   EXIT;
  end;

 hSession := InternetOpen(PChar(ExtractFileName(Application.ExeName)), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);  { The INTERNET_OPEN_TYPE_PRECONFIG flag specifies that if the user has configured Internet Explorer to use a proxy server, WinInet will use it as well. }
 if NOT Assigned(hSession) then
  begin
   Result:= -4;
   EXIT;
  end;

 TRY
   hService := InternetOpenUrl(hSession, PChar(aUrl), nil, 0, INTERNET_FLAG_RELOAD, 0);
   if NOT Assigned(hService) then Exit;
   TRY
     FillChar(Buffer, SizeOf(Buffer), 0);

     { Set time out }
     dwTimeOut:= wTimeOut;
     InternetSetOption(hService, INTERNET_OPTION_RECEIVE_TIMEOUT, @dwTimeOut, SizeOf(dwTimeOut));   { use INTERNET_FLAG_RELOAD instead of NIL to redownload the file instead of using the cache }


     InternetSetOption(hService, INTERNET_OPTION_CONNECT_TIMEOUT, @dwTimeOut, SizeOf(dwTimeOut));

     REPEAT
       lRetries := 0;

       REPEAT
         lSucc:= InternetQueryDataAvailable( hService, dwBytesAvail, 0, 0);
         if NOT lSucc
         then Sleep( wSleep );
         if lRetries > wAttempts
         then Result:= -2;
       UNTIL lSucc OR (Result= -2);

       if NOT InternetReadFile(hService, @Buffer, BufferSize, dwBytesRead) then
        begin
          Result:= -3;                                                          { Error: File not found/File cannot be downloaded }
          EXIT;
        end;

       if dwBytesRead = 0
       then Break;

       pStream.WriteBuffer(Buffer[0], dwBytesRead);

     UNTIL False;
   FINALLY
     InternetCloseHandle(hService);
   end;
 FINALLY
   InternetCloseHandle(hSession);
 end;

 Result:= 1;
end;

นี่คือเอกสาร:

{

INTERNET_OPTION_CONNECT_TIMEOUT         Sets or retrieves an unsigned long integer value that contains the time-out value to use for Internet connection requests. If a connection request takes longer than this time-out value, the request is canceled. When attempting to connect to multiple IP addresses for a single host (a multihome host), the timeout limit is cumulative for all of the IP addresses. This option can be used on any HINTERNET handle, including a NULL handle. It is used by InternetQueryOption  and InternetSetOption.
INTERNET_OPTION_RECEIVE_TIMEOUT         Sets or retrieves an unsigned long integer value that contains the time-out value to receive a response to a request.      If the response takes longer than this time-out value, the request is canceled. This option can be used on any HINTERNET handle, including a NULL handle. It is used by InternetQueryOption and InternetSetOption. For using WinInet synchronously, only the default value for this flag can be changed by calling InternetSetOption and passing NULL in the hInternet parameter.
                  INTERNET_OPTION_CONTROL_RECEIVE_TIMEOUT - Identical to INTERNET_OPTION_RECEIVE_TIMEOUT. This is used by InternetQueryOption and InternetSetOption.
}

แก้ไข: ฉันตัดการเชื่อมต่ออินเทอร์เน็ตโดยการถอดสายเคเบิลหรือ (สำหรับไร้สาย) ออกจากซอฟต์แวร์หลังจากแอปพลิเคชันเริ่มการดาวน์โหลด (ฉันเลือกดาวน์โหลดไฟล์ขนาดใหญ่) มันจำลองเว็บไซต์ออฟไลน์


person Z80    schedule 28.06.2010    source แหล่งที่มา
comment
คุณไม่ดีใจหรือที่ไม่ได้ทำสิ่งที่ซับซ้อนโดยใช้ Indy?   -  person Rob Kennedy    schedule 28.06.2010
comment
เพียงเพื่อชี้แจง. ฉันไม่ได้บอกว่าอินดี้ไม่ดี! มันมากเกินไปสำหรับสิ่งที่ฉันต้องการ ฉันต้องการที่จะนำแอปพลิเคชันติดตัวไปด้วยและคอมไพล์บนคอมพิวเตอร์ที่เปิดใช้งาน Delphi เครื่องใดก็ได้ การพกพาไม่ใช่เรื่องแย่ใช่ไหม?   -  person Z80    schedule 28.06.2010
comment
หากต้องการตอบคำถามของคุณเอง ให้คลิกปุ่มตอบคำถามของคุณที่ด้านล่างของหน้านี้ จากนั้นพิมพ์คำตอบของคุณเหมือนกับที่คุณทำสำหรับคำถามอื่นๆ บนไซต์ หลังจากช่วงรอ คุณสามารถทำเครื่องหมายว่าเป็นคำตอบที่ยอมรับได้   -  person Rob Kennedy    schedule 29.06.2010
comment
ฉันไม่ชอบอินดี้ ใหญ่โตและมักจะมีปัญหาความเข้ากันได้แบบย้อนหลังครั้งใหญ่   -  person Z80    schedule 10.06.2014


คำตอบ (4)


เห็นได้ชัดว่าการหมดเวลาการเชื่อมต่อไม่เกี่ยวข้องกับการทดสอบของคุณ เนื่องจากเมื่อคุณเริ่มการทดสอบ (เช่น ดึงปลั๊ก) การเชื่อมต่อได้ถูกสร้างขึ้นแล้ว แน่นอนว่าการเชื่อมต่อได้ถูกสร้างขึ้นแล้วก่อนที่คุณจะตั้งค่าตัวเลือกการหมดเวลาด้วยซ้ำ

ความถูกต้องของการหมดเวลารับยังเป็นที่น่าสงสัย เนื่องจากคุณเริ่มได้รับการตอบกลับแล้วเช่นกัน

การหมดเวลาที่ดูมีแนวโน้มมากที่สุดคือการหมดเวลาของการตัดการเชื่อมต่อ แต่ MSDN บอกว่ายังไม่ได้นำมาใช้

สำหรับฉันแล้วดูเหมือนว่าวิธีที่จะไปคือการใช้การดำเนินการ อะซิงโครนัส ใช้ InternetReadFileEx และใช้แฟล็ก irf_Async และ irf_No_Wait หากผ่านไปนานเกินไปโดยไม่ได้รับข้อมูลใดๆ ให้ปิดการเชื่อมต่อ อีกทางเลือกหนึ่งคือให้ใช้การโทรแบบซิงโครนัสต่อไป แต่ให้โทร InternetCloseHandle จากเธรดอื่น หากการดาวน์โหลดใช้เวลานานเกินไป

person Rob Kennedy    schedule 28.06.2010
comment
ฉันตัดการเชื่อมต่ออินเทอร์เน็ตโดยการถอดสายเคเบิลหรือ (สำหรับระบบไร้สาย) ออกจากซอฟต์แวร์ หลังจากที่แอปพลิเคชันเริ่มการดาวน์โหลด (ฉันเลือกดาวน์โหลดไฟล์ขนาดใหญ่) มันจำลองเว็บไซต์ออฟไลน์ - person Z80; 28.06.2010
comment
ขวา. หากการดาวน์โหลดเริ่มต้นขึ้น แสดงว่าการเชื่อมต่อได้ถูกสร้างขึ้นแล้ว และการตอบกลับได้เริ่มต้นขึ้นแล้ว ดังนั้น การหมดเวลาการเชื่อมต่อจึงไม่เกี่ยวข้องอีกต่อไป และการหมดเวลาการรับก็เช่นกัน - person Rob Kennedy; 29.06.2010
comment
ดังนั้น โดยพื้นฐานแล้วจะไม่มีการหมดเวลาในไลบรารีนั้น (หลังจากการดาวน์โหลดเริ่มต้น) - person Z80; 29.06.2010

มีข้อผิดพลาดที่บันทึกไว้ในโค้ด MS IE สามารถแก้ไขได้โดยใช้โค้ดในเธรดและนำกลไกการหมดเวลาไปใช้ใหม่เท่านั้น

รายละเอียด:

"แนวทางนี้แสดงวิธีแก้ไขข้อบกพร่องของ InternetSetOption API ในการตั้งค่าการหมดเวลาโดยการสร้างเธรดที่สอง InternetSetOption ไม่ได้ตั้งค่าการหมดเวลา"

http://support.microsoft.com/default.aspx?scid=kb;en-us;Q224318
(รายงานลิงก์เสียหาย ตำหนิ MS ไม่ใช่ฉัน)

บางทีอาจมีบางคนสามารถช่วยดำเนินการแก้ไขข้อบกพร่องนี้ใน Delphi ได้เช่นกัน โดยส่วนตัวแล้วฉันไม่มีประสบการณ์กับ C แม้แต่กระดูกสันหลังในรูปแบบ pseudo-Pascal ก็ยังดี

person Z80    schedule 28.06.2010
comment
มีลิงก์ไปยังเอกสารนั้นหรือไม่? - person Rob Kennedy; 29.06.2010
comment
สวัสดี Rob ดูลิงก์ที่ฉันเพิ่งเพิ่ม - person Z80; 29.06.2010
comment
ดูเหมือนคำแนะนำสุดท้ายในคำตอบของฉัน แทนที่จะรอการหมดเวลาและเรียก InternetCloseHandle ในเธรดที่แยกจากกัน บทความ KB จะตั้งค่าการเชื่อมต่อในเธรดอื่น โค้ดตัวอย่างเป็นการเรียก API เกือบทั้งหมด ส่วนไหนที่ทำให้คุณเดือดร้อน? - person Rob Kennedy; 29.06.2010
comment
ขอโทษ. มันใช้งานได้จนถึงตอนนี้ แต่คุณคงทราบดีว่า Microsoft มักจะย้ายสิ่งต่าง ๆ (หน้า) เป็นจำนวนมาก คุณได้ลองค้นหาข้อความที่ฉันใส่เครื่องหมายคำพูดใน Google แล้วหรือยัง? - person Z80; 29.01.2014
comment
PS: บางทีพวกเขาอาจแก้ไขข้อบกพร่องใน IE 11 ใหม่แล้วจึงลบบทความออก - person Z80; 29.01.2014

IMO คุณควรเรียกใช้สิ่งนี้ในเธรด การทำเธรดไม่จำเป็นต้องหมายถึงการวนซ้ำ - อาจเป็นเธรดที่ "เป็นหนึ่งเดียวและเสร็จสิ้น" ก็ได้ เรียกใช้ในลักษณะนั้น และ GUI ของคุณจะยังคงตอบสนองจนกว่าเธรดจะเสร็จสิ้น ฉันรู้ว่านี่ไม่ได้ตอบคำถามของคุณจริงๆ แต่จะทำให้โค้ดของคุณดีขึ้น

นอกจากนี้ หากคุณตัดการเชื่อมต่ออินเทอร์เน็ตในช่วงแรกที่คุณกำลังตรวจสอบข้อมูล ฉันคิดว่าจะลองอีกครั้ง 10 ครั้ง ควรตรวจพบแล้วเลิกทันที

สุดท้ายนี้ ฉันไม่คิดว่าคุณควรใช้ EXIT เมื่อคุณมีที่จับและของต่างๆ ที่เปิดอยู่ พังแทน เพื่อที่คุณจะได้ยังคงวิ่งผ่านการตัดการเชื่อมต่อ ฉันคาดหวังว่ารหัสของคุณจะผูกเข้ากับซ็อกเก็ต ฉันเห็นสิ่งนี้เมื่อเร็ว ๆ นี้ในระหว่างการตรวจสอบโค้ดเมื่อมี EXIT แทน BREAK และมันทำให้หน่วยความจำรั่วเนื่องจากวัตถุถูกสร้างขึ้นและไม่เคยถูกปล่อย ฉันจะใช้กฎเดียวกันที่นี่

person Chris Thornton    schedule 28.06.2010
comment
-1. ไม่ตอบคำถาม. นอกจากนี้ คุณจะรู้ได้อย่างไรว่าโค้ดนี้ไม่ได้ พร้อมแล้ว ทำงานในเธรดแยกต่างหาก - person Rob Kennedy; 28.06.2010
comment
@Chris ฉันคิดว่าทางออกอาจจะใช้ได้ที่นี่เนื่องจาก OP ใช้ try... ในที่สุดเพื่อปิดการเชื่อมต่อ - person Marcus Adams; 28.06.2010
comment
@Rob - เขาบอกว่าแอปของเขาค้าง ดังนั้นฉันคิดว่าเขาไม่ได้เรียกใช้สิ่งนี้ในเธรด - person Chris Thornton; 28.06.2010
comment
ไม่ ฉันไม่ได้รันมันในเธรด ฉันต้องการทำเช่นนั้น แต่ฉันไม่แน่ใจว่าโค้ดนั้นปลอดภัยสำหรับเธรดอย่างไร - person Z80; 28.06.2010
comment
@Andreas - Exit ใช้งานได้จริงเหรอ? ฉันคิดว่ามันเพิ่งออกจากฟังก์ชัน/ขั้นตอน ถ้ามันถึงจุดสุดยอดฉันก็โอเค ฉันเดาว่าฉันดู Exit นั้นไม่ถูกต้องมาเป็นเวลานานแล้ว! - person Chris Thornton; 29.06.2010
comment
@คริส ธอร์นตัน: ใช่ ลองด้วยตัวเอง! try Exit; finally ShowMessage('test'); end; - person Andreas Rejbrand; 29.06.2010
comment
@Altar คุณสามารถใช้เธรดเดี่ยวได้แม้ว่าจะไม่ปลอดภัยสำหรับเธรดก็ตาม - person Marcus Adams; 29.06.2010
comment
+1 จากฉันเพื่อแก้ไขการโหวตเชิงลบ คำตอบก็ดี ไม่มีเหตุผลที่จะ downvoted - person Z80; 29.01.2014

คุณแน่ใจหรือว่าไม่ได้เข้าสู่ INTERNET_OPTION_CONNECT_TIMEOUT มันจะลองเชื่อมต่อก่อนแล้วค่อยรับ

หากต้องการทดสอบการหมดเวลาการเชื่อมต่อ จะต้องแก้ไข แต่อย่าเชื่อมต่อเลย เพื่อทดสอบการหมดเวลาการอ่าน จะต้องเชื่อมต่อ แต่ไม่เคยได้รับข้อมูลใดๆ

โดยทั่วไป ฉันตั้งค่าระยะหมดเวลาการเชื่อมต่อไว้ที่ 10 วินาที และระยะหมดเวลาการอ่านอยู่ที่ 30 วินาที อะไรที่ยาวกว่านั้นผมถือว่าลงอยู่แล้ว

person Marcus Adams    schedule 28.06.2010
comment
คุณแน่ใจหรือว่าไม่ได้เข้าสู่ INTERNET_OPTION_CONNECT_TIMEOUT มันจะลองเชื่อมต่อก่อนแล้วค่อยรับ -ชัวร์!! ฉันได้ทำการทดสอบบางอย่างแล้ว - person Z80; 28.06.2010