[Delphi][WinAPI] Aplikacja sieciowa, wiele połączeń na raz

0

Witam.

Na wstępie zaznaczam, że piszę w WinAPI !

Od dłuższego czasu piszę sobie program, przeznaczenia jego jednak na razie nie chcę zdradzić bo nie wiem czy go skończę :)
Większość interfejsu już zrobiłem. Przyszła pora na sieć. No i nie wiem jak to zaprogramować.
Może wyjaśnię:
Program posiada kontrolkę ListView, do której użytkownik wrzuca linki.
Następnie użytkownik klika sobie opcję "Sprawdź linki".
Aplikacja powinna łączyć się z serwerem i uzyskać informacje:

  • czy plik istnieje
  • jaki jest jego rozmiar
    a następnie, w osobnej kolumnie te informacje wyświetlić.
    Kod HTML serwera rozpykałem. Zapytania POST to też na razie nie problem.
    Zastanawia mnie jak wykonać połączenia.

Najpierw wymyśliłem, żeby zrobić tak:
Po kliknięciu na "Sprawdź linki" odpalam nowy wątek.
Wątek ten z każdego linku wyodrębnia:

  • adres hosta
  • ścieżkę do pliku
    a następnie odpala wątek przekazując mu te informacje.
    Nowy wątek wykonuje połączenie, odbiera dane od serwera, analizuje je i aktualizuje ListView po czym się wyłącza. (Oczywiście wątek używa socketów blokujących).
    Przy czym użytkownik może wybrać ile maksymalnie wątków może być odpalonych.
    Jeśli maksymalna liczba wątków została osiągnięta to w pierwszym wątku sprawdzam, czy któryś wątek:
  • przekroczył dozwolony czas (timeout dla connect, lub timeout dla (connect + czas transferu) )
  • zakończył się

Uchwyty wątków, czas ich utworzenia itd. trzymam w tablicy dynamicznej (maks. ilość zależna od tego ile maks. wątków zdefiniował user).
Dalszych szczegółów nie będę opisywał. Dodam tylko, że w tym sposobie wychodzi masa kodu i zawiłości i nie udało mi się go wykonać prawidłowo. W dodatku taka ilość wątków to chyba nie jest dobry pomysł.

Jak typowe menedżery pobierania, przeglądarki itd. realizują kilkanaście (kilkadziesiąt) połączeń na raz?
Nie wydaje mi się, żeby wykorzystywały taką ilość wątków.
Może powinienem użyć socketów nie-blokujących?

Jak wy byście zrealizowali takie zadanie?
Może ma ktoś jakiś sprawdzony "schemat ideowy"?
Z pobieraniem docelowego pliku to już mniejszy problem, bo będę pobierał tylko jeden na raz.
A, gdyby kogoś dziwiło, czemu chcę sprawdzać kilka linków na raz zamiast jednego - myślę, że tak będzie sprawdzało szybciej na szybszych łączach.

Ostatnia sprawa, jak wiadomo, podczas ściągania pliku, będę musiał na bieżąco szukać określonych fragmentów kodu HTML, jak już znajdę wszystko co potrzebuję to rozłączę się (bo po co pobierać niepotrzebne dane).
Jak zrobić zarządzanie pamięcią dla pobieranych danych?
Bo przecież nie będę zapisywał do pliku, żeby za chwilę go odczytywać :)

0

@wiele polaczen bez wielu watkow - patrz asynchroniczne api socketow - np. http://www.gamedev.net/reference/programming/features/asyncsock/
i drobna uwaga - nie sa to sockety nieblokujace. to cos zupelnie innego:) ale, jakby sie uprzec, nonblocking tez mozna uzyc, tylko wtedy wiecej trzeba robic od reki

@szybciej na szybszych - tak, dokladnie tak. jesli nie przegniesz z iloscia polaczen jednoczesnych to bedzie szybciej

@pamiec - zaleznie od wielkosci pliku. jesli maly - mozesz go sobie trzymac w pamieci chocby w vector<char>. jak duzy - pare mega/giga to o wybacz, ale naprawde lepiej trzymac go na dysku w jakims tempie..

0
quetzalcoatl napisał(a)

drobna uwaga - nie sa to sockety nieblokujace. to cos zupelnie innego:)

Kurcze, ja tam nie widzę równicy. Ale może coś skumam jak się skupię bardziej przy czytaniu tego tekstu, bo póki co niewiele rozumiem bo jest pisany językiem potocznym :)

quetzalcoatl napisał(a)

ale, jakby sie uprzec, nonblocking tez mozna uzyc, tylko wtedy wiecej trzeba robic od reki

Co konkretnie masz na myśli?

quetzalcoatl napisał(a)

@szybciej na szybszych - tak, dokladnie tak. jesli nie przegniesz z iloscia polaczen jednoczesnych to bedzie szybciej

Nie wiem czy się dobrze zrozumieliśmy. Chcę zrobić wiele połączeń, ale w sensie, że dla każdego linku będzie JEDNO, ale kilka/kilkanaście linków będzie sprawdzanych jednocześnie - tak będzie szybciej, czy musiałbym zrobić dla każdego linku kilka/kilkanaście połączeń tak, jak robią np. przeglądarki internetowe wysyłając do serwera domyślnie 8 żądań.

quetzalcoatl napisał(a)

@pamiec - zaleznie od wielkosci pliku. jesli maly - mozesz go sobie trzymac w pamieci chocby w vector<char>. jak duzy - pare mega/giga to o wybacz, ale naprawde lepiej trzymac go na dysku w jakims tempie..

To tylko plik HTML, od kilkunastu do kilkudziesięciu KB.
Dopiero docelowy plik (który user chce pobrać z serwera) będzie zapisywany na dysku.
Mnie chodziło raczej jakie funkcje użyć do zapisu w pamięci.
Piszę to w Delphi.
Globalnie alokować pamięć, czy może użyć GetMem, FreeMem, czy inne funkcje, czy może wrzucać do stringa/tablicy dynamicznej albo jeszcze inaczej?
Muszę to zrobić tak, aby działało to było sensownie zoptymalizowane i tak, bym mógł na bieżąco badać otrzymany kod HTML.
Może dynamiczna tablica PAnsiChar, gdzie każdy element jest dla danych otrzymywanych na konkretnym połączeniu?

0
Goodrock napisał(a)
quetzalcoatl napisał(a)

drobna uwaga - nie sa to sockety nieblokujace. to cos zupelnie innego:)

Kurcze, ja tam nie widzę równicy.(...)

np.

  • socket nonblocking jesli kazesz mu odebra tablice 1024000 bajtowa, odbierze cos pomiedzy 0<N<32000 bajtow i natychmiast sie zakonczy, sterowanie wroci do Ciebie i sobie robisz cos innego, a socket caly szczesliwy ze juz wiecej roboty nie ma.
  • socketasync ktory dostal rozkaz odebrania tablicy 1024000 bajtow rozpocznie odbieranie i sterowanie wroci do Ciebie i sobie robisz cos innego. socket pamieta ze ma robote i ze ma jej tyle-a-tyle. jak odbieranie sie zakonczy, socket -jakos- Ciebie poinforumuje ze wlasnie skonczyl prace.
Goodrock napisał(a)
quetzalcoatl napisał(a)

ale, jakby sie uprzec, nonblocking tez mozna uzyc, tylko wtedy wiecej trzeba robic od reki

Co konkretnie masz na myśli?

patrz wyzej. skoro socket nonblock moze np. stwierdzic ze teraz nic nie zrobi ot bo tak, to musisz utrzymywac bufory i pamietac ktory sock co ma zrobic, ile zrobil itp. poza tymmusisz albo badac ktory sock jest w stanie cos teraz zrobic, albo tez co chwila przelatywac po wszystkich i probowac je szturchac. w async po prostu zostaniesz poinformowany

Goodrock napisał(a)
quetzalcoatl napisał(a)

@szybciej na szybszych - tak, dokladnie tak. jesli nie przegniesz z iloscia polaczen jednoczesnych to bedzie szybciej

mialem na mysli, ze masz racje, w ten sposob bedzie szybciej - ale tylko pod warunkiem (...). gdyz jak przesadzisz to po prostu zapchasz albo lacze, albo winsocka.. chociaz lacze predzej

Goodrock napisał(a)

To tylko plik HTML, od kilkunastu do kilkudziesięciu KB.

IMHO, kilkadziesiat kb to mozesz przy paru plikach, to i w pojedynczych stringach mozesz trzymac. w tym miejscu wpierw skupilbym sie na tym, zeby odbierac te html'e poprawnie i przetwarzac poprawnie, a szybkoscia tego zajmij sie potem. szczerze - szybkosc ich analizowania i tak w 90% bedzie zalezala od tego JAK bedziesz je analizowal, a nie w CZYM /tablicy, stringu, etc/

0

Dobra pokombinuje w wolnych chwilach. Może jeszcze się odezwę jak będę miał problemy lub pytania.
Tymczasem dzięki za wskazówki.

0

Robię double posta żeby ktoś tu zajrzał :)

Mam taki dziwny problem - po zakończeniu odbierania danych i zamknięciu wszystkich socketów w Menedżerze Zadań widzę, że jest o jeden wątek za dużo - chyba to jest jakiś wątek utworzony przez Winsock.
Co może być przyczyną? Konkretniej - czemu wątek nie zostaje zamknięty?

Połączenia zakańczam po odebraniu komunikatu FD_CLOSE za pomocą closesocket().

Kontrukcja połączeń wygląda tak:
1.) W PopupMenu (od ListView) klikam "Sparawdź linki... -> Wszystkie".
2.) Odpalam nowy wątek (aby odciążyć procedurę obłsugi komunikatów).
3.) Wątek wstawia napis "Sprawdzanie..." w odpowiednie komórki ListView (styl LVS_REPORT).
4.) Po ustawieniu napisów rozpoczyna wykonywanie połączeń.
5.) Po wykonaniu połączeń wątek wyłącza się [ExitThread(0);]
6.) Bez względu na to czy wątek się już zakończył, czy też nie - okno typu Message-Only zajmuje się odbieraniem i analizowaniem danych, zwalnianiem buforów oraz zamykaniem połączeń. W zależności od tego czy plik istnieje na serwerze, czy nie - wstawia odpowiedni napis do odpowiednich komórek w ListView. Podczas FD_CLOSE sprawdza czy wątek się już zakończył. Jeśli tak to wykonuje CloseHandle i w razie powodzenia zeruje zmienną przechowującą uchwyt wątku.

1 użytkowników online, w tym zalogowanych: 0, gości: 1