Sprawozdanie - Komunikator w C#
Transkrypt
Sprawozdanie - Komunikator w C#
PAŃSTWOWA WYśSZA SZKOŁA ZAWODOWA
W ELBLĄGU
INSTYTUT INFORMATYKI STOSOWANEJ
Sprawozdanie
Komunikator internetowy w C#
Artur Domachowski
autor:
Elbląg, 2009 r.
Komunikacja przy uŜyciu poczty internetowej jest najbardziej powszechną i
ogólnodostępną formą komunikacji poprzez Internet. Pozwala ona na przesyłanie wiadomości
pomiędzy uŜytkownikami, którzy posiadają konta na róŜnych serwerach. Taka forma
komunikacji zaspokaja wiele potrzeb, jednakŜe nie wszystkie. Mimo iŜ wiadomości
przekazywane są przewaŜnie bardzo szybko to prowadzenie rozmowy w czasie rzeczywistym
jest niemoŜliwe. Powstały więc aplikacje sieciowe pozwalające na rozmowę tekstową w
czasie rzeczywistym. Komunikatory internetowe (ang. instant Messenger, IM) pozwalają
komunikować się poprzez natychmiastowe przesyłanie wiadomości między uŜytkownikami.
Tylko one dają namiastkę prawdziwej rozmowy w formie tekstowej. Dla uŜytkownika
przesyłanie wiadomości jest proste i wygodne, a co najwaŜniejsze natychmiastowe.
Komunikator internetowy oparty o architekturę klient - serwer składa się z aplikacji klienckiej
oraz serwerowej. KaŜdy klient łączy się z serwerem, a ten z kolei zbiera informacje o
klientach i udostępnia innym klientom. Serwer pośredniczy takŜe w przekazywaniu
informacji między klientami.
Komunikator internetowy Domacho działa w oparciu o architekturę klient - serwer.
Serwer tego komunikatora jest wielowątkową aplikacją konsolową, napisaną w języku
C#(csharp). Dzięki wykorzystaniu protokołu TCP/IP, przesyłanie wiadomości moŜliwe jest w
sieciach lokalnych (LAN, local area network) jak i przy uŜyciu sieci rozległych (WAN, wide
area network). Serwer komunikatora wykorzystuje bazę danych Microsoft SQL Server 2005
do przechowywania informacji o klientach oraz zapisywania wiadomości do niedostępnych
uŜytkowników. Całość działa pod kontrolą systemu operacyjnego Microsoft Windows 2003
R2 Enterprise Edition with SP2. Do prawidłowego funkcjonowania aplikacja serwerowa jak i
kliencka wymaga zainstalowanego środowiska Microsoft .NET Framework w wersji 2.0 lub
wyŜszej.
Serwer komunikatora Domacho akceptuje połączenia na porcie 8000. Po połączeniu
się z klientem wysyła Ŝądanie przesłania nazwy uŜytkownika oraz hasła. Hasło przekazywane
jest w postaci zaszyfrowanego kodu, do szyfrowania uŜyto algorytmu kryptograficznego
SHA1. Podczas logowania serwer porównuje otrzymany zaszyfrowany kod z tym zapisanym
w bazie danych. Po zalogowaniu wszystkie dane dotyczące klienta, połączenia z nim oraz z
wszystkimi innymi klientami przekazywane są do osobnego wątku. KaŜdemu zalogowanemu
klientowi odpowiada jeden wątek w serwerze.
2
Rys1. Klasy w kodzie źródłowym serwera komunikatora Domacho, opracowanie własne.
Podstawę do przekazywania informacji z klasy głównej Serwer do klasy Sesja stanowi
struktura clientinfo. Przechowuje ona dwie wartości: pierwszą z nich jest ciąg znaków
odpowiadający nazwie uŜytkownika, drugą zaś jest obiekt klasy TcpClient, który przechowuje
wszystkie informacje dotyczące połączenia. Klasa TcpClient jest elementem środowiska
.NET Framework.
Hashtable klienci = new Hashtable();
.
.
.
struct clientInfo
{
public String login;
public TcpClient tcpclient;
}
KaŜdy nowo utworzony obiekt struktury jest dodawany do tablicy Hashtable o nazwie klienci,
która jako klucz przyjmuje nazwę uŜytkownika, a jako wartość obiekt. Tablica Hashtable
deklarowana jest poza klasą serwer.
3
clientInfo clientinf = new clientInfo(); //tworzenie obiektu struktury
clientinf.login = login;
clientinf.tcpclient = client;
klienci.Add(clientinf.login, clientinf); // dodawanie do Hashtable
Do nowoutworzonego wątku przekazywane są wszystkie niezbędne dane do obsługi
połączenia z klientem komunikatora, nazwa uŜytkownika oraz informacje o połączeniach z
innymi klientami w sieci.
new Thread(new ThreadStart(new sesja(clientinf.login, client,
klienci).run)).Start();
KaŜdy wątek tworzy nowy obiekt klasy sesja, w którym obsługuje klienta do momentu, aŜ nie
wyśle on wiadomości o chęci zakończenia połączenia, bądź teŜ połączenie nie zostanie
zerwane.
NajwaŜniejszą funkcją jaką pełni kaŜdy wątek jest przekazywanie informacji
pomiędzy klientami. Operacja moŜliwa jest poprzez pobranie odpowiedniego obiektu
struktury clientInfo z tablicy klienci. Następnie ze struktury clientInfo pobierany jest obiekt
klasy TcpClient. Z pobranego obiektu klasy TcpClient tworzy się nowy strumień do
wysyłania danych do tego klienta BinaryWriter.
clientInfo info = (clientInfo)users[login];
wclient = new BinaryWriter(info.tcpclient.GetStream());
Posiadając strumień wysyłania do danego uŜytkownika, moŜna wysłać do niego dane w
postaci ciągów znaków. Po wysłaniu wiadomości naleŜy zwolnić zasób.
wclient.Write(wiadomosc);
wclient.Flush();
Serwer przyjmuje i wysyła dane oddzielone specjalnym znakami „#*|”, dla przykładu
wiadomość dla uŜytkownika odbiorca od uŜytkownika nadawca przedstawia się następująco:
„1#*|nadawca#*|odbiorca#*|wiadomość#*|data”. Na początku kaŜdej wiadomości jest typ
polecenia wyraŜany liczbą, następnie nazwa uŜytkownika od którego pochodzi wiadomość,
potem nazwa uŜytkownika, do którego wiadomość ma być przekazana. Treść tej wiadomości
oraz na samym końcu data, którą dodaje serwer podczas przekazywania wiadomości.
4
Rys2. Okno serwera komunikatora Domacho, opracowanie własne.
Dzięki zastosowaniu prostego modelu architektury, klient nie musi nawiązywać wielu
połączeń. Nawiązuje jedynie jedno z serwerem komunikatora. Połączenie nawiązywane jest
przez wątek działający w tle (backgroundWorker). Po zalogowaniu się następuje zmiana
grafiki w głównym oknie klienta.
Rys3. Okna klienta komunikatora Domacho, przed i po zalogowaniu, opracowanie własne.
5
Głównym problemem przy konstrukcji klienta komunikatora było rozróŜnianie okien
formularza drugiego (Form2). W oknie drugiego formularza prowadzone są rozmowy z
innymi uŜytkownikami. KaŜde pojedyncze okno Form2 odpowiada rozmowie z jednym
uŜytkownikiem. Przykładowo, gdy klient komunikatora otrzyma wiadomość od uŜytkownika
artdom, musi wyświetlić ją oknie rozmowy z uŜytkownikiem artdom. Z uwagi na to, Ŝe
uŜytkownik moŜe prowadzić wiele rozmów jednocześnie, będzie istniało wiele obiektów
From2.
Rys4. Okno Form2 komunikatora Domacho, opracowanie własne.
RozróŜnianie obiektów Form2 jest niezbędnym elementem przekazywania wiadomości do
odpowiednich okien rozmowy. Problem ten rozwiązano przy uŜyciu słownika
przechowującego obiekty From2. Słownik Dictionary, o nazwie rozmowy jest elementem
środowiska .NET Framework, moŜe przechowywać dowolne zmienne i obiekty.
Dictionary<String, Form2> rozmowy = new Dictionary<String, Form2>();
Przyjmuje on dwie wartości, pierwszą jest nazwa uŜytkownika, z którym prowadzona jest
rozmowa, drugą zaś obiekt formularza rozmowy Form2. Klient otrzymując wiadomość
sprawdza, czy słownik rozmowy zawiera informacje o uŜytkowniku, od którego pochodzi
wiadomość. JeŜeli tak, pobiera obiekt Form2 ze słownika Dictionary i przekazuje wiadomość
do niego. JeŜeli nie, tworzy nowe okno rozmowy.
if (rozmowy.ContainsKey(nadawca))
{
rozmowy[nadawca].wyswietlanie_wiadomosci(fullNadawca, wiad, dataCzas);
}
else
{
TheForm = new Form2(w, nadawca, odbiorca, wiad, fullNadawca, fullOdbiorca,
dataCzas); // uwtorzenie nowego okna Form2
TheForm.Text = "Rozmowa z " + fullNadawca; // napis na belce okna Form2
rozmowy.Add(nadawca, TheForm); // dodajemy rozmowę do Dictionary
TheForm.Show();
}
6
Funkcja showWindow odpowiedzialna jest za przekazywanie wiadomości do
odpowiednich okien rozmowy lub tworzenie nowych okien rozmowy. Aby bezpiecznie i
bezawaryjnie wywołać ją z wątku obsługującego połączenie naleŜy uŜyć delegaty. Delegata
najprościej moŜna opisać jako wskaźnik na funkcję.
private delegate void showWindowCallback(BinaryWriter w, string nadawca,
string odbiorca, string wiad, string dataCzas); // Delegata
private void showWindow(BinaryWriter w, string nadawca, string odbiorca,
string wiad, string dataCzas) // Funkcja
{
if (this.InvokeRequired)
{
showWindowCallback f = new showWindowCallback(showWindow);
this.Invoke(f, new object[] { w, nadawca, odbiorca, wiad, dataCzas });
}
else
{
// Treść funkcji showWindow
}
Gdy funkcja showWindow wymaga wywołania poprzez metodę Invoke, tworzony jest nowy
obiekt delegaty showWindowCallback, który ponownie wywołuje funkcję showWindow.
Delegata przyjmuje i przekazuje takie same zmienne jak funkcja, z której została wywołana.
Tworzony jest nowy obiekt delegaty, przez co odwołanie się do funkcji showWindow z
innego wątku jest bezpieczne.
Komunikator posiada równieŜ dodatkowe metody, jak na przykład moŜliwość zmiany
hasła, czy wylogowania się. Komunikator Domacho nadal jest w fazie testów, najnowsze
wiadomości dotyczące jego rozwoju moŜna otrzymać pod adresem: http://domacho.pl.
7