Click here to Skip to main content
15,886,519 members
Articles / Web Development / IIS

Duplex Web Services

Rate me:
Please Sign up or sign in to vote.
4.93/5 (25 votes)
26 Jun 2008CPOL13 min read 94.1K   1.6K   140  
Using multi-threading techniques to create a duplex (two-way) web service that can push events/messages to the client.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using ChatClient.TwoWayWS;
using System.Collections;
using System.Net;

namespace ChatClient
{

    public partial class Form1 : Form
    {

        private TwoWayWS.Service1 _ws = new TwoWayWS.Service1();
		private bool _formClosing = false;
		private int _myLastEventID = 0;
		private int _myLastEventsListResetId = 0;

        public Form1()
        {
            InitializeComponent();
			_ws.Url = Properties.Settings.Default.ChatClient_TwoWayWS_Service1;
			//NetworkCredential cred = CredentialCache.DefaultCredentials as NetworkCredential;
			//_ws.Credentials = cred;
			_ws.UseDefaultCredentials = true;

			_ws.ListenCompleted += new ListenCompletedEventHandler(_ws_ListenCompleted);

			// Start to listen by calling the listen webmethod which will wait for events
			_ws.ListenAsync(0, 0);

		}

		/// <summary>
		/// When Listen ws request returns then an event was registered and needs to be processed.
		/// Process the event and call the Listen webmethod again to listen for further events
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _ws_ListenCompleted(object sender, ListenCompletedEventArgs e) {

			bool getAllUsers = false;

			ChatClient.TwoWayWS.EventObject[] events = e.Result;
			
			System.Diagnostics.Trace.WriteLine("_ws_ListenCompleted - Made it in - _formClosing=" + _formClosing);
			System.Diagnostics.Trace.WriteLine("_ws_ListenCompleted - " + events.Length);

			if (_formClosing)
				return;

			foreach (EventObject eventObj in events) {

				System.Diagnostics.Trace.WriteLine("_ws_ListenCompleted - event Object type " + eventObj.ToString());

				_myLastEventID = eventObj.EventID;
				_myLastEventsListResetId = eventObj.EventsListResetID;

				switch (eventObj.ToString()) {
					case "ChatClient.TwoWayWS.LoginEvent":
						// If list of users on UI is empty and we got a loggin event then call another ws to 
						// get a list of all the logged in users. This condition would be true first we log in
						// and we want a list of the current users. Otherwise just add the new user(someone else logging in)
						// to the bottom of our list.
						if (lvLoggedInUsers.Items.Count == 0)
							getAllUsers = true;
						else {
							LoginEvent le = eventObj as LoginEvent;
							// Login user may exist in the list due to bulk load of all logged on users. And the cached event
							// of the login can be now processed as well.
							try {
								if (!lvLoggedInUsers.Items.ContainsKey(le.UserName))
									lvLoggedInUsers.Items.Add(le.UserName, le.UserName, 0);
							}
							catch { }
						}
						break;
					case "ChatClient.TwoWayWS.LoggedOutEvent":
						LoggedOutEvent lo = eventObj as LoggedOutEvent;
						lvLoggedInUsers.Items.RemoveByKey(lo.UserName);
						break;
					case "ChatClient.TwoWayWS.MessageEvent":
						MessageEvent msg = eventObj as MessageEvent;
						string fromPart = "From " + msg.From + ":";
						// Add the line - Note SelectedText acts as an append text function.
						rtChatRoom.SelectionBackColor = Color.LightGray;
						rtChatRoom.SelectionColor = Color.Crimson;
						rtChatRoom.SelectedText = fromPart; // Append the text
						rtChatRoom.SelectionBackColor = Color.White;
						rtChatRoom.SelectionColor = Color.Black;
						rtChatRoom.SelectedText = " " + msg.Message; // Append the text
						rtChatRoom.SelectedText = "\n";
						break;
					default:
						// Non-event processing - increment the non-event count
						int nonEventCnt = int.Parse(lblNonEventCnt.Text);
						nonEventCnt++;
						lblNonEventCnt.Text = nonEventCnt.ToString();
						break;
				}

				// Get a list of all the currently logged in users - see comments above.
				if (getAllUsers) {
					LoginEvent[] allLoginEvents = _ws.GetAllUsers();

					foreach (LoginEvent le in allLoginEvents) {
						try {
							lvLoggedInUsers.Items.Add(le.UserName, le.UserName, 0);
						}
						catch { }
					}

					getAllUsers = false;
				}
				
			}

			// Sleep the current UI thread to simulate network latency 
			Application.DoEvents();
			int milliSecdelay = string.IsNullOrEmpty(txtListenDelay.Text) ? 0 : int.Parse(txtListenDelay.Text);
			System.Threading.Thread.Sleep(milliSecdelay * 1000);
			txtListenDelay.Text = "0"; // reset to no delay.
			
			// re-establish a listen request.
			System.Diagnostics.Trace.WriteLine("_ws_ListenCompleted - Calling _ws.ListenAsync() for " + txtUsername.Text);
			_ws.ListenAsync(_myLastEventID, _myLastEventsListResetId);

		}


		private void ListenForEvents() {
		}


		private void btnLogin_Click(object sender, EventArgs e) {
			LoginEvent le = new LoginEvent();
			le.UserName = txtUsername.Text;
			_ws.Login(le);
			#region Affect UI
			btnLogout.Enabled = true;
			btnLogin.Enabled = false;
			btnSend.Enabled = true;
			txtUsername.Enabled = false;
			#endregion
		}

		private void btnLogout_Click(object sender, EventArgs e) {
			LoggedOutEvent lo = new LoggedOutEvent();
			lo.UserName = txtUsername.Text;
			_ws.LogOut(lo);
			#region Affect UI
			btnLogout.Enabled = false;
			btnLogin.Enabled = true;
			btnSend.Enabled = false;
			txtUsername.Enabled = true;
			lvLoggedInUsers.Clear();
			rtChatRoom.Clear();
			#endregion

		}


		private void btnSend_Click(object sender, EventArgs e) {
			MessageEvent msg = new MessageEvent();
			msg.From = txtUsername.Text;
			msg.Message = txtMsg.Text;
			_ws.SendChatMessage(msg);
			txtMsg.Clear();
		}

		private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
			this._formClosing = true;
			// Release the waiting Listener on the server by sending an abstract event object.
			_ws.ReleaseListeners(new EventObject());
		}

 


      
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead Sela College
Israel Israel
With over 20 years of IT experience, Boaz currently works as a Consultant/Instructor at Sela College

Comments and Discussions