Multi-Client/Server GUI Application. Overview As part of the TCP.

Slides:



Advertisements
Similar presentations
Qualtrics Survey Kenyon A Beginner’s Guide.
Advertisements

C# - Files and Streams Outline Files and Streams Classes File and Directory Creating a Sequential-Access File Reading Data from a Sequential-Access.
A CHAT CLIENT-SERVER MODULE IN JAVA BY MAHTAB M HUSSAIN MAYANK MOHAN ISE 582 FALL 2003 PROJECT.
Programming Based on Events
Cosc 4755 Phone programming: GUI Concepts & Threads.
(NHA) The Laboratory of Computer Communication and Networking Network Host Analyzer.
1 Web Services Visual C# 2008 Step by Step Chapter 30.
Programming Handheld and Mobile devices 1 Programming of Handheld and Mobile Devices Lecture 19 Microsoft’s Approach 1 –.NET Mobile Framework part 2 Rob.
Web Proxy Server. Proxy Server Introduction Returns status and error messages. Handles http CGI requests. –For more information about CGI please refer.
Form Handling, Validation and Functions. Form Handling Forms are a graphical user interfaces (GUIs) that enables the interaction between users and servers.
Microsoft Visual Basic 2005 CHAPTER 8 Using Procedures and Exception Handling.
Assignment 3 A Client/Server Application: Chatroom.
JavaScript & jQuery the missing manual Chapter 11
Chapter 5 Java Script And Forms JavaScript, Third Edition.
CSCI 6962: Server-side Design and Programming Introduction to AJAX.
Microsoft Visual Basic 2012 Using Procedures and Exception Handling CHAPTER SEVEN.
by Chris Brown under Prof. Susan Rodger Duke University June 2012
Overview of Previous Lesson(s) Over View  ASP.NET Pages  Modular in nature and divided into the core sections  Page directives  Code Section  Page.
Microsoft Visual Basic 2008 CHAPTER 8 Using Procedures and Exception Handling.
Microsoft Visual Basic 2008 CHAPTER NINE Using Arrays and File Handling.
Navigating database with windows forms.. Tiered applications  Provide a means to develop many presentations of the same app  Makes changes to the back.
1 Event Driven Programming wirh Graphical User Interfaces (GUIs) A Crash Course © Rick Mercer.
Copyright © 2007, Oracle. All rights reserved. Managing Concurrent Requests.
T U T O R I A L  2009 Pearson Education, Inc. All rights reserved Typing Application Introducing Keyboard Events, Menus, Dialogs and the Dictionary.
© The McGraw-Hill Companies, 2006 Chapter 4 Implementing methods.
LiveCycle Data Services Introduction Part 2. Part 2? This is the second in our series on LiveCycle Data Services. If you missed our first presentation,
1 Working with Menus and Dialog Boxes. 2 Objectives You will be able to Create and edit menus for Windows Forms applications. Write code to handle menu.
11 Web Services. 22 Objectives You will be able to Say what a web service is. Write and deploy a simple web service. Test a simple web service. Write.
Nonvisual Arrays and Recursion by Chris Brown under Prof. Susan Rodger Duke University June 2012.
REVIEW On Friday we explored Client-Server Applications with Sockets. Servers must create a ServerSocket object on a specific Port #. They then can wait.
Internet Applications and Network Programming Dr. Abraham Professor UTPA.
Sample Application Multi Layered Architecture (n-tier): –Graphical User Interface (GUI): Forms, components, controls The Visual Designer in Visual Studio.
1 Chapter Eleven Handling Events. 2 Objectives Learn about delegates How to create composed delegates How to handle events How to use the built-in EventHandler.
Live Meeting Meet Whenever and Wherever. What You Will Learn How to schedule a Live Meeting –How Live Meeting integrates with.
T U T O R I A L  2009 Pearson Education, Inc. All rights reserved Security Panel Application Introducing the Select Case Multiple-Selection Statement.
Windows Forms Navigating database with windows forms.
T U T O R I A L  2009 Pearson Education, Inc. All rights reserved Class Average Application Introducing the Do...Loop While and Do...Loop Until.
Java server pages. A JSP file basically contains HTML, but with embedded JSP tags with snippets of Java code inside them. A JSP file basically contains.
Concurrent Programming and Threads Threads Blocking a User Interface.
Practical Session 12 Reactor Pattern. Disadvantages of Thread per Client It's wasteful – Creating a new Thread is relatively expensive. – Each thread.
Visual Basic 2010 How to Program © by Pearson Education, Inc. All Rights Reserved.
Li Tak Sing COMPS311F. RMI callbacks In previous example, only the client can initiate a communication with the server. The server can only response to.
Microsoft Visual Basic 2012 CHAPTER ELEVEN Multiple Classes and Inheritance.
M1G Introduction to Programming 2 3. Creating Classes: Room and Item.
CHAPTER 14 Classes, Objects, and Games XNA Game Studio 4.0.
ICS3U_FileIO.ppt File Input/Output (I/O)‏ ICS3U_FileIO.ppt File I/O Declare a file object File myFile = new File("billy.txt"); a file object whose name.
Architecture Multi Layered Architecture (n-tier): Application: Model Controllers Database Access Graphical User Interface (GUI): Forms, components, controls.
1 Reverse a String iPhone/iPad, iOS Development Tutorial.
Creating a GUI Class An example of class design using inheritance and interfaces.
T U T O R I A L  2009 Pearson Education, Inc. All rights reserved Student Grades Application Introducing Two-Dimensional Arrays and RadioButton.
1 Event Driven Programs Rick Mercer. 2 So what happens next?  You can layout a real pretty GUI  You can click on buttons, enter text into a text field,
T U T O R I A L  2009 Pearson Education, Inc. All rights reserved Address Book Application Introducing Database Programming.
Lecture Set 7 Procedures and Event Handlers Part B - The Structure of an Application Event Handlers.
M1G Introduction to Programming 2 2. Creating Classes: Game and Player.
Beginning ASP.NET in C# and VB Chapter 9
Li Tak Sing COMPS311F. Case study: a multithreaded chat server The source contains 3 files: ChatServer //the chat server ChatThread //the thread on the.
111 State Management Beginning ASP.NET in C# and VB Chapter 4 Pages
How to develop a VoIP softphone in C# that enables SIP Instant Messaging (IM) This presentation describes how to create a softphone in C# that allows you.
How to develop a VoIP softphone in C# by using OZEKI VoIP SIP SDK This presentation demonstrates the first steps concerning to how to develop a fully-functional.
Chapter 9 Programming Based on Events
INF230 Basics in C# Programming
Chapter Eleven Handling Events.
Using Procedures and Exception Handling
Windows Desktop Applications
Cookies BIS1523 – Lecture 23.
PC02 Term 1 Project Basic Messenger. PC02 Term 1 Project Basic Messenger.
Tutorial 19 - Microwave Oven Application Building Your Own Classes and Objects Outline Test-Driving the Microwave Oven Application Designing.
Qualtrics Survey Kenyon
CIS16 Application Development and Programming using Visual Basic.net
Tonga Institute of Higher Education
Presentation transcript:

Multi-Client/Server GUI Application

Overview As part of the TCP Client-Server Laboratory Project you are to build a GUI (Form) client- server in which the IP address of the server can be specified by the user interactively through the Form. In this lecture we will review the development of Windows Form client- server applications based on tutorials by Andrew Pociu and presented in geekpedia. The principal features of these apps are: Clients can specify the IP address of the server. Multiple clients can connect to the same server. Each client can specify a name to be placed at the front of sent messages. All connected clients can see messages sent from every client. Multiple clients can send messages to the server concurrently.

Building the Client txtIP txtUser btnConnect btnSend txtLog txtSend

using System.Net; using System.Net.Sockets; using System.IO; using System.Threading; Since we will be making use of networking, streaming and threading objects, start by adding the following using statements: Referencing the Standard Classes

// Will hold the user name private string UserName = "Unknown"; private StreamWriter swSender; private StreamReader srReceiver; private TcpClient tcpServer; // Needed to update the form with messages from another thread private delegate void UpdateLogCallback(string strMessage); // Needed to set the form to a "disconnected" state from another thread private delegate void CloseConnectionCallback(string strReason); private Thread thrMessaging; private IPAddress ipAddr; private bool Connected; Declaring Objects Most of our objects can be private since we will not be using them in another class. Our object list includes (1) a stream reader and writer to send and receive messages, (2) a TcpClient object to connect to the server, (3) a delegate to be able to update the form with messages from another thread, (4) another delegate to be able to disconnect the form from another thread, and (5) objects to hold the UserName and ipAddr.

The btnConnect Click Event private void btnConnect_Click(object sender, EventArgs e) { // If we are not currently connected but awaiting to connect if (Connected == false) { // Initialize the connection InitializeConnection(); } else // We are connected, thus disconnect { CloseConnection("Disconnected at user's request."); } What happen's when you click the btnConnect button? What happen's when you click it again?

private void InitializeConnection() { ipAddr = IPAddress.Parse(txtIp.Text); //convert IP text to object tcpServer = new TcpClient(); // Start TCP connections to server tcpServer.Connect(ipAddr, 1986); Connected = true; // track whether we are connected or not UserName = txtUser.Text; // Prepare the form. txtIp.Enabled = false; // Disable & enable appropriate fields txtUser.Enabled = false; txtMessage.Enabled = true; btnSend.Enabled = true; btnConnect.Text = "Disconnect"; // Send the desired username to the server swSender = new StreamWriter(tcpServer.GetStream()); swSender.WriteLine(txtUser.Text); swSender.Flush(); // Start the thread for receiving messages and further communication thrMessaging = new Thread(new ThreadStart(ReceiveMessages)); thrMessaging.Start(); } The InitializeConnection( ) Method

private void ReceiveMessages() { srReceiver = new StreamReader(tcpServer.GetStream()); // from server string ConResponse = srReceiver.ReadLine(); if (ConResponse[0] == '1') // if 1st char = 1 connect was successful { // Update the form to tell it we are now connected this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { "Connected Successfully!" }); } else // if 1st char != 1, the connection was unsuccessful { string Reason = "Not Connected: "; // reason starts at 3rd char Reason += ConResponse.Substring(2, ConResponse.Length - 2); this.Invoke(new CloseConnectionCallback(this.CloseConnection), new object[] { Reason }); return; // exit the method } while (Connected) // while connected read incoming lines from server { // Show the messages in the log TextBox this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { srReceiver.ReadLine() }); } ReceiveMessages( ) Method

more on ReceiveMessages( ) The this.Invoke( ) calls tell the form to update itself. We can't directly update the form elements ourselves from this method because it's in a separate thread (remember we called it using ThreadStart( )) and cross-thread operations are illegal. Finally, the while (Connected) loop keeps calling the srReceiver.ReadLine( ) method which checks for incoming messages from the server. Next comes the method that we kept calling using this.Invoke() - all it does is to update the txtLog TextBox with the latest message: // called from a different thread in order to update the log TextBox private void UpdateLog(string strMessage) { // Append text also scrolls the TextBox to the bottom each time txtLog.AppendText(strMessage + "\r\n"); }

So far we've seen how to receive messages from the server, but nothing about how to send them. When do we want to send a message? When the Send button is clicked or when the Enter key is pressed while txtMessage has the focus. This should be hooked up to the Click event of the btnSend button: // We want to send the message when the Send button is clicked private void btnSend_Click(object sender, EventArgs e) { SendMessage(); } And this needs to be hooked up to the KeyPress event of txtMessage: // But we also want to send the message once Enter is pressed private void txtMessage_KeyPress(object sender, KeyPressEventArgs e) { // If the key is Enter if (e.KeyChar == (char)13) { SendMessage(); } Two Ways to Send a Message

Both btnSend and a Carriage Return invokes the SendMessage( ) method. // Sends the message typed in to the server private void SendMessage() { if (txtMessage.Lines.Length >= 1) { swSender.WriteLine(txtMessage.Text); swSender.Flush(); txtMessage.Lines = null; } txtMessage.Text = ""; } SendMessage( ) Method

// Closes a current connection private void CloseConnection(string Reason) { // Show the reason why the connection is ending txtLog.AppendText(Reason + "\r\n"); // Enable and disable the appropriate controls on the form txtIp.Enabled = true; txtUser.Enabled = true; txtMessage.Enabled = false; btnSend.Enabled = false; btnConnect.Text = "Connect"; // Close the objects Connected = false; swSender.Close(); srReceiver.Close(); tcpServer.Close(); } CloseConnection( ) Method

public Form1() { // On application exit, don't forget to disconnect first Application.ApplicationExit += new EventHandler(OnApplicationExit); InitializeComponent(); } // The event handler for application exit public void OnApplicationExit(object sender, EventArgs e) { if (Connected == true) { // Closes the connections, streams, etc. Connected = false; swSender.Close(); srReceiver.Close(); tcpServer.Close(); } An Event Handler is Needed for the Form This completes the Chat Client...

Building the Chat Server The Chat Server application will hold information on all the connected clients, await for messages from each and send incoming messages to all. Since the server does not initiate messages there is no need for a txtMessage textbox or Send Button. txtIP txtLog btnListen

using System.Net; using System.Net.Sockets; using System.IO; using System.Threading; As with the Chat Client, the Server will be making use of networking, streaming and threading objects, start by adding the following using statements: Referencing the Standard Classes

private delegate void UpdateStatusCallback(string strMessage); For the Chat Server we need to create only one object, a delegate to handle the messages to and from the Clients. This will be used to update the txtLog TextBox from another thread : Declaring Object(s)

private void btnListen_Click(object sender, EventArgs e) { // Parse the server's IP address out of the TextBox IPAddress ipAddr = IPAddress.Parse(txtIp.Text); // Create a new instance of the ChatServer object ChatServer mainServer = new ChatServer(ipAddr); // Hook the StatusChanged event handler to mainServer_StatusChanged ChatServer.StatusChanged += new StatusChangedEventHandler(mainServer_StatusChanged); // Start listening for connections mainServer.StartListening(); // Show that we started to listen for connections txtLog.AppendText("Monitoring for connections...\r\n"); } The btnListen_Click( ) Method

public void mainServer_StatusChanged(object sender, StatusChangedEventArgs e) { // Call the method that updates the form this.Invoke(new UpdateStatusCallback(this.UpdateStatus), new object[] { e.EventMessage }); } private void UpdateStatus(string strMessage) { // Updates the log with the message txtLog.AppendText(strMessage + "\r\n"); } mainServer_StatusChange( ) & UpdateStatus( ) In the btnListen_Click( ) method we instantiated a new StatusChanged event handler. This method, named mainServer_StatusChanged( ) calls the method that updates the Chat Server form. The method UpdateStatus( ), which is called by mainServer_ StatusChanged( ) actually changes the contents of the txtLog textbox. This completes the Chat Server Form1 code...

The ChatServer.cs Class Now we have to create a separate class. This is done within the.NET IDE in the followig manner: (1) Inside the Solution Explorer panel, right-click the Solution 'ChatServer' 1 Project (or whatever name you have given your project). (2) Select Add..New Item in the drop-down menu and sub-menu that appears (3) Choose Visual C# Class from the list of types of objects and name it ChatServer.cs (same name as project is OK). (4) Click the Add Button to create the shell for this new class.

using System; using System.Collections.Generic; using System.Text; using System.Net; using System.Net.Sockets; using System.IO; using System.Threading; using System.Collections; This class needs the following references to System defined classes included in its project header. Referencing the Standard Classes

// Holds the arguments for the StatusChanged event public class StatusChangedEventArgs : EventArgs { // The argument we're interested in is a message describing the event private string EventMsg; // Property for retrieving and setting the event message public string EventMessage { get { return EventMsg; } set { EventMsg = value; } // Constructor for setting the event message public StatusChangedEventArgs(string strEventMsg) { EventMsg = strEventMsg; } Get( ) & Set( ) for the Event Arguments

// delegate needed to specify parameters we're passing with event public delegate void StatusChangedEventHandler(object sender, StatusChangedEventArgs e); Delegate for the Event Handler

class ChatServer { // This hash table stores users and connections (browsable by user) public static Hashtable htUsers = new Hashtable(30); // 30 users max // hash table stores connections and users (browsable by connection) public static Hashtable htConnections = new Hashtable(30); // 30 users max private IPAddress ipAddress; //stores IP address passed to it private TcpClient tcpClient; // this event notifies the form when a user connects, disconnects, etc.. public static event StatusChangedEventHandler StatusChanged; private static StatusChangedEventArgs e; // constructor sets IP address to one retrieved by instantiating object public ChatServer(IPAddress address) { ipAddress = address; } private Thread thrListener; //thread to hold the connection listener private TcpListener tlsClient; //TCP listening object bool ServRunning = false; //tells while loop to continue monitoring The ChatServer Class There are two hash tables defined for the client list because we will need to search by both connections and by users at different times in the operation of the application.

// Add the user to the hash tables public static void AddUser(TcpClient tcpUser, string strUsername) { // first add username and associated connection to hash tables ChatServer.htUsers.Add(strUsername, tcpUser); ChatServer.htConnections.Add(tcpUser, strUsername); // report new connection to all other users and to server form SendAdminMessage(htConnections[tcpUser] + " has joined us"); } // remove this user from the hash tables public static void RemoveUser(TcpClient tcpUser) { if (htConnections[tcpUser] != null) //if user is there { // show the info and tell the other users about disconnection SendAdminMessage(htConnections[tcpUser] + " has left us"); // Remove the user from the hash table ChatServer.htUsers.Remove(ChatServer.htConnections[tcpUser]); ChatServer.htConnections.Remove(tcpUser); } AddUser( ) & RemoveUser( ) Methods The AddUser() method adds a new user to the hash tables, and therefore to our list of connected chat clients. The RemoveUser() method does the opposite.

// this is called to raise the StatusChanged event public static void OnStatusChanged(StatusChangedEventArgs e) { StatusChangedEventHandler statusHandler = StatusChanged; if (statusHandler != null) { // Invoke the delegate statusHandler(null, e); } OnStatusChanged( ) The OnStatusChanged will fire the StatusChanged event, which is handled inside Form1.cs. This is a way of updating the form with the latest message from inside the ChatServer object.

for (int i = 0; i < tcpClients.Length; i++) //loop through client list { try //try sending a message to each { if (Message.Trim() == "" || tcpClients[i] == null) { continue; //if message blank or connection null, break out } swSenderSender = new StreamWriter(tcpClients[i].GetStream()); swSenderSender.WriteLine("Administrator: " + Message); swSenderSender.Flush(); swSenderSender = null; //send message to current user in loop } catch // if problem, the user is gone, remove him { RemoveUser(tcpClients[i]); } Send AdminMessage( ) // Send administrative messages public static void SendAdminMessage(string Message) { StreamWriter swSenderSender; // first, show in our application who says what e = new StatusChangedEventArgs("Administrator: " + Message); OnStatusChanged(e); // create an array of TCP clients, size = num of users we have TcpClient[] tcpClients = new TcpClient[ChatServer.htUsers.Count]; // copy the TcpClient objects into the array ChatServer.htUsers.Values.CopyTo(tcpClients, 0);. The SendAdminMessage sends an administrative message to all connected clients, by looping through the hash table, attempting to send each the message. If the message doesn't get through, they are assumed to be disconnected and they are removed.

// send messages from one user to all the others public static void SendMessage(string From, string Message) { StreamWriter swSenderSender; e = new StatusChangedEventArgs(From + " says: " + Message); OnStatusChanged(e); //show who says what // create array of TCP clients, size = num of users we have TcpClient[] tcpClients = new TcpClient[ChatServer.htUsers.Count]; ChatServer.htUsers.Values.CopyTo(tcpClients, 0); //copy object to array for (int i = 0; i < tcpClients.Length; i++) //loop through client list { try //try sending a message to each { if (Message.Trim() == "" || tcpClients[i] == null) { continue; //if blank or if connection is null, break out } // send message to the current user in the loop swSenderSender = new StreamWriter(tcpClients[i].GetStream()); swSenderSender.WriteLine(From + " says: " + Message); swSenderSender.Flush(); swSenderSender = null; } catch // if problem, user is gone, remove him { RemoveUser(tcpClients[i]); } SendMessage( ) Method SendMessage( ) method sends a message from a specific chat client to all the others.

public void StartListening() { IPAddress ipaLocal = ipAddress; //get IP of 1st device tlsClient = new TcpListener(1986); //create listener object tlsClient.Start(); //start listener and listen for connections ServRunning = true; //check for true before checking for connections thrListener = new Thread(KeepListening); //start new host thread thrListener.Start(); } private void KeepListening() { while (ServRunning == true) //while server is running... { tcpClient = tlsClient.AcceptTcpClient(); //accept pending conn Connection newConnection = new Connection(tcpClient); //new conn } StartListening( ) & KeepListening( ) Methods StartListening( ) method is the one we called inside Form1, and it initiates all other events. It defines the first needed objects and starts a new thread that keeps listening for connections, which is the KeepListening( ) method.

The Connection Class Looking inside KeepListening() method you will see that a new instance of an object of type Connection is created for each connected client (user). The Connection Class implements these objects. class Connection //this class handles connections, an instance for each user { TcpClient tcpClient; private Thread thrSender; //thread to send info to the client private StreamReader srReceiver; private StreamWriter swSender; private string currUser; private string strResponse; public Connection(TcpClient tcpCon) //constructor of the class { tcpClient = tcpCon; thrSender = new Thread(AcceptClient); //thread that accepts client thrSender.Start(); //thread to call the AcceptClient() method } private void CloseConnection() { tcpClient.Close(); //closes currently open objects srReceiver.Close(); swSender.Close(); }

private void AcceptClient() //occurs when new client is accepted { srReceiver = new System.IO.StreamReader(tcpClient.GetStream()); swSender = new System.IO.StreamWriter(tcpClient.GetStream()); currUser = srReceiver.ReadLine(); //read account info from client if (currUser != "") //response from client!!! { if (ChatServer.htUsers.Contains(currUser) == true) { // store user name in hash table swSender.WriteLine("0|This username already exists."); swSender.Flush(); // 0 means 'not connected' CloseConnection(); return; } else if (currUser == "Administrator") { swSender.WriteLine("0|This username is reserved."); swSender.Flush(); // 0 means 'not connected' CloseConnection(); return; } else { swSender.WriteLine("1"); swSender.Flush(); // 1 means connected successfully ChatServer.AddUser(tcpClient, currUser); } //add user to has tables and start listening } else { CloseConnection(); return; } AcceptClient( ) Method

try { // Keep waiting for a message from the user while ((strResponse = srReceiver.ReadLine()) != "") { // If it's invalid, remove the user if (strResponse == null) { ChatServer.RemoveUser(tcpClient); } else { // Otherwise send the message to all the other users ChatServer.SendMessage(currUser, strResponse); } catch { // If anything went wrong with this user, disconnect him ChatServer.RemoveUser(tcpClient); } The the constructor initializes the TcpClient object. The CloseConnection( ) method is called when we want to get rid of a currently connected client The AcceptClient( ) method checks to make sure the username is valid and if all is fine, it adds the user to the hash tables. Otherwise it removes the user. AcceptClient( ) Method concluded

References: