13.07.2015 Views

Remoting in C# and .NET - The Journal of Object Technology

Remoting in C# and .NET - The Journal of Object Technology

Remoting in C# and .NET - The Journal of Object Technology

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

JOURNAL OF OBJECT TECHNOLOGYOnl<strong>in</strong>e at http://www.jot.fm. Published by ETH Zurich, Chair <strong>of</strong> S<strong>of</strong>tware Eng<strong>in</strong>eer<strong>in</strong>g ©JOT, 2004Vol. 3, No. 1, January-February 2004<strong>Remot<strong>in</strong>g</strong> <strong>in</strong> <strong>C#</strong> <strong>and</strong> .<strong>NET</strong>Dr. Richard Wiener, Editor-<strong>in</strong>-Chief, JOT, Associate Pr<strong>of</strong>essor <strong>of</strong> ComputerScience, University <strong>of</strong> Colorado at Colorado Spr<strong>in</strong>gsThis is the first <strong>in</strong> a series <strong>of</strong> tutorial columns that shall deal with specialized aspects <strong>of</strong><strong>C#</strong> programm<strong>in</strong>g <strong>and</strong> the .<strong>NET</strong> framework.<strong>The</strong> .<strong>NET</strong> <strong>Remot<strong>in</strong>g</strong> API is the equivalent <strong>of</strong> the Java Remote Method Invocation(RMI) API. Both frameworks allow objects on a client mach<strong>in</strong>e to communicate withremote objects on a server. To me, the <strong>in</strong>frastructure required <strong>in</strong> .<strong>NET</strong> appears simplerthan <strong>in</strong> Java’s RMI.What makes <strong>Remot<strong>in</strong>g</strong> (or equivalently RMI) so attractive is that the low-levelsocket protocol that the programmer must normally manage is abstracted out. <strong>The</strong>programmer is able to operate at a much higher <strong>and</strong> simpler level <strong>of</strong> abstraction. In bothlanguages there is some overhead <strong>in</strong> the form <strong>of</strong> boilerplate protocols that must beobserved <strong>in</strong> order to setup the h<strong>and</strong>shak<strong>in</strong>g between the client <strong>and</strong> server mach<strong>in</strong>es. Oncethis is done, send<strong>in</strong>g a message from a client mach<strong>in</strong>e to a server object uses the samesyntax as send<strong>in</strong>g a message to a local object. <strong>The</strong> metaphor <strong>of</strong> object-orientedprogramm<strong>in</strong>g rema<strong>in</strong>s central to this distributed programm<strong>in</strong>g.My last column presented a distributed solution to the Travel<strong>in</strong>g SalespersonProblem (see this column <strong>in</strong> the Nov/Dec, 2003 issue <strong>of</strong> JOT) us<strong>in</strong>g Java’s RMI.Although it is tempt<strong>in</strong>g to present a detailed <strong>and</strong> exhaustive presentation <strong>of</strong> .<strong>NET</strong>’sdistributed comput<strong>in</strong>g protocol, space does not permit this here. In this column, only an<strong>in</strong>troduction <strong>and</strong> several simple examples <strong>of</strong> .<strong>NET</strong> <strong>Remot<strong>in</strong>g</strong> us<strong>in</strong>g <strong>C#</strong> are presented.<strong>The</strong> namespaces that one typically uses <strong>in</strong> <strong>C#</strong> distributed object applications are thefollow<strong>in</strong>g:us<strong>in</strong>g System;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels.Http;When us<strong>in</strong>g one <strong>of</strong> the major IDE’s (Visual Studio or <strong>C#</strong> Builder), it is important to addthe reference system.remot<strong>in</strong>g.dll to your build if it is not already present.Cite this column as follows: Richard Wiener: “<strong>Remot<strong>in</strong>g</strong> <strong>in</strong> <strong>C#</strong> <strong>and</strong> .<strong>NET</strong>”, <strong>in</strong> <strong>Journal</strong> <strong>of</strong> <strong>Object</strong><strong>Technology</strong>, vol. 3, no. 1, January-February 2004, pp. 83-100.http://www.jot.fm/issues/issue_2004_01/column8


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>In <strong>C#</strong>, us<strong>in</strong>g distributed objects does not require stubs or <strong>in</strong>terfaces as <strong>in</strong> Java. <strong>The</strong>CLR (common language runtime) provides full support for remote object calls. Us<strong>in</strong>gdistributed objects does not depend on the system registry for <strong>in</strong>formation about theremote classes. This <strong>in</strong>formation is encapsulated <strong>in</strong> a .DLL file that must be added as areference when compil<strong>in</strong>g the client code.Classes derived from System.MarshalByRef<strong>Object</strong> cause the distributed objectsystem to generate proxy objects on the client that encapsulate the low-level socketprotocol. When the client sends a message to a remote object, it is the proxy thatprocesses this message <strong>and</strong> sends serialized <strong>in</strong>formation across the network. <strong>The</strong> sameworks <strong>in</strong> reverse when proxy objects de-serialize <strong>in</strong>formation that is returned from theserver.Channel objects are the mechanism used to transfer messages between client <strong>and</strong>server. <strong>The</strong> .<strong>NET</strong> framework provides two bidirectional channels:System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels.http.HttpChannel <strong>and</strong>System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels.Tcp.TcpChannel.<strong>The</strong> http channel uses SOAP (Simple <strong>Object</strong> Access Protocol) <strong>and</strong> the tcp channel uses ab<strong>in</strong>ary stream. This latter method is more efficient because it avoids the need to encode<strong>and</strong> decode SOAP messages.A channel must be registered before it can be used. <strong>The</strong> ChannelServices class isused to accomplish this as follows:ChannelServices.RegisterChannel(someChannel);<strong>The</strong> general steps <strong>in</strong>volved <strong>in</strong> writ<strong>in</strong>g a distributed application are summarized below.Writ<strong>in</strong>g the Server1. Construct the server class.2. Select a method for host<strong>in</strong>g the server object(s) on the server. Typically a shortapplication is created that launches the server <strong>and</strong> makes the server objectavailable to the client(s).3. <strong>The</strong> server object typically waits for one or more client objects to communicatewith it.Writ<strong>in</strong>g the Client1. Identify the remote server object to the client.2. Connect the server to the client through a channel.3. <strong>The</strong> client must activate the remote object <strong>and</strong> create a reference to it.4. Communication to the remote object(s), once activated, is similar to send<strong>in</strong>gmessages to local objects.84 JOURNAL OF OBJECT TECHNOLOGY VOL. 3, NO. 1


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>Let us consider a simple client server application. <strong>The</strong> server uses a support<strong>in</strong>gNameHolder class that encapsulates an ArrayList <strong>of</strong> Str<strong>in</strong>g objects, names, each hold<strong>in</strong>g aperson’s name. <strong>The</strong> server has an AddName method that uses a Str<strong>in</strong>g parameter to addanother object to the names field with<strong>in</strong> the NameHolder object.<strong>The</strong> client application takes Console <strong>in</strong>put as it is <strong>in</strong>voked <strong>and</strong> passes the str<strong>in</strong>grepresent<strong>in</strong>g a person’s name to the server object serv<strong>in</strong>g as a proxy. It will be clear fromthe code how this is all accomplished. Let us exam<strong>in</strong>e the details <strong>of</strong> List<strong>in</strong>g 1.List<strong>in</strong>g 1 – Simple Client/Server application us<strong>in</strong>g <strong>Remot<strong>in</strong>g</strong>us<strong>in</strong>g System;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels;us<strong>in</strong>g System.Collections;namespace <strong>Remot<strong>in</strong>g</strong> {// Server classpublic class SaveNamesServer : MarshalByRef<strong>Object</strong> {// Fieldsprivate NameHolder holder; // Must be serializable// Constructorpublic SaveNamesServer() {holder = new NameHolder();}// Comm<strong>and</strong>spublic void AddName(Str<strong>in</strong>g name) {holder.AddName(name);}}}// Queriespublic ArrayList GetNames() {return holder.GetNames();}us<strong>in</strong>g System;us<strong>in</strong>g System.Collections;namespace <strong>Remot<strong>in</strong>g</strong> {[Serializable]public class NameHolder {// Fieldsprivate ArrayList names = new ArrayList();VOL. 3, NO. 1 JOURNAL OF OBJECT TECHNOLOGY 85


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>}}}us<strong>in</strong>g System;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels.Http;namespace <strong>Remot<strong>in</strong>g</strong> {public class StartServer {static void Ma<strong>in</strong>() {// Create <strong>and</strong> register the channelHttpChannel channel = new HttpChannel(12345);ChannelServices.RegisterChannel(channel);Console.WriteL<strong>in</strong>e("Start<strong>in</strong>g SaveNamesServer");}}}// Register the SaveNamesServer for remot<strong>in</strong>g<strong>Remot<strong>in</strong>g</strong>Configuration.RegisterWellKnownServiceType(type<strong>of</strong>(SaveNamesServer),"SaveNamesServer",WellKnown<strong>Object</strong>Mode.S<strong>in</strong>gleton);Console.WriteL<strong>in</strong>e("Press return to exit SaveNamesServer.");Console.ReadL<strong>in</strong>e();Discussion <strong>and</strong> Analysis <strong>of</strong> List<strong>in</strong>g 1<strong>The</strong> Client class uses the <strong>Remot<strong>in</strong>g</strong>Configuration.RegisterWellKnownClientType methodas follows to connect itself to the server.<strong>Remot<strong>in</strong>g</strong>Configuration.RegisterWellKnownClientType(type<strong>of</strong>(SaveNamesServer),"http://localhost:12345/SaveNamesServer");Here one computer is be<strong>in</strong>g used as a client <strong>and</strong> server.<strong>The</strong> Client constructor creates an <strong>in</strong>stance <strong>of</strong> the SaveNamesServer <strong>and</strong> passes this<strong>in</strong>stance to its AddName method that uses the AddName method <strong>of</strong> the server (proxy)object to <strong>in</strong>crease the size <strong>of</strong> the ArrayList <strong>in</strong> the NameHolder field by one.<strong>The</strong> StartServer is used to launch the server process. It uses the same localhostsocket as the Client. Us<strong>in</strong>g the S<strong>in</strong>gleton mode ensures that the server object ma<strong>in</strong>ta<strong>in</strong>sstate between client calls to this s<strong>in</strong>gle server object.VOL. 3, NO. 1 JOURNAL OF OBJECT TECHNOLOGY 87


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>It is essential that the NameHolder class be tagged as [Serializable] <strong>in</strong> order for theSaveNamesServer to be able to marshal its <strong>in</strong>formation properly.DeploymentTo deploy this distributed application, the follow<strong>in</strong>g sequence <strong>of</strong> steps must be followed:1. Compile the server classes <strong>in</strong>to a DLL as follows:csc /target:library SaveNamesServer.cs NameHolder.cs SaveNamesServer.dll2. Compile the StartServer application as follows:csc StartServer.cs /reference:SaveNamesServer.dll -> StartServer.exe3. Compile the Client application as follows:csc Client.cs /reference:SaveNamesServer.dll -> Client.exe4. Launch the StartServer application.5. Launch the Client application.Each time the client is launched <strong>and</strong> a new name is specified on the comm<strong>and</strong> l<strong>in</strong>e, theprevious names that were entered will be output.Two GUI Clients <strong>and</strong> a Server Application<strong>The</strong> next application <strong>of</strong> <strong>Remot<strong>in</strong>g</strong> <strong>in</strong>volves two client GUI’s runn<strong>in</strong>g <strong>in</strong> separateprocesses that communicate with each other through a server object.One <strong>of</strong> the GUI applications, Client2, periodically takes the coord<strong>in</strong>ates <strong>of</strong> a square<strong>of</strong> size 50 from the server <strong>and</strong> moves a rectangle to this position (upper-left corner <strong>of</strong> therectangle). <strong>The</strong> server changes this coord<strong>in</strong>ate every two seconds so the square jumpsfrom one location to another every two seconds.<strong>The</strong> other GUI application, Client1, waits for the user to click the mouse button <strong>in</strong> apanel. A blue “X” marks the spot <strong>of</strong> the mouse click. Simultaneously, the spot at whichthe user clicked the mouse is marked with a small red “x” <strong>in</strong> Client2. <strong>The</strong> communicationis done through the server. If the Client1 user clicks with<strong>in</strong> the boundaries <strong>of</strong> the squarethat is slow<strong>in</strong>g danc<strong>in</strong>g around <strong>in</strong> Client2, a red “H” is shown <strong>in</strong> the panel <strong>of</strong> Client1. <strong>The</strong>cumulative hits <strong>and</strong> misses are also updated after each mouse click <strong>in</strong> Client1.A screenshot <strong>of</strong> both client applications runn<strong>in</strong>g <strong>and</strong> the server provid<strong>in</strong>g thecommunication channel as well as coord<strong>in</strong>ates for the mov<strong>in</strong>g square <strong>in</strong> Client2 is shownbelow.88 JOURNAL OF OBJECT TECHNOLOGY VOL. 3, NO. 1


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>List<strong>in</strong>g 2 conta<strong>in</strong>s the <strong>C#</strong> classes that implement this <strong>Remot<strong>in</strong>g</strong> application.VOL. 3, NO. 1 JOURNAL OF OBJECT TECHNOLOGY 89


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>List<strong>in</strong>g 2 - <strong>Remot<strong>in</strong>g</strong> application with a server <strong>and</strong> two GUI clientsus<strong>in</strong>g System;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels.Http;us<strong>in</strong>g System.Thread<strong>in</strong>g;namespace <strong>Remot<strong>in</strong>g</strong> {public class TargetServer: MarshalByRef<strong>Object</strong> {// Fieldsprivate <strong>in</strong>t xPos, yPos; // position <strong>of</strong> mov<strong>in</strong>g rectangleprivate <strong>in</strong>t x, y; // position <strong>of</strong> shotprivate Thread thrd;private R<strong>and</strong>om rnd;// Constructorpublic TargetServer() {rnd = new R<strong>and</strong>om();for (<strong>in</strong>t i = 0; i < 50000; i++) {rnd.Next(100000);}thrd = new Thread(new ThreadStart(MovePosition));thrd.Start();}// Read-only propertiespublic <strong>in</strong>t XPOS {get {return xPos;}}public <strong>in</strong>t YPOS {get {return yPos;}}public <strong>in</strong>t X {get {return x;}}public <strong>in</strong>t Y {get {return y;}}90 JOURNAL OF OBJECT TECHNOLOGY VOL. 3, NO. 1


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>// Comm<strong>and</strong>spublic void Record(<strong>in</strong>t x, <strong>in</strong>t y) {this.x = x;this.y = y;}// Queriespublic bool IsTargetHit(<strong>in</strong>t x, <strong>in</strong>t y) {return (x >= XPOS && x = YPOS && y


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>}panel.MouseDown += new MouseEventH<strong>and</strong>ler( H<strong>and</strong>leMouseClick);HttpChannel channel = new HttpChannel();ChannelServices.RegisterChannel(channel);InitializeRemoteServer();server = new TargetServer();}}protected override void Dispose (bool dispos<strong>in</strong>g) {if (dispos<strong>in</strong>g) {if (components != null){components.Dispose();}}base.Dispose(dispos<strong>in</strong>g);}private void InitializeRemoteServer() {<strong>Remot<strong>in</strong>g</strong>Configuration.RegisterWellKnownClientType(type<strong>of</strong>(TargetServer),"http://localhost:12345/TargetServer");}private void H<strong>and</strong>leMouseClick(<strong>Object</strong> sender, MouseEventArgs e) {Graphics g = panel.CreateGraphics();g.DrawStr<strong>in</strong>g("X", Font, new SolidBrush(Color.Blue), e.X, e.Y);server.Record(e.X, e.Y);if (server.IsTargetHit(e.X, e.Y)) {g.DrawStr<strong>in</strong>g("H", Font, new SolidBrush(Color.Red),e.X, e.Y);hits++;} else {misses++;}hitsLbl.Text = "Hits: " + hits;missesLbl.Text = "Misses: " + misses;}private void InitializeComponent() {// Details not shown}[STAThread]static void Ma<strong>in</strong>() {Application.Run(new Client1());}92 JOURNAL OF OBJECT TECHNOLOGY VOL. 3, NO. 1


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>us<strong>in</strong>g System;us<strong>in</strong>g System.Draw<strong>in</strong>g;us<strong>in</strong>g System.Collections;us<strong>in</strong>g System.ComponentModel;us<strong>in</strong>g System.W<strong>in</strong>dows.Forms;us<strong>in</strong>g System.Data;us<strong>in</strong>g System.Thread<strong>in</strong>g;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels.Http;namespace <strong>Remot<strong>in</strong>g</strong> {public class Client2 : Form {// Controlsprivate Conta<strong>in</strong>er components = null;private Panel panel;// Fieldsprivate TargetServer server;private Thread thrd;// Constructorpublic Client2() {InitializeComponent();// Set position on screenthis.SetBounds(0, 0, this.Width, this.Height);panel.Pa<strong>in</strong>t += new Pa<strong>in</strong>tEventH<strong>and</strong>ler(OnPa<strong>in</strong>t);HttpChannel channel = new HttpChannel();ChannelServices.RegisterChannel(channel);InitializeRemoteServer();}server = new TargetServer();thrd = new Thread(new ThreadStart(Pulse));thrd.Start();private void Pulse() {for (;;) {Thread.Sleep(500);panel.Invalidate();}}private void InitializeRemoteServer() {<strong>Remot<strong>in</strong>g</strong>Configuration.RegisterWellKnownClientType(type<strong>of</strong>(TargetServer),"http://localhost:12345/TargetServer");}protected override void Dispose (bool dispos<strong>in</strong>g) {VOL. 3, NO. 1 JOURNAL OF OBJECT TECHNOLOGY 93


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>}if (dispos<strong>in</strong>g) {if (components != null) {components.Dispose();}}base.Dispose(dispos<strong>in</strong>g);}}private void OnPa<strong>in</strong>t(<strong>Object</strong> sender, Pa<strong>in</strong>tEventArgs e) {Graphics g = e.Graphics;g.DrawRectangle(new Pen(new SolidBrush(Color.Blue)),server.XPOS, server.YPOS, 50, 50);if (server.X != 0 && server.Y != 0) {g.DrawStr<strong>in</strong>g("x", Font,new SolidBrush(Color.Red), server.X, server.Y);}}private void InitializeComponent() {// Details not shown}[STAThread]static void Ma<strong>in</strong>() {Application.Run(new Client2());}Discussion <strong>and</strong> Analysis <strong>of</strong> List<strong>in</strong>g 2<strong>The</strong> TargetServer spawns a thread <strong>and</strong> starts creat<strong>in</strong>g xPos <strong>and</strong> yPos values every twoseconds. <strong>The</strong> Client1 application communicates with this server through the Recordmethod. Because the TargetServer is declared a subclass <strong>of</strong> MarshalByRef<strong>Object</strong>,communication through proxy objects is accomplished for both Client1 <strong>and</strong> Client2. Each<strong>of</strong> these GUI applications holds a reference to this proxy object as a server field.Chat Session Application<strong>The</strong> f<strong>in</strong>al application is a simple chat client/server application.Any number <strong>of</strong> client applications, each conta<strong>in</strong><strong>in</strong>g a GUI that allows text to beadded to the exist<strong>in</strong>g session, should permit communication among all clients that havelogged <strong>in</strong>. All clients are updated on all communications every second.<strong>The</strong> application is designed so that each client must know about the server, but theserver does not know about any <strong>of</strong> the clients. <strong>The</strong> server’s responsibility is to ma<strong>in</strong>ta<strong>in</strong> acentralized store <strong>of</strong> the clients that are logged <strong>in</strong> as well as the text for the entire chatsession by add<strong>in</strong>g text to it whenever a client posts a new message by click<strong>in</strong>g the “SendText” button.94 JOURNAL OF OBJECT TECHNOLOGY VOL. 3, NO. 1


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>Each client, through a thread, polls the server every second to update the list <strong>of</strong>clients <strong>and</strong> to update the overall session text. <strong>The</strong>refore the server does not need to knowabout any <strong>of</strong> the clients.Two screen shots that depict a session between two clients before <strong>and</strong> after one haslogged <strong>of</strong>f are shown below.VOL. 3, NO. 1 JOURNAL OF OBJECT TECHNOLOGY 95


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong><strong>The</strong> <strong>C#</strong> code for this application is given <strong>in</strong> List<strong>in</strong>g 3.List<strong>in</strong>g 3 – Chat Session Client/Server Application Us<strong>in</strong>g <strong>Remot<strong>in</strong>g</strong>us<strong>in</strong>g System;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels.Http;us<strong>in</strong>g System.Collections;namespace <strong>Remot<strong>in</strong>g</strong> {public class ChatServer : MarshalByRef<strong>Object</strong> {// Fieldsprivate ArrayList clients = new ArrayList(); // Names <strong>of</strong> clients96 JOURNAL OF OBJECT TECHNOLOGY VOL. 3, NO. 1


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>private Str<strong>in</strong>g chatSession = ""; // Holds text for chat session// Comm<strong>and</strong>spublic void AddClient(Str<strong>in</strong>g name) {if (name != null) {lock (clients) {clients.Add(name);}}}public void RemoveClient(Str<strong>in</strong>g name) {lock (clients) {clients.Remove(name);}}public void AddText(Str<strong>in</strong>g newText) {if (newText != null) {lock (chatSession) {chatSession += newText;}}}// Queriespublic ArrayList Clients() {return clients;}}}public Str<strong>in</strong>g ChatSession() {return chatSession;}us<strong>in</strong>g System;us<strong>in</strong>g System.Draw<strong>in</strong>g;us<strong>in</strong>g System.Collections;us<strong>in</strong>g System.ComponentModel;us<strong>in</strong>g System.W<strong>in</strong>dows.Forms;us<strong>in</strong>g System.Data;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels.Http;us<strong>in</strong>g System.Thread<strong>in</strong>g;namespace <strong>Remot<strong>in</strong>g</strong> {public class ChatClient: Form {// Controlsprivate Button sendBtn;private Button logoutBtn;private Button log<strong>in</strong>Btn;VOL. 3, NO. 1 JOURNAL OF OBJECT TECHNOLOGY 97


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>private ListBox listBox;private Conta<strong>in</strong>er components = null;private RichTextBox sendTextBox;private RichTextBox sessionTextBox;private Button clearTextBtn;private Label label1;private Label label2;// Fieldsprivate ChatServer server;private Str<strong>in</strong>g name;private Thread thrd; // Used to poll the server for client names// Constructorpublic ChatClient(Str<strong>in</strong>g name) {InitializeComponent();this.name = name;}protected override void Dispose( bool dispos<strong>in</strong>g ) {if( dispos<strong>in</strong>g ) {if (components != null) {components.Dispose();}}base.Dispose( dispos<strong>in</strong>g );}private void InitializeComponent() {// Details not shown}private void InitializeRemoteServer() {<strong>Remot<strong>in</strong>g</strong>Configuration.RegisterWellKnownClientType(type<strong>of</strong>(ChatServer), "http://localhost:12345/ChatServer");}[STAThread]static void Ma<strong>in</strong>(Str<strong>in</strong>g [] args) {if (args.Length != 1) {MessageBox.Show("Must supply name <strong>in</strong> comm<strong>and</strong> l<strong>in</strong>e.");return;}Application.EnableVisualStyles();Application.Run(new ChatClient(args[0]));}private void log<strong>in</strong>Btn_Click(object sender, System.EventArgs e) {HttpChannel channel = new HttpChannel();ChannelServices.RegisterChannel(channel);InitializeRemoteServer();server = new ChatServer();server.AddClient(this.name);98 JOURNAL OF OBJECT TECHNOLOGY VOL. 3, NO. 1


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>}thrd = new Thread(new ThreadStart(PollServer));thrd.Start();}}private void logoutBtn_Click(object sender, System.EventArgs e) {server.RemoveClient(this.name);listBox.Items.Clear();listBox.Items.Add("No longer logged <strong>in</strong>.");thrd.Abort();}private void PollServer() {for (; ; ) {Thread.Sleep(1000);ArrayList clients = server.Clients();listBox.Items.Clear();foreach (Str<strong>in</strong>g clientName <strong>in</strong> clients) {listBox.Items.Add(clientName);}Str<strong>in</strong>g sessionText = server.ChatSession();sessionTextBox.Clear();sessionTextBox.Text = sessionText;}}private void clearTextBtn_Click(object sender,System.EventArgs e) {sendTextBox.Clear();}private void sendBtn_Click(object sender, System.EventArgs e) {Str<strong>in</strong>g toSend = name + ": ";server.AddText(name + ":\n" + sendTextBox.Text + "\n\n");}us<strong>in</strong>g System;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels;us<strong>in</strong>g System.Runtime.<strong>Remot<strong>in</strong>g</strong>.Channels.Http;namespace <strong>Remot<strong>in</strong>g</strong> {public class StartServer {static void Ma<strong>in</strong>() {// Create <strong>and</strong> register the channelHttpChannel channel = new HttpChannel(12345);ChannelServices.RegisterChannel(channel);Console.WriteL<strong>in</strong>e("Start<strong>in</strong>g ChatServer");// Register the ChatServer for remot<strong>in</strong>gVOL. 3, NO. 1 JOURNAL OF OBJECT TECHNOLOGY 99


REMOTING IN <strong>C#</strong> AND .<strong>NET</strong>}}}<strong>Remot<strong>in</strong>g</strong>Configuration.RegisterWellKnownServiceType(type<strong>of</strong>(ChatServer),"ChatServer",WellKnown<strong>Object</strong>Mode.S<strong>in</strong>gleton);Console.WriteL<strong>in</strong>e("Press return to exit ChatServer.");Console.ReadL<strong>in</strong>e();Discussion <strong>and</strong> Analysis <strong>of</strong> List<strong>in</strong>g 3<strong>The</strong> ChatServer class holds two serializable fields, clients, an ArrayList, <strong>and</strong> chatSession,a Str<strong>in</strong>g.<strong>The</strong> ChatClient class is more complex because <strong>of</strong> the GUI. It conta<strong>in</strong>s a server field<strong>of</strong> type ChatServer. When each client application is launched, a comm<strong>and</strong>-l<strong>in</strong>e argument(str<strong>in</strong>g) that conta<strong>in</strong>s the name <strong>of</strong> the client (Richard <strong>and</strong> Erik <strong>in</strong> the example above) mustbe supplied to the program. This important identification is the only means that clientshave to know who is logged-on <strong>and</strong> who they can “talk” to.When the user clicks the “Log<strong>in</strong>” button, code that enables the client to be connectedto the server <strong>and</strong> the server object activated is executed. S<strong>in</strong>ce the server object is as<strong>in</strong>gleton, its state is ma<strong>in</strong>ta<strong>in</strong>ed between calls from various clients.All calls to the remote server object are shown <strong>in</strong> blue. MarshalByRef<strong>Object</strong> is usedto ensure that <strong>in</strong>formation is transmitted to the server through a proxy throughserialization <strong>and</strong> not by value. If marshal by value were used (by remov<strong>in</strong>g the<strong>in</strong>heritance from class MarshalByRef<strong>Object</strong>), each client would have its own <strong>in</strong>dependentcopy <strong>of</strong> the server object <strong>and</strong> would not be updated when another client added text orremoved itself from the system.About the authorRichard Wiener is Associate Pr<strong>of</strong>essor <strong>of</strong> Computer Science at theUniversity <strong>of</strong> Colorado at Colorado Spr<strong>in</strong>gs. He is also the Editor-<strong>in</strong>-Chief <strong>of</strong> JOT <strong>and</strong> former Editor-<strong>in</strong>-Chief <strong>of</strong> the <strong>Journal</strong> <strong>of</strong> <strong>Object</strong>Oriented Programm<strong>in</strong>g. In addition to University work, Dr. Wiener hasauthored or co-authored 21 books <strong>and</strong> works actively as a consultant<strong>and</strong> s<strong>of</strong>tware contractor whenever the possibility arises.100 JOURNAL OF OBJECT TECHNOLOGY VOL. 3, NO. 1

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!