in last 10 minutes
SIK - Temat C - gniazdka w j. Tcl
Uwagi:
1. materiały o j. Tcl, w szczególności interpreter "tclkit" oraz konsola do eksperymentów...
2. w j. Tcl mamy do czynienia z tzw. programowaniem zdarzeniowym;
serwer nie tworzy procesów/wątków obsługujących klientów, lecz definiuje handler,
który (sekwencyjnie) obsługuje komunikaty przychodzące od klientów
czyli serwer jest 1-wątkowy ... ma to zalety jak i wady!!
4. konsola2c.tcl pod Windows: zastąpić puts "komentarz diagnost." (NIE puts $sock "tekst" !)
przez _puts "komentarz diagnost" (bo pod win nie ma "prawdziwej" konsoli tekstowej)
5. jak wklejać tekst do konsola2c.tcl??? odp: shift+insert = Paste, ctrl+insert = Copy (jak w starych Win...)
Prosty przykład użycia gniazd
(zakłada się, że komunikat to linia tekstu zakończona znakiem \n).
## klient
#
set host "localhost"
set s [socket $host 10000]
# tworzymy połączenie tcp z serwerem na maszynie $host, port=10000
# komenda socket zwraca gniazdko repr. koniec tego połączenia
puts $s "A ku ku !!!"
# wysylamy dane przez gniazdko s
flush $s
# flush to wypróżnienie buforów
# !!! bardzo ważne bez tego dane nie zostaną "wypchnięte" !!!
close $s
## serwer 1
#
socket -server obsluga 10000
# ustanawiamy serwer na porcie 10000, na lokalnej maszynie
# zgloszenie się klienta jest obsługiwane przez proc "obsluga"
proc obsluga {s args} {
# s to socket klienta po stronie serwera
set linia [gets $s]
puts "od $s: $linia"
close $s
# po jednokrotnym obslużeniu klienta zamykamy połączenie
}
## serwer 2
# - pozwala klientowi wysłać dowolną liczbę linii
# - obsluguje danego klienta tak dlugo, aż nie zamknie on gniazdka!!!
#
socket -server obsluga 10000
proc obsluga {s args} {
fileevent $s readable "obslugaKli $s"
# dla każdego zgłaszającego się klienta definiujemy
# obsługę zdarzenia "readable" na gniazdku tego klienta ...
# uwaga: jeśli można odczytać 1 znak to można odczytać cały komunikat!
}
proc obslugaKli s {
if {[eof $s]} { close $s; return }
# "eof" pojawi się dokładnie wtedy, gdy klient zamknie gniazdko!
# wtedy po stronie serwera usuwamy zasoby związane z tym klientem
set linia [gets $s]
puts "od $s: $linia"
}
Przykład z bardziej wyrafinowanymi komunikatami, tworzonymi przy pomocy proc send/recv_msg,
których definicje w j. Tcl i Java znajdziesz tutaj.
UWAGA: ten przykład jest niekompletny!, trzeba go uzupełnić!!
## klient używający proc _msg
#
set s [podlacz $host 10000] # uwaga: nie ma proc podlacz!, trzeba ją napisać, pamiętać o odp. konfiguracji kanału!! send_msg $s "a ku ku !!!"
send_msg $s {"a ku ku !!!" 1 2 3 4 5}
send_msg $s [list "zapisz" "plik.gif" {...gif...}]
# w tym przypadku komunikat to lista 3 elementow;
# 3 element to zawartość pliku gif, czyli "dane binarne"
# mozna to traktowac jako rozkaz "zapisz" wysłany do serwera
# z 2 parametrami: nazwą pliku i danymi które mają być w tym pliku zapisane
recv_msg $s odpowiedz
# 2 parametr recv_msg to nazwa zmiennej, w której ma być zapisany komunikat
puts "przyszła następująca odpowiedz: $odpowiedz"
## serwer używający proc _msg
#
socket -server obsluga 10000
proc obsluga {s args} {
fconfigure $s -translation binary
fconfigure $s -encoding utf-8
fileevent $s readable "obslugaKli $s"
}
proc obslugaKli {s} {
upvar ::dane,$s dane
# z kazdym gniazdkiem klienta po stronie serwera łączymy
# zmienna globalną o nazwie dane,$s
# (może to być tablica, czyli zbiór zmiennych -tak jak w tym przykładzie)
# dla tej zmiennej tworzymy alias "dane" ...
recv_msg $s msg
# odbieramy pojedynczy komunikat
if {[eof $s]} { close $s; unset dane; return }
# czyszczenie, jeśli klient zamknął gniazdko
switch [lindex $msg 0] {
# obsluga komuniaktu; tutaj zakłądamy, że jest to lista,
# w której pierwszy element to nazwa rozkazu ...
rozkaz1 {
# wykonujemy rozkaz 1 ...
set dane(jakies_pole) "???"
# mamy wygodny dostęp do prywatnych danych klienta po stronie serwera!!!
}
rozkaz2 {
# wykonujemy rozkaz 2 ...
}
rozkaz3 {
# wykonujemy rozkaz 3 ...
}
}
}
Objaśnienie operacji I/O w j. Tcl: Tcl_io_tutorial.htm
Zastosowanie komendy "vwait"
Komenda "vwait zmienna" pozwala zaprogramować nieblokujące czekanie na odpowiedź...
Scenariusz jest następujący:
1. wysyłamy komunikat do serwera
2. czekamy na odp. przy pomocy "vwait jakas_zmienna"
3. handler zdarzeń (obsługujący przychodzące komunikaty) zapisuje odpowiedź do jakas_zmienna.
Nieblokujące czekanie na odpowiedź ma tę zaletę, że w czasie jego trwania są obsługiwane inne zdarzenia,
np. inne komunikaty jak i zdarzenia GUI...
Zadanie T.0 "test"
Wypróbuj powyższe przykłady...
Zarówno ten używający komunikatów jednolinijkowych jak i ten używający proc send/recv_msg...
Wskazówka: Uruchom osobno klienta i serwer w 2 konsolach tcl-owych!!
Napisy diagnostyczne wygodnie umieszczać w okienku "output" przy pomocy "_puts tekst".
W przypadku proc *_msg trzeba dopisac odrobinę kodu w Tcl...
Zadanie T.1 "send/recv_msg w Tcl i w Javie"
Zbadaj czy implementacje proc send/recv_msg (z tego pliku) w obu językach rzeczywiście ze sobą współpracują.
W tym celu wykonaj następujący eksperyment:
uruchom klienta javowego przeciwko serwerowi w j. Tcl (komunikacja przez poł TCP !);
spróbuj przekazywać w komunikacie "polskie znaki" (ęą itp);
uwaga: podczas kompilacji pliku .java TRZEBA podac sposób kodowania tego pliku
np. javac -encoding utf-8 plik.java
Zadanie T.2 "czat tekstowy" (3pkt)
Zaprogramuj tradycyjny czat tekstowy pozwalający "rozmawiać" dowolnej liczbę użytkowników
posiadających oprogramowanie klienckie ...
Powinien on mieć następujące cechy:
1. każdy użytkownik widzi 2 okna: duże w którym sa komunikaty od społeczności,
oraz małe w którym sam pisze komunikat (tj krótki tekst) który zamierza wysłać ...
2. na spodzie znajdują się guzik "wyslij", pole entry "login", guzik "podłącz" i "odłącz",
guzik "kto tam jest" - wyświetla liste podłączonych użytkowników (w danej chwili),
guzik "pobudka" - budzi użytkowników np wydając jakiś dzwięk.
3. cała społeczność widzi gdy ktoś się podłącza/ odłącza
Uwaga: to zadanie można zrobić w dowolnym języku!
Wskazówki:
Szkielet tworzący GUI programu :
package require Tk text .t1 -height 25 -width 70; pack .t1 -fill both -expand 1 text .t2 -height 10; pack .t2 -fill both frame .f1; pack .f1 button .f1.b1 -text "Wyslij" -command { ... }; pack .f1.b1 -side left # zamiast ... należy wpisać kod obsługi naciśnięcia guzika ... entry .f1.e1 -width 15 -textvariable login; pack .f1.e1 -side left # wpisany w to pole login bedzie dostępny przez zm. globalną login! button .f1.b2 -text "Podłącz" -command { ... }; pack .f1.b2 -side left button .f1.b3 -text "Odłącz" -command { ... }; pack .f1.b3 -side leftZadanie T.2a "rozproszona tablica (asocjacyjna)" (3pkt)
różne interpy Tcl-a posiadają tablice (array) która jest identyczna we wszystkich procesach
podłączonych pod ten sam serwer...
operacja typu "set arr(imie) Jan" w jednym z procesów powoduje podobną zmianę w pozostałych procesach;
można użyć komendy "trace" do przechwytywania modyfikacji tablicy;
rozwiązanie powinno być w miarę ogólne, tj powinno być możliwe istnienie dowolnej liczby takich tablic;
w ramach projektu należy zademonstrować działanie oprogramowania,
np. w postaci wydruków z wielu konsol tcl-owych...
Uwaga: to zadanie można zrobić w dowolnym języku!
Zadanie T.3 "UDP w Tcl"
Zbuduj przykład w j. Tcl z "klientem" i "serwerem" UDP;
klient wysyła do serwera tekst (krótki),
a serwer odsyła klientowi ten tekst z dodanym na końcu znakiem "!";
Potrzebny będzie pakiet tcludp (tcludp.zip), który znajdziesz w folderze (szukaj po słowie "tcludp");
w tym folderze są też dokumentacja i przykłady (patrz też przykłady w tcludp.zip);
Wskazówka: patrz opcje -remote i -peer w gniazdku udp...