Con la configurazione scelta viene istanziata una classe per ogni utente, ma la lista degli utenti connessi viene mantenuta in una lista statica che quindi può essere acceduta da più oggetti. Questo significa che bisogna gestire l'accesso concorrente alla lista per garantire che non ci siano problemi dovuti a più thread che vi accedono. E' qui che entra in gioco la proprietà ConcurrencyMode che decide se una classe può essere acceduta da un singolo o da multipli thread. In realtà, un servizio è collegato ad un lock che viene usato per gestire l'accesso sia singolo che multiplo. I possibili valori sono:
- Single: un servizio può essere acceduto da un solo thread;
- Multiple: un servizio può essere acceduto da più thread;
- Reentrant: un servizio pu essere acceduto da un solo thread, ma se questo deve eseguire un'operazione di callback sul client allora il thread viene sospeso per lasciare il posto ad un altro ed infine rimesso in coda alla fine del callback.
Visto che il servizio viene istanziato per ogni nuovo utente, le variabili dichiarate nello snippet precedente, sono praticamente equiparabili alle variabili di sessione di ASP.NET fatta eccezione per quelle statiche. Particolare attenzione va dedicata all'attributo callback che contiene un'istanza del callback da invocare per lanciare operazioni sul client, a users che contiene la lista degli utenti e synchronizer che è l'oggetto su cui basare il lock quando si fanno operazioni sugli oggetti statici.
Passata la fase di dichiarazione degli attributi e delle variabli della classe, si passa alla definizione dei metodi definiti nell'interfaccia IChat. Il primo è SignIn che accetta il nick dell'utente che si vuole loggare, e che restituisce la lista degli utenti connesi se il procedimento è andato a buon fine, o un oggetto nullo in caso contrario.
public string[] SignIn(string nick) {
bool nickExists = false;
chatEventHandler = new ChatEventHandler(ChatEventHandler);
lock (synchronizer) {
nickExists = users.Contains(nick);
if (!nickExists) {
this.nick = nick;
users.Add(nick);
}
}
if (!nickExists) {
callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
ChatEventArgs e = new ChatEventArgs();
e.msgType = MessageType.SignIn;
e.name = name;
BroadcastMessage(e);
ChatEvent += chatEventHandler;
string[] list = new string[users.Count];
lock (synchronizer) {
users.CopyTo(list, 0);
}
return list;
}
else {
return null;
}
}Il metodo appena visto esegue due passi ben distinti: innanzitutto verifica se l'utente è connesso preoccupandosi anche di acquisire un lock sull'oggetto di sincronizzazione, per essere sicuro di leggere dati senza che questi vengano alterati durante l'operazione. Se l'utente non esiste, viene aggiunto alla lista degli utenti. Successivamente, viene aperto il canale di callback per invocare dal server operazioni sul client. Viene poi creato l'oggetto ChatEventArgs che contiene i dati dell'evento di SignIn dell'utente e passato al metodo BroadCastMessage che invia il messagio ad ogni utente. Come ultimo passo, viene prima aggiunto il delegate del nuovo utente alla lista di quelli che vengono invocati quando si deve mandare un messaggio, e poi restituita al nuovo client la lista di quelli connessi.
Attenzione: Questo articolo contiene un allegato
Contenuti dell'articolo
- Pagina 1
- Pagina 2
- Pagina 5
- Pagina 6
Aggiungi un nuovo commento »»»
Per inserire un commento, devi registrarti alla nostra community.







Difficoltà
Utilità
Stampa
Download


10annidi.ASPItalia.com: iscriviti alla competizione e vinci fantastici premi ogni mese!

ciao Stefano, bell'articolo, molto interessante l'invocazione asincrona dei delegati sottoscritti all'evento.- C'é un motivo per cui non hai usato la ...
Continua »»» | Rispondi »»»