Programowanie dla sieci Internet
Transkrypt
Programowanie dla sieci Internet
MICROSOFT FOUNDATION CLASSES
PROGRAMOWANIE DLA SIECI INTERNET
Biblioteka MFC pozwala na wykorzystanie możliwości Internetu w rozmaity sposób. Na najwyższym poziomie abstrakcji można wskazać aplikacje korzystające z widoku przeglądarki internetowej (CHtmlView),
które w oparciu o kontrolkę ActiveX implementują właściwie pełną przeglądarkę WWW. Na niższym poziomie znajduje się klasa CInternetSession, pozwalająca na obsługę połączeń w protokołach ftp czy http.
Wreszcie na najniższym poziomie wymienić należy gniazda (sockets), które mogą być przydatne do bezpośredniej komunikacji pomiędzy dwoma komputerami z wykorzystaniem protokołu TCP/IP. Właśnie takim
rozwiązaniom poświęcono tu najwięcej miejsca.
KLASA CINTERNETSESSION I OTWIERANIE POŁĄCZEŃ
Klasa CInternetSession reprezentuje sesję połączenia z Internetem. W ramach sesji można otwierać połączenia (np. za pomocą funkcji składowych GetFtpConnection, GetHttpConnection, GetGopherConnection).
Poniżej przedstawiono fragment kodu, w którym, w ramach sesji połączenia z Internetem, otwierane jest
połączenie FTP (obiekt klasy CFtpConnection) w celu ściągnięcia określonego pliku z określonego serwera.
Dodano konstrukcję try – catch aby skutecznie wykryć błędy:
CInternetSession session;
CFtpConnection *pCon;
try
{
pCon=session.GetFtpConnection("host.domena.pl", "user", "hasło");
if (pCon)
{
pCon->GetFile("optical.exe", "c:\\optical.exe");
pCon->Close();
}
delete pCon;
}
catch (CInternetException *e)
…
GNIAZDA INTERNETOWE
Gniazda (oryginalnie stworzone dla systemu Unix) stały się powszechnie przyjmowanym standardem komunikacji w sieciach typu TCP/IP. W MFC reprezentowane są jako obiekty klasy CSocket (lub CAsynchSocket). Obiekty
te pozwalają wysyłać i odbierać dane poprzez sieć. Należy zauważyć, że w każdym takim przesyle wyróżnić
można stronę serwera, którego zadaniem jest nasłuchiwanie sieci i wykrywanie prób dostępu ze strony klientów,
oraz stronę klientów, którzy po prostu łączą się z serwerem.
OTWIERANIE GNIAZDEK PO STRONIE KLIENTA
Otwieranie gniazdek po stronie klienta jest dość proste:
CSocket socket;
//
socket.Create();
//
if (m_socket.Connect("host", 100)) //
// połączenie nawiązane
else
// przekroczenie czasu lub inny
utworzenie gniazdka (konstruktor)
utworzenie gniazdka (druga faza)
próba nawiązania połączenia
błąd
Parametry funkcji CSocket::Connect służącej do nawiązania połączenia to adres serwera i numer portu. Jeśli serwer w danej chwili nie nasłuchuje łącza, funkcja Connect zwróci sterowanie z wynikiem FALSE. Zauważmy, że
numer portu jest parametrem połączenia (nawiązujemy połączenie po porcie 100), a nie samego gniazdka.
OTWIERANIE GNIAZDEK PO STRONIE SERWERA
Otwieranie gniazdek po stronie serwera jest tylko trochę bardziej skomplikowane:
CSocket hostsocket;
m_hostsocket.Create(100);
m_hostsocket.Listen();
// utworzenie gniazdka do nasłuchu
// w II fazie określa się numer portu
// włączenie nasłuchu
CSocket socket;
// utworzenie gniazdka do połączenia
if (m_hostsocket.Accept(m_socket)) // próba przyjęcia połączenia
// połączenie od klienta odebrane
else
// błąd
Dlaczego tworzymy aż dwa gniazdka? Pierwsze z nich (hostsocket) jest przeznaczone do nasłuchu i ma na stałe
określony numer portu (w funkcji Create). Gdyby na tym poprzestać, to znaczy wykorzystać to gniazdko do
transmisji danych, oznaczałoby to blokadę serwera na czas trwania tej transmisji – gniazdko bowiem nie mogłoby
dalej nasłuchiwać. Dlatego połączenia przychodzące od klienta są przyjmowane do innego gniazdka (socket),
które dynamicznie wiązane jest z innym portem, nie zakłócając w ten sposób nasłuchu serwera.
NADAWANIE I ODBIERANIE DANYCH POPRZEZ GNIAZDKA
Po otwarciu i przyjęciu połączenia oba gniazdka – po stronie klienta i serwera – mogą się komunikować. Służą do
tego celu niskopoziomowe funkcje Send i Receive. Jednak biblioteka MFC dostarcza inne, znacznie wygodniejsze
rozwiązanie. Można utworzyć skojarzony z gniazdkiem plik (CSocketFile), a następnie (jak dla wszystkich plików CFile) – obiekt archiwum CArchive, co pozwala na wykorzystanie operatorów << i >> do przesyłania dowolnych danych, w tym – zorganizowanych jako obiekty (z przeciążonymi operatorami do obsługi CArchive).
// plik związany z gniazdkiem
CSocketFile file(&m_socket);
// wysyłanie danych
CArchive ar(&file, CArchive::store);
ar << str;
// odbiór danych
CArchive ar(&file, CArchive::load);
ar >> str;
IMPLEMENTACJA TRANSMISJI W OSOBNYM WĄTKU
Operacje na gniazdkach, takie jak nawiązywanie czy odbieranie połączeń, ale przede wszystkim odbiór wiadomości, przejmują sterowanie sprawiając wrażenie, iż aplikacja się zawiesiła. Prostym rozwiązaniem jest umieszczenie obsługi gniazda w osobnym wątku. Należy zwrócić uwagę na fakt, iż nie należy do funkcji wątkowej przekazywać wskaźników na obiekty MFC, zamiast tego przesyłając uchwyty:
AfxBeginMessage(ReceiveFunc, socket.m_hSocket);
UINT ReceiveFunc(LPVOID p)
//
{
CSocket socket;
//
socket.Attach((SOCKET)p);
//
CSocketFile file(&socket);
CArchive ar(&file, CArchive::load);
CString str;
while (1)
//
{
//
ar >> str;
//
// konsumpcja danych
}
return 0;
}
funkcja ciągłego odbioru danych
gniazdko...
...dołączone do uchwytu
pętla nieskończona – w rzeczywistym
rozwiązaniu należałoby określić
warunek zakończenia