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

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

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!