Witam wszystkich bardzo serdecznie.
Mam taki "drobny" problem. Mianowicie jako projekt mam do napisania komunikator internetowy na dowolnie wybranym protokole. W pierwszej wersji będzie on działał na TCP w kolejnych rozwojowych TCP zastąpię SSL. Ale protokół to najmniejsze moje zmartwienie. Otóż pytałem wujka google, przekopałem serwis 4programmers.net i jakoś nie mogę znaleźć sensownej odpowiedzi na pytanie:
"Jak napisać wielowątkowy serwer który identyfikowałby użytkowników oraz wysyłał wiadomość od użytkownika A tylko do użytkownika B?"
W sieci jest całe mnóstwo przykładów aplikacji klient-serwer, łącznie z wielowątkowymi serwerami ale wszystkie opisane serwery działają na zasadzie broadcastu/chatu - rozsyłają wiadomość do wszystkich klientów.
Świta mi tylko tyle, że:
- serwer powinien być wielowątkowy
- odpowiednie wątki powinny wymieniać między sobą komunikaty od/do klientów
- do przechowywania bazy userów można wykorzystać bazę danych MySQL/PostgreSQL
Stąd moja prośba do starych wyjadaczy i osób bardziej zaawansowanych w kodowaniu w Javie. Jak się zabrać za wymianę informacji między wątkami. Baza danych może poczekać nie jest w tej chwili niezbędna. Przygodę z kodowaniem w Javie dopiero zaczynam stąd moje dziwne i być może dziecinne pytania.
Mam nadzieję, że dość jasno sprecyzowałem założenia serwera ;) Interfejs tekstowy. Ewentualne GUI w kolejnym semestrze jako "nakładka" na tekstową wersję.
Z góry dziękuję za odpowiedzi i podpowiedzi.
P.S Dodam jeszcze, że na forum znalazłem odnośnik do interesującej mnie wersji serwera ale link jest już nieczynny (Error 404)) a w dziale download jeden z linków (właśnie ten odnośnie serwera wielowątkowego) również jest nieczynny.
Vidharr says hail!
P.S.2
Po kolejnych godzinach walki, szperaniu po googlach, etc z pomocą takich i owakich HowTo, tutoriali i książek otrzymałem taki oto kod serwera:
import java.io.*; /* import bibliotek WE/Wy */
import java.net.*; /*import bibliotek odpowiedzialnych za komunikacje sieciowa */
import java.util.*; /* import bibliotek narzedziowych */
/*
Klasa serwer odpowiedzialna za komunikacje pomiedzy uzytkownikami
Dla każdego podlaczonego uzytkownika tworzony jest nowy watek
*/
public class Server //glowna klasa serwera
{
ArrayList<PrintWriter> C_OutStreams;
public class CH implements Runnable //CH (Client Handler) wewnetrzna klasa tworzaca uchwyty do poszczegolnych klientow
{
BufferedReader C_BR; //strumien buforowanych danych przychodzacych od klienta
Socket C_socket; //gniazdo polazceniowe odbierajace dane od klienta
public CH (Socket ClientSocket) //konstruktor klasy
{
try //probuj utworzyc polaczenie na podanym gniezdzie, utworz buforowane strumienie I/O
{
C_socket = ClientSocket; //otworz gniazdo nr ClientSocket
InputStreamReader Input = new InputStreamReader(C_socket.getInputStream());
//utworz strumien wejscia i pobieraj do niego dane przesylane z gniazda klienta
C_BR = new BufferedReader (Input);
//buforuj przychodzace dane
}//koniec bloku try
catch (Exception E) //przechwyc ewentualne wyjatki oraz poinformuj o tym
{
System.out.println("Cos poszlo nie tak!"); //poinformuj o wystapieniu bledu
E.printStackTrace(); //powiedz gdzie wystapil blad
System.out.println("Blad: " + E.getMessage()); //podaj szczegoly dotyczace bledu
}//koniec bloku przechwytywania i obslugi wyjatkow
}//koniec konstruktora
public void run() //wlasna implementacja metody Run(), "ozywianie" watku
{
String msg; // wiadomosc przychodzaca i rozsylana dalej
try //probuj odbierac i rozsylac wiadomosci
{
while ((msg = C_BR.readLine()) != null)
//tak dlugo jak bufor wejsciowy NIE jest pusty
{
System.out.println("otrzymano: " + msg); //na konsoli serwera wysietl ta wiadomosc
Broadcast(msg); //rozeslij wiadomosc do wszystkich uzytkownikow lacznie z nadwaca
//Broadcast(String message) - wlasna metoda do rozsylania wiadomosci
}//koniec petli while
}//koniec bloku try
catch(Exception E) //przechwyc ewentualne wyjatki i poinformuj co to za blad
{
System.out.println("Cos poszlo nie tak!");
E.printStackTrace();
System.out.println("Blad: " + E.getMessage());
}//koniec bloku przechwytywania i obslugi wyjatkow
}//koniec metody run()
}//koniec klasy wewnetrznej CH
public static void main (String[] args) //glowna metoda klasy Server
{
new Server().Uruchom(); //stworz i uruchom Server
} //koniec metody main()
public void Uruchom() //metoda obslugujaca zycie watku
{
C_OutStreams = new ArrayList<PrintWriter>(); //tablica przechowujaca strumienie wyjscia do klientow, kazdy nowy
//strumien to nowe polaczenie, watek i klient
try //proba utworzenia gniazda serwera, tworzenia watkow potomnych
{
ServerSocket S_socket = new ServerSocket(4096); //utworz gniazdo dla serwera na porcie 4096
//port nalezy wybrac z zakresu 1024-65536 poniewaz na ogol te porty sa wolne
//porty z zakresu 0-1023 sa zarezerwowane dla standardowych aplikacji
//klient-serwer, takich jak serwery www, ftp, ssh, telnet, pop, imap czy smtp
while(true) //petla nieskoniczona, wykonywana przez cale "zycie" watku
{
Socket ClientSocket = S_socket.accept(); //nasluchuj i akceptuj polaczenia przychodzace
//przychodzace na port serwera
PrintWriter Output = new PrintWriter (ClientSocket.getOutputStream());
//utworz strumien wyjsciowy na gniezdzie klienta
C_OutStreams.add(Output); //do tablicy klientow dodaj nowy strumien wyjsciowy od
//nowego klienta
Thread Th = new Thread (new CH (ClientSocket)); //utworz nowy watek dla klienta
Th.start(); //uruchom watek
System.out.println("Pomyslnie nawiazano polaczenie z serwerem. Mozna rozpoczac komunikacje");
}//koniec petli while
}//koniec bloku try
catch (Exception E) //obsluga ewentualnych wyjatkow
{
System.out.println("Cos poszlo nie tak!");
E.printStackTrace();
System.out.println("Blad: " + E.getMessage());
}//koniec obslugi wyjatkow
}//koniec metody Uruchom()
public void Broadcast(String message) //metoda rozgloszeniowa odpowiedzialna za
//rozsylanie wiadomosci do wszystkich uzytkownikow
{
Iterator C_Ile = C_OutStreams.iterator(); //sprawdz ilu jest podlaczonych klientow
//i zapamietaj to w zmiennej pomocniczej
while(C_Ile.hasNext())//dopoki nie trafisz na ostatniego klienta probuj
{
try
{
PrintWriter Output = (PrintWriter) C_Ile.next();
Output.println(message); //wyswietl wiadomosc u klienta
Output.flush();//wyczysc bufory
}//koniec bloku try
catch (Exception E)
{
System.out.println("Cos poszlo nie tak!");
E.printStackTrace();
System.out.println("Blad: " + E.getMessage());
}//koniec obslugi wyjatkow
}//koniec while
}//koniec metody Broadcast
}//koniec klasy glownej
Ok kod poprawiony, rzutowania nie ma (trochę jeszcze pogrzebałem i znalazłem co jest nie halo) teraz pozostałe "problemy" Dzięki za naprowadzenie. Odnośnie identyfikacji userów podczas podłączania to zaraz coś pokombinuję i postaram się odpowiednio zmodyfikować kod. Uwzględniam opcję działania a'la IRC czyli globalne wiadomości a po odpowiednim słowie kluczowym (typu /msg nick wiadomość) tylko u zadanego użytkownika pokazuje się komunikat. Trochę to toporne i ograniczone ale w pierwszych wersjach może działać i tak ;) Potem będę modyfikował dalej :)