Solved

Sockets/Clients/Server

Posted on 2004-08-29
58
491 Views
Last Modified: 2007-12-19
I'm writing a Chat Server using the information I gathered from a program called "TalkServ" that I found online.

In general, it works okay. But the problem is this.

I want to be able to invoke methods on the client side from the server. To do that, I need to obtain a handle to each Client() form object on the server end. Please don't worry about the reasons for this. I need it to work on the client side, rather than the server side for some very specific reasons.

The problem is that there is never really a point at which the client is even aware of the server. It connects through a simple TcpClient Socket connection, and everything else is handled on the server.

What I need to do is figure out how to pass the instance of the Client() Form to the Server.

Here is all of my code. It can be easily compiled by creating a new Visual Studio Solution and creating three Projects.
Chat.NET.Client (WinExe) | Chat.NET.Librayr (Class Library) | Chat.NET.Server (Console Application)

Add the Server code to a File in Chat.NET.Server
Add the Library code to a File in Chat.NET.Library
Add the Client   code to a File in Chat.NET.Client

Set Chat.NET.Library as a Dependency of both the Server and Client

Compile and run Server.exe before you run Client.exe

Thanks again!

Any Help is greatly appreciated

================= SERVER (.exe) ========================

using System;
using System.IO;

using System.Net;
using System.Net.Sockets;

using System.Collections;
using System.Threading;

namespace Chat.NET
{
      public enum ServerState
      {
            Running, Starting, Stopping, Waiting, Stopped
      }

      /// <summary>
      /// The Server class creates and manages
      /// the remote chat protocol and connect
      /// requests. All messages are sent from
      /// and to the central server, and  then
      /// to different parts of  the  software
      /// </summary>
      internal class Server
      {
            /***********************/
            /*|- [STAThread] Main |*/
            /***********************/
            [STAThread] public static void Main() { Server netServer = new Server(); }

            /**********************/
            /*|- Private Fields -|*/
            /**********************/

            /// <summary> Property backer for the Server Status </summary>
            private ServerState m_Status;

            /// <summary> Hashtable of the Users (Sorted by UserID) </summary>
            static System.Collections.Hashtable m_Users;
            /// <summary> Hashtable of the Users (Sorted by ClientID) </summary>
            static System.Collections.Hashtable m_Clients;

            /// <summary> Socket Connectivity </summary>
            private System.Net.Sockets.TcpListener m_Host;


            /*************************/
            /*|- Class Constructor -|*/
            /*************************/

            /// <summary>
            /// Creates a new instance of the Server.
            /// </summary>
            internal Server()
            {
                  Server.ConsoleOutput( "internal Server()[Server]") ;

                  // Create a Hashtable to store the Users
                  // This will be replaced by  MySQL  soon
                  Users = new Hashtable(); Clients = new Hashtable();

                  // Specify that we are going to listen
                  // on  Port  5222
                  Host = new TcpListener( IPAddress.Any, 5222 );

                  // SystemEvent Output (Starting Server)
                  Console.WriteLine( ".Beta started : {0}", DateTime.Now ) ;

                  // As long as the Server is Running , Accept Requests
                  while ( Status == ServerState.Running )
                  {
                        
                        
                        // Start the Chat Server
                        Host.Start();

                        // Accept incoming connections
                        if ( Host.Pending() )
                        {
                              Server.ConsoleOutput(" Client Connecting on Port 5222 [Server]" );
                              
                              // Listen for Incoming TCP/IP Clients
                              System.Net.Sockets.TcpClient TcpConnection = Host.AcceptTcpClient();

                              // SystemLogging Output
                              Console.WriteLine ( "New User Connected." );

                              // Asynchronously Connect to the Socket Client
                              AsyncConnection AsyncSocket = new AsyncConnection(TcpConnection);
                        }
                  }
            }

            private void ListUsers()
            {
                  Console.WriteLine("-- Current Users --") ;

                  // Create an array of the users
                  String[] UserNames = new string[Users.Count];
                  // Copy all of the Users into this String
                  Server.Users.Values.CopyTo(UserNames,0);
                  // Iterate the Array and List the Users
                  for ( int i = 0; i < UserNames.Length; i++ )
                  {
                        Console.WriteLine( UserNames[i].ToString() );
                  }

                  Console.WriteLine( "-- End of User Listing --") ;
            }

            /*********************/
            /*|- Class Methods -|*/
            /*********************/

            /// <summary>
            /// Broadcast a Message Across the Server.
            /// </summary>
            /// <param name="Message">The Message to Broadcast.</param>
            /// <param name="Name">The Username to Broadcast as.</param>
            public static void Broadcast( string Name, string Message )
            {
                  // Create a writeable stream buffer for adding information into
                  StreamWriter Writer ;
                  // Create an ArrayListing of all the Users
                  ArrayList ToRemove = new ArrayList();

                  // Create an Array of the Connections
                  TcpClient[] Clients = new TcpClient[Server.Users.Count];
                  // Copy the users into this temporary array
                  Server.Users.Values.CopyTo( Clients, 0 );

                  // Run-through all of the Clients
                  for ( int i = 0; i < Clients.Length; i++ )
                  {
                        try
                        {
                              // If Removing the Spaces from the Beginning and End
                              // of the Message does not equal 'nothing',  or  the
                              // Message itself does not equal a NullReference  to
                              // begin with, then go ahead and continue  with  the
                              // broadcast.
                              if ( Message.Trim() == "" || Clients[i] == null )
                                    continue;

                              // Create a Stream To Send Text to the Client
                              Writer = new StreamWriter(Clients[i].GetStream());

                              // A Stream is Created from each Client Connection
                              // that will send the data to them. This may cause
                              // some clients to get the message sooner than the
                              // others if the server is experiencing  any  form
                              // of lag or downtime altogether.
                              Writer = new StreamWriter(Clients[i].GetStream());

                              // Writes the Desired Broadcast Message into each
                              // Clients ChatWindow. This will prepend the  set
                              // username to the message
                              Writer.WriteLine( Name + " : " + Message );

                              // Flush the Stream Buffer and Dispose of the Stream
                              Writer.Flush(); Writer = null;
                        }
                        catch ( Exception )
                        {
                              break;
                        }
                  }
            }

            /// <summary>
            /// Broadcasts a Message (from the System)
            /// across the server. This  is  different
            /// than Broadcast() because it  does  not
            /// come from any one specific  user,  but
            /// from the Server itself as a SystemHost
            /// </summary>
            /// <param name="Message">The Message to Broadcast. </param>
            public static void SystemBroadcast ( string Message )
            {
                  // Create a writeable stream buffer for adding information into
                  StreamWriter Writer ;
                  // Create an ArrayListing of all the Users
                  ArrayList ToRemove = new ArrayList();

                  // Create an Array of the Connections
                  TcpClient[] Clients = new TcpClient[Server.Users.Count];
                  // Copy the users into this temporary array
                  Server.Users.Values.CopyTo( Clients, 0 );

                  // Run-through all of the Clients
                  for ( int i = 0; i < Clients.Length; i++ )
                  {
                        try
                        {
                              // If Removing the Spaces from the Beginning and End
                              // of the Message does not equal 'nothing',  or  the
                              // Message itself does not equal a NullReference  to
                              // begin with, then go ahead and continue  with  the
                              // broadcast.
                              if ( Message.Trim() == "" || Clients[i] == null )
                                    continue;

                              // A Stream is Created from each Client Connection
                              // that will send the data to them. This may cause
                              // some clients to get the message sooner than the
                              // others if the server is experiencing  any  form
                              // of lag or downtime altogether.
                              Writer = new StreamWriter(Clients[i].GetStream());

                              // Writes the Desired Broadcast Message into each
                              // Clients ChatWindow.
                              Writer.WriteLine( Message );

                              // Flush the Stream Buffer and Dispose of the Stream
                              Writer.Flush(); Writer = null;
                        }
                        catch ( Exception )
                        {
                              break;
                        }
                  }
            }

            public static void ConsoleOutput( String Message )
            {
                  Console.WriteLine( Message ) ;
            }


            /************************/
            /*|- Class Properties -|*/
            /************************/

            /// <summary>
            /// Gets or Sets the Users Table
            /// </summary>
            protected internal static System.Collections.Hashtable Users
            {
                  get { return m_Users; }
                  set { m_Users = value;}
            }

            /// <summary>
            /// Gets or Sets the Users Table
            /// </summary>
            protected internal static System.Collections.Hashtable Clients
            {
                  get { return m_Clients; }
                  set { m_Clients = value;}
            }

            /// <summary>
            /// Gets or Sets the TCP/IP Listener
            /// </summary>
            protected internal System.Net.Sockets.TcpListener Host
            {
                  get { return m_Host; }
                  set { m_Host = value;}
            }

            /// <summary>
            /// Controls the Server
            /// </summary>
            protected internal ServerState Status
            {
                  get { return m_Status; }
                  set { m_Status = value;}
            }
      }

      public sealed class AsyncConnection
      {
            /********************/
            /*| Private Fields |*/
            /********************/

            /// <summary> Specifies the connected Client. </summary>
            TcpClient m_Client;

            /// <summary> Streams For Data Entry </summary>
            System.IO.StreamReader Reader;
            System.IO.StreamWriter Writer;

            /// <summary> Client Username </summary>
            String m_UserName = "";

            /************************/
            /*| Class Constructors |*/
            /************************/

            /// <summary>
            /// Establishes an Asynchronous Connection with a given Client.
            /// </summary>
            /// <param name="Client">The Client to Connect with.</param>
            public AsyncConnection( System.Net.Sockets.TcpClient Client )
            {
                  Server.ConsoleOutput( "private void AsyncConnection()[AsyncSocket]") ;
                  
                  // Assign the Client
                  m_Client = Client;

                  // Create a new Thread for the Client
                  Thread ClientThread = new Thread(new ThreadStart(Connect));

                  // Start the Connecting Procedure
                  ClientThread.Start ( );
            }

            /**********************/
            /*| Class Methods    |*/
            /**********************/

            /// <summary>
            /// Alerts the Server that we are now
            /// connected, and that we are active
            /// </summary>
            private void RunClient()
            {
                  Server.ConsoleOutput( "private void RunClient()[AsyncSocket]") ;
                  try
                  {
                        // Create the String for storing the new user
                        string Message = "";
                        
                        while ( true )
                        {
                              Message = Reader.ReadLine();
                              Server.Broadcast( UserName, Message );
                        }
                  }
                  catch ( Exception )
                  {
                        
                  }
            }

            private void Connect()
            {
                  Server.ConsoleOutput( "private void Connect()[AsyncSocket]") ;
                  
                  // Create a Stream Input Buffer
                  Reader = new System.IO.StreamReader(Client.GetStream());
                  // Create a Stream Output Buffer
                  Writer = new System.IO.StreamWriter(Client.GetStream());

                  // Poll for a UserName
                  string CheckUserName = GetHandle( false );

                  // if the user attempts to use a name
                  // that is already logged in, then  a
                  // chance to change it should be seen
                  while ( Server.Users.Contains(CheckUserName))
                  {
                        CheckUserName = GetHandle( true );
                  }

                  // Once a Valid Username is obtained, set it
                  UserName = CheckUserName;

                  Server.Users.Add( UserName, Client );
                  Server.Clients.Add( Client, UserName );

                  Writer.WriteLine( " *|* Welcome to .Beta *|* ") ;
                  Server.ConsoleOutput( " Client " + UserName + " Connected " );
                  Server.SystemBroadcast ( "*| " + UserName + " Has Joined the Service. |*" );

                  Writer.Flush();

                  System.Threading.Thread ConnectionThread =
                        new Thread(new ThreadStart(RunClient));

                  // Connect to the Host (Finally)
                  ConnectionThread.Start();
            }

            /// <summary>
            /// Prompts the User for a Name.
            /// </summary>
            /// <param name="DisplayError">True if the user needs to be informed that
            /// their selection was already taken.
            /// </param>
            private string GetHandle( bool DisplayError )
            {
                  // Prompt the User for a Name
                  CreateUser UserForm = new CreateUser( DisplayError );

                  if ( UserForm.ShowDialog() == System.Windows.Forms.DialogResult.OK )
                  {
                        return UserForm.UserName;
                  }
                  return null;
            }


            /**********************/
            /*| Class Properties |*/
            /**********************/

            /// <summary>
            /// Gets or Sets the UserName.
            /// </summary>
            string UserName
            {
                  get { return m_UserName; }
                  set { m_UserName = value;}
            }

            /// <summary>
            /// Gets the Connected Client.
            /// </summary>
            TcpClient Client
            {
                  get { return m_Client; }
            }
      }
}
======================== LIBRARY (.dll) =========================

using System;
using System.Drawing;
using System.Windows.Forms;

namespace Chat.NET
{
      public sealed class CreateUser : Form
      {

            /********************/
            /*| Private Fields |*/
            /********************/
            private System.Windows.Forms.Label lblMessage;
            private System.Windows.Forms.Button btnOK;
            private System.Windows.Forms.Button btnCancel;
            private System.Windows.Forms.TextBox txtName;

            /***********************/
            /*| Class Constructor |*/
            /***********************/

            /// <summary>
            /// Prompts the User  to
            /// input a desired name
            /// </summary>
            public CreateUser()
            {
                  // Required for Windows Form Designer
                  InitializeComponent();
            }

            /// <summary>
            /// Prompts the user to input
            /// a desired name, and tells
            /// them  that  the  previous
            /// name chosen was taken.
            /// </summary>
            /// <param name="Error">Displays the Error Text.</param>
            public CreateUser(bool Error)
            {
                  // Required for Windows Form Designer
                  InitializeComponent();
                  this.BringToFront();
                  
                  txtName.SelectionStart = 0;

                  if ( Error )
                  {
                        lblMessage.Text =
                              "The username you selected is already taken.";
                  }
            }

            #region /*| Windows Form Designer |*/
            
            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                  this.lblMessage = new System.Windows.Forms.Label();
                  this.txtName = new System.Windows.Forms.TextBox();
                  this.btnOK = new System.Windows.Forms.Button();
                  this.btnCancel = new System.Windows.Forms.Button();
                  this.SuspendLayout();
                  //
                  // lblMessage
                  //
                  this.lblMessage.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
                  this.lblMessage.Location = new System.Drawing.Point(8, 8);
                  this.lblMessage.Name = "lblMessage";
                  this.lblMessage.Size = new System.Drawing.Size(216, 32);
                  this.lblMessage.TabIndex = 0;
                  this.lblMessage.Text = "Please specify your username";
                  //
                  // txtName
                  //
                  this.txtName.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
                  this.txtName.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
                  this.txtName.Location = new System.Drawing.Point(8, 48);
                  this.txtName.Name = "txtName";
                  this.txtName.Size = new System.Drawing.Size(216, 21);
                  this.txtName.TabIndex = 1;
                  this.txtName.Text = "";
                  this.txtName.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
                  //
                  // btnOK
                  //
                  this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
                  this.btnOK.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
                  this.btnOK.Location = new System.Drawing.Point(8, 72);
                  this.btnOK.Name = "btnOK";
                  this.btnOK.Size = new System.Drawing.Size(104, 23);
                  this.btnOK.TabIndex = 2;
                  this.btnOK.Text = "OK";
                  //
                  // btnCancel
                  //
                  this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
                  this.btnCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
                  this.btnCancel.Location = new System.Drawing.Point(120, 72);
                  this.btnCancel.Name = "btnCancel";
                  this.btnCancel.Size = new System.Drawing.Size(104, 23);
                  this.btnCancel.TabIndex = 3;
                  this.btnCancel.Text = "Cancel";
                  //
                  // CreateUser
                  //
                  this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
                  this.ClientSize = new System.Drawing.Size(232, 96);
                  this.Controls.Add(this.btnCancel);
                  this.Controls.Add(this.btnOK);
                  this.Controls.Add(this.txtName);
                  this.Controls.Add(this.lblMessage);
                  this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
                  this.MaximizeBox = false;
                  this.MinimizeBox = false;
                  this.Name = "CreateUser";
                  this.ResumeLayout(false);

            }
            #endregion

            /**********************/
            /*| Class Properties |*/
            /**********************/

            /// <summary>
            /// Gets the given username.
            /// </summary>
            public string UserName
            {
                  get { return txtName.Text; }
            }
      }
}
======================== CLIENT (.exe)=========================

using System;
using System.Drawing;
using System.Net.Sockets;
using System.Windows.Forms;

namespace Chat.NET
{
      public sealed class Client : Form
      {
            /********************/
            /*| Main Method    |*/
            /********************/
            [STAThread]
            public static void Main()
            {
                  Application.Run(new Client());
            }

            /********************/
            /*| Private Fields |*/
            /********************/
            private System.Windows.Forms.MainMenu mainMenu;
            private System.Windows.Forms.MenuItem connectionMENU;
            private System.Windows.Forms.MenuItem menuLOGIN;
            private System.Windows.Forms.MenuItem menuLOGOUT;
            private System.Windows.Forms.MenuItem menuItem1;
            private System.Windows.Forms.MenuItem menuEXIT;

            static TcpClient TcpSocket;

            /***********************/
            /*| Class Constructor |*/
            /***********************/

            /// <summary>
            /// Prompts the User  to
            /// input a desired name
            /// </summary>
            public Client()
            {
                  // Required for Windows Form Designer
                  InitializeComponent();

                  TcpSocket = new TcpClient();
            }

            #region /*| Windows Form Designer |*/
            
            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                  this.mainMenu = new System.Windows.Forms.MainMenu();
                  this.connectionMENU = new System.Windows.Forms.MenuItem();
                  this.menuLOGIN = new System.Windows.Forms.MenuItem();
                  this.menuLOGOUT = new System.Windows.Forms.MenuItem();
                  this.menuItem1 = new System.Windows.Forms.MenuItem();
                  this.menuEXIT = new System.Windows.Forms.MenuItem();
                  //
                  // mainMenu
                  //
                  this.mainMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
                                                                                                                               this.connectionMENU});
                  //
                  // connectionMENU
                  //
                  this.connectionMENU.Index = 0;
                  this.connectionMENU.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
                                                                                                                                       this.menuLOGIN,
                                                                                                                                       this.menuLOGOUT,
                                                                                                                                       this.menuItem1,
                                                                                                                                       this.menuEXIT});
                  this.connectionMENU.Text = "Connection";
                  //
                  // menuLOGIN
                  //
                  this.menuLOGIN.Index = 0;
                  this.menuLOGIN.Text = "Login";
                  this.menuLOGIN.Click += new System.EventHandler(this.LOGIN);
                  //
                  // menuLOGOUT
                  //
                  this.menuLOGOUT.Index = 1;
                  this.menuLOGOUT.Text = "Logout";
                  //
                  // menuItem1
                  //
                  this.menuItem1.Index = 2;
                  this.menuItem1.Text = "-";
                  //
                  // menuEXIT
                  //
                  this.menuEXIT.Index = 3;
                  this.menuEXIT.Text = "Exit";
                  //
                  // Client
                  //
                  this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
                  this.ClientSize = new System.Drawing.Size(352, 266);
                  this.Menu = this.mainMenu;
                  this.Name = "Client";

            }
            #endregion

            private void LOGIN(object sender, System.EventArgs e)
            {
                  TcpSocket.Connect("localhost", 5222 );
            }

            /**********************/
            /*| Class Properties |*/
            /**********************/

      }
}
0
Comment
Question by:derekthornton
  • 35
  • 23
58 Comments
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11929665
you cant pass a form to the server, I am not sure I am, clear on what you are asking here.
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11929670
the general method of handling this is, the server passes the client a message, the client receives the message and calls the appropriate routine.
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11929956
How would the client receive a message, though?
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11930022
it would receive it through the socket (same as text) you would create a protocol for how you ship data over the socket ...

example ..

{{1}{This is some text}} would say its type 1 (text) and then includes the text
{{2}{Some parameter}} would be message type 2 with different data (maybe passed to a function)
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11930090
That's more or less what my problem is.

I want to pass more than text back and forth between the server and client, but have no idea how. I've tried serialization, but that doesn't do me very much good ...

I want to invoke a method on the Client from the Server.
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11930231
you pass text back and depending what the text is take action ... you could serialize objects over the stream as well without too much issue. You cannot however just call a function on the client. Instead you would tell the client to do something by passing it a message, the client would then do something based on the message that it received.
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11930274
Alright. Here's another quick question (somewhat different context)

If I try Remoting instead of Sockets, I've got a set up like this. ..

=== server ===
        HttpChannel hChan = new HttpChannel(5222);
         ChannelServices.RegisterChannel(hChan);

         Type ChatCoordinatorType = Type.GetType("SimpleChatCoordinator");
         RemotingConfiguration.RegisterWellKnownServiceType(ChatCoordinatorType,
         "Chat",WellKnownObjectMode.Singleton);
============

I've got a similar setup on the Client ,only it uses RemotingConfiguration.Connect instead.

ChatCoordinator implements some Delegates, which I know will work with this (it was part of the original example!)
but I get an error saying that the Delegates cannot be deserialized at that 'security level'. What can I do to fix this?
Using "RemotingConfiguration.Configure(---)" with a Server.exe.config file won't work, because then it will not connect correctly (Cannot get Channels to register) but I can't set the TypeFilterType=Full without the XML file. So how can I use both?
0
 
LVL 37

Accepted Solution

by:
gregoryyoung earned 500 total points
ID: 11930316
http://www.gotdotnet.com/team/changeinfo/Backwards1.0to1.1/default.aspx#00000153 includes a full description of why this happens, why it only happens in 1.1, and how to resolve it. It explains how to do it via code or config file.

0
 
LVL 2

Author Comment

by:derekthornton
ID: 11930325
That doesn't really explain how to use it ...I've got the code, but it says nothing about what you need to do with it or where it goes ...do I run it in something? or just ..put it in the server or what?
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11930344
Using a .config file is out of the question. I'm sorry, every example I've read that recommends config files fails to take Channel Registration into account. It just doesn't work ...not that I've found.
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11930379
out of curiosity did you look at this ? http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconchannelregistration.asp it explains exactly how to do it via a config file.
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11930391
No, it really doesn't.

It doesn't explain how the 'MyChannels' thing works or how you designate a registered channel.
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11930404
Furthermore, I need to do other things that the .config file just doesn't do. I'm sorry, I don't mean to be rude, but .config files just don't seem to work in "ANY" example I have ever seen. I've yet to see an example that had them where they worked on ANYTHING but a Local Server (That is, the Client and Server both running on the same computer, which is COMPLETELY useless)
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11930430

           HttpChannel hChan = new HttpChannel(5222);
           ChannelServices.RegisterChannel(hChan);

        Type ChatCoordinatorType = Type.GetType("SimpleChatCoordinator");
      
        RemotingConfiguration.RegisterWellKnownServiceType(ChatCoordinatorType,
        "Chat",WellKnownObjectMode.Singleton);


I don't see any way that that can be expressed using a config file, and this other document you linked me to has no information on how to use the Soap/BinaryServerFormatterProvider in any way. I'm doing a web search right now, but not finding anything of any real use or value ...
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11930510
Alright. I think I've got part of this ...

the SoapServerFormatterProvider is inserted when a Channel is created along with a SoapClientFormatterProvider.

This requires a Channel to have a Properties object to pass through it.

I think I've got this part working ... but if it could be done with an XML Config file, it would be helpful, but I just can't seem to ever make them work. Here's the code I have to "XML", so to speak.

--- client ---

            ListDictionary channelProperties = new ListDictionary();              
            channelProperties.Add("port", 0);                        

            IDictionary props = new Hashtable();
            props["typeFilterLevel"] = "Full";
            SoapClientFormatterSinkProvider clientformatterProvider = new SoapClientFormatterSinkProvider();
            SoapServerFormatterSinkProvider serverformatterProvider = new SoapServerFormatterSinkProvider(props,null);


            HttpChannel hChan = new HttpChannel(channelProperties,clientformatterProvider,serverformatterProvider);
            ChannelServices.RegisterChannel(hChan);

            Object remoteObject =
                  RemotingServices.Connect(typeof(ChatCoordinator),
                  "http://localhost:5222/Chat");

            // BinaryServerFormatterSinkProvider formatterProvider = new BinaryServerFormatterSinkProvider(props, null);

            ChatCoordinator chatcenter = remoteObject as ChatCoordinator;


---- server ----

         ListDictionary channelProperties = new ListDictionary();              
         channelProperties.Add("port", 5222);                        

         IDictionary props = new Hashtable();
         props["typeFilterLevel"] = "Full";
         SoapClientFormatterSinkProvider clientformatterProvider = new SoapClientFormatterSinkProvider();
         SoapServerFormatterSinkProvider serverformatterProvider = new SoapServerFormatterSinkProvider(props,null);


           HttpChannel hChan = new HttpChannel(channelProperties,clientformatterProvider,serverformatterProvider);
           ChannelServices.RegisterChannel(hChan);

        Type ChatCoordinatorType = Type.GetType("SimpleChatCoordinator");
      
        RemotingConfiguration.RegisterWellKnownServiceType(ChatCoordinatorType,
        "Chat",WellKnownObjectMode.Singleton);
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11930516
perhaps someone else will come along and help you.

Perhaps thats because the config files were pointed at the localhost (you can always change them) but instead of nit picking with you over config files I am going to stick to my original answer to the original question (using TCP sockets). If you want to send events over your sockets you need to build some protocol over your socket instead of just dumping the text out http://www.simplechat.net/ is an example of a chat protocol (very easy to implement as well). Any chat protocol is going to more than just send text anyway (naturally you will want things like user arrived). You will notice that in that example there are multiple fields being passed (user, message, etc). here is another very simple protocol.

number of bytes in message first 4 bytes (integer) this is usefull so you know how much you need to read to be able to process the message
1234
message Type 2 bytes
short
message data n bytes

then just use binary serialization (or custom serialization of some form to put the messages onto the socket even if its just copying strings seperated by pipes as in the example above)

this will allow you to switch based off of the MessageType (could be an alert, could be a message etc) it will also allow you to have verifiable data
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11930545
Hey. You don't need to get defensive. I'm appreciative for your help. I was merely pointing out that the presentation of material in most of the links you sent me are not suitable for someone new to Remoting. They seem to expect you have more experience than they realize they do.

I think I've got it working without the .config file. The purpose was to find a way to get the program working using either Remoting or Sockets, and I thank you for helping. Despite the fact that the pages you sent me did not have the immediate answers, they had what I needed to figure out what to do. That's what being a programmer is all about, Problem Solving.
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11930562
I would recommend getting a book on remoting, it is a huge topic (and one well worth learning). These links are about as simple as you will find, it takes some getting used to as its a slightly different mind set.
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11933329
Do you know any good books that you would recommend? Typically I find most of the MS Press books to be pretty good. I do enjoy the O'Reilly texts as well ...but books are expensive, so I'd like to find some outside opinions on which one to get before I blow the $30+ for one. Heh.

<-- Poor College student.
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11933381
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11934446
Ergh ...

The thing works fine on my local machine, but when I run the server on a remote machine and try to connect to it, it just returns a 'bad request'. I KNOW the ports I am using are opened on the server ..any ideas?
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11934537
this is the remoting version ?
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11934578
Yes. I can post the code.

====== Server.exe ============

// ChatCentral.cs
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

public class SimpleChatCoordinator : ChatCoordinator
{
      
}
public class ServerProcess
{

   // This simply keeps the ChatCoordinator application domain alive.
   public static void Main(string[] Args)
   {

         ListDictionary channelProperties = new ListDictionary();              
         channelProperties.Add("port", 5222);                        

         IDictionary props = new Hashtable();
         props["typeFilterLevel"] = "Full";
         SoapClientFormatterSinkProvider clientformatterProvider = new SoapClientFormatterSinkProvider();
         SoapServerFormatterSinkProvider serverformatterProvider = new SoapServerFormatterSinkProvider(props,null);


           HttpChannel hChan = new HttpChannel(channelProperties,clientformatterProvider,serverformatterProvider);
           ChannelServices.RegisterChannel(hChan);

        Type ChatCoordinatorType = Type.GetType("SimpleChatCoordinator");
      
        RemotingConfiguration.RegisterWellKnownServiceType(ChatCoordinatorType,
        "Chat",WellKnownObjectMode.Singleton);

      Console.WriteLine("The host application domain is running. Press Enter again to stop the application domain.");
      Console.ReadLine();
   }
}
===========================
===== ChatCoordinator.dll =========
// ChatCoordinator.cs
using System;
using System.Runtime.Remoting;
using System.Collections;

// Define the class that contains the information for a Submission event.
[Serializable]
public class SubmitEventArgs : EventArgs
{

      private string _string = null;
      private string _alias = null;

      public SubmitEventArgs(string contribution, string contributor)
      {
            this._string = contribution;
            this._alias = contributor;
      }

      public string Contribution
      {
            get
            {
                  return _string;
            }
      }

      public string Contributor
      {
            get
            {
                  return _alias;
            }    
      }
}

// The delegate declares the structure of the method that the event will call when it occurs.
// Clients implement a method with this structure, create a delegate that wraps it, and then
// pass that delegate to the event. The runtime implements events as a pair of methods,
// add_Submission and remove_Submission, and both take an instance of this type of delegate
// (which really means a reference to the method on the client that the event will call).
public delegate void SubmissionEventHandler(object sender, SubmitEventArgs submitArgs);

// Define the service.
public class ChatCoordinator : MarshalByRefObject
{

      public ChatCoordinator()
      {
   
            Console.WriteLine("ChatCoordinator created. Instance: " + this.GetHashCode().ToString());
   
      }

      // This is to insure that when created as a Singleton, the first instance never dies,
      // regardless of the time between chat users.
      public override object InitializeLifetimeService()
      {
            return null;
      }

      // The client will subscribe and unsubscribe to this event.
      public event SubmissionEventHandler Submission;

      // Method called remotely by any client. This simple chat server merely forwards
      // all messages to any clients that are listening to the Submission event, including
      // whoever made the contribution.
      public void Submit(string contribution, string contributor)
      {
            Console.WriteLine("{0} sent: {1}.", contributor, contribution);

            // Package String in SubmitEventArgs, which will be sent as an argument
            // to all event "sinks", or listeners.
            SubmitEventArgs e = new SubmitEventArgs(contribution, contributor);

            if (Submission != null)
            {
                  Console.WriteLine("Broadcasting...");
                  // Raise Event. This calls the remote listening method on all clients of this object.
                  Submission(this, e);
            }
      }
}

// Class to be used by clients to submit delegates to the ChatCoordinator object.
// Clients must derive a custom class from this and override the InternalSubmissionCallback
// function.  InternalSubmissionCallback is where they need to implement their callback
// logic.  They must use the SubmissionCallback function in their remotable delegates.
public abstract class RemotelyDelegatableObject : MarshalByRefObject
{
      public void SubmissionCallback (object sender, SubmitEventArgs submitArgs)
      {
            InternalSubmissionCallback (sender, submitArgs) ;            
      }

      protected abstract void InternalSubmissionCallback (object sender, SubmitEventArgs submitArgs) ;
}
===============================
========= Client.exe ===============
// ChatClient.cs
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

public class ChatClient
{

      private string _alias = null;

      public ChatClient(string alias)
      {
            this._alias = alias;
      }

      public void Run()
      {
            //HttpChannel hChan = new HttpChannel(5222);
            //ChannelServices.RegisterChannel(hChan);
      
            ListDictionary channelProperties = new ListDictionary();              
            channelProperties.Add("port", 5222);                        

            IDictionary props = new Hashtable();
            props["typeFilterLevel"] = "Full";
            SoapClientFormatterSinkProvider clientformatterProvider = new SoapClientFormatterSinkProvider();
            SoapServerFormatterSinkProvider serverformatterProvider = new SoapServerFormatterSinkProvider(props,null);


            HttpChannel hChan = new HttpChannel(channelProperties,clientformatterProvider,serverformatterProvider);
            ChannelServices.RegisterChannel(hChan);

            Object remoteObject =
                  RemotingServices.Connect(typeof(ChatCoordinator),
                  "http://[DomainNameHere].com:5222/Chat",null);

            // BinaryServerFormatterSinkProvider formatterProvider = new BinaryServerFormatterSinkProvider(props, null);

            ChatCoordinator chatcenter = remoteObject as ChatCoordinator;

      //      RemotingConfiguration.Configure("ChatClient.exe.config");

            // Create a proxy to the remote object.
            // ChatCoordinator chatcenter = new ChatCoordinator();

            // Create a remotely delegatable object
            MyCallbackClass callback = new MyCallbackClass (_alias) ;
       
            // Create a new delegate for the method that you want the event to call
            // when it occurs on the server. The SubmissionReceiver method will
            // then be a remote call for the server object; therefore, you must
            // register a channel to listen for this call back from the server.
            chatcenter.Submission += new SubmissionEventHandler(callback.SubmissionCallback);
            String keyState = "";
       
            while (true)
            {
                  Console.WriteLine("Press 0 (zero) and ENTER to Exit:\r\n");
                  keyState = Console.ReadLine();

                  if (String.Compare(keyState,"0", true) == 0)
                        break;
                  // Call the server with the string you submitted and your alias.
                  chatcenter.Submit(keyState, _alias);
            }
            chatcenter.Submission -= new SubmissionEventHandler(callback.SubmissionCallback);
      }

//      // Args[0] is the alias that will be used.
//      public static void Main(string[] Args)
//      {            
//            if (Args.Length != 1)
//            {
//                  Console.WriteLine("You need to type an alias.");
//                  return;
//            }
//
//            ChatClient client = new ChatClient(Args[0]);
//            client.Run();
//      }

      public static void Main()
      {
            Console.Write("Enter UserName: ");
            ChatClient client = new ChatClient(Console.ReadLine());
            client.Run();
      }
}

// MyCallbackClass is the class that contains the callback function to
// which ChatClient will submit a delegate to the server.
// To to pass a reference to this method (that is, a delegate)
// across an application domain boundary, this class must extend
// MarshalByRefObject or a class that extends MarshallByRefObject like all
// other remotable types.  MyCallbackClass extends RemotelyDelegatableObject because
// RemotelyDelegatableObject is a class that the server can obtain type information
// for.
class MyCallbackClass : RemotelyDelegatableObject
{
      private string _alias = null;

      public MyCallbackClass () {}
      public MyCallbackClass (string alias) { _alias = alias ; }
            
      // InternalSubmissionCallback is the method that is called by
      // RemotelyDelegatableObject.SubmissionCallback().  SubmissionCallback() is
      // sent to the server via a delegate.  You want the chat server to call
      // when the Submission event occurs -- even if the submission is yours.
      // The SubmissionEventHandler delegate wraps this function and is passed
      // to the Add_Submission (SubmissionEventHandler delegate) member of
      // the ChatCoordinator object. The .NET Remoting system handles the transfer
      // of all information about the client object and channel necessary to
      // make a return remote call when the event occurs.
      protected override void InternalSubmissionCallback (object sender, SubmitEventArgs submitArgs)
      {

            // Block out your own submission.
            // This simple chat server does not filter anything.
            if (String.Compare(submitArgs.Contributor, _alias, true) == 0)
            {
                  Console.WriteLine("Your message was broadcast.");
            }
            else
                  Console.WriteLine(submitArgs.Contributor
                        + " says:\r\n"
                        + new String('-', 80)
                        + submitArgs.Contribution
                        + "\r\n"
                        + new String('-', 80)
                        + "\r\n");
      }

      // This override ensures that if the object is idle for an extended
      // period, waiting for messages, it won't lose its lease. Without this
      // override (or an alternative, such as implementation of a lease
      // sponsor), an idle object that inherits from MarshalByRefObject
      // may be collected even though references to it still exist.
      public override object InitializeLifetimeService()
      {
            return null;
      }
}
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11934930
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11934937
it might be registerring itself on the wrong interface.
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11934980
I'm not sure what you mean 'wrong interface'.
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11935034
a machine can have 5 interfaces (ip addresses) make sure it is binding to the right address.
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11935049
I see. So this line ...


//        RemotingConfiguration.RegisterWellKnownServiceType(ChatCoordinatorType,
//        "Chat",WellKnownObjectMode.Singleton);

might be what you're referring to ?

should "Chat" be the localhost name of my Server Domain?
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11935188
Hrnm.. No. Changing that doesnt' seem to do it ... that's the only place it looks like it binds to any sort of address....
0
Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 2

Author Comment

by:derekthornton
ID: 11935207
Wait ...the Domain has to be bound to the port ...so ...


channelProperties.Add("bindTo","ipaddress");
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11935210
i gotta get to work for a bit ... ill be back later this afternoon.

sorry for the delay.
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11935257
yes. that or machinename I believe ipaddress is another thats set.
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11935261
ill check back in a bit ... lemme know any progress.
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11935303
Hrnm.. Nope. None of that made any difference. Still get all the same errors ...I just don't understand why it work locally but not remotely...
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11935780
Okay. The problem is with the event handlers.

I get a "Bad Request" return when I try to attach the event handlers ...but how is that so? They attach fine when the server runs on my local machine, but not when I run it on a remote machine?
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11937062
Yeah. The problem is definitly the event handler. It seems it's just not liking trying to attach it ...
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11938065
Any suggestions?

The problem is that the delegate/event handler doesn't work because of the ports ...I'm not quite sure what it expects me to do about that ...I can't control the ports that it goes through, can I?

Why would it work on the local server, but not the remote one ..though ..I've only got ports 5222 and 5223 open on that remote server ..( at least for this part of the program )
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11938265
hey derek, I have seen this, I will look in the morning (3 hours and 9 beers to late:) )
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11938298
You must be on a different side of the world as me ...
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11938299
Alright. Thanks!
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11946619
Since this part is basically a different question, I'm going to open up a new question for it .
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11946642
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11956351
Just got done reading the .NET Remoting book you recommended and I must say that was the poorest programming book I've ever seen ...
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11956480
Forgive the comment ..that wasn't me. Someone's posted under my account here at my home ...
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11956720
if thats the poorest book you have ever seen I can give you quite a few worse ones most from wrox ;)

0
 
LVL 2

Author Comment

by:derekthornton
ID: 11956950
While I disagree with my (insolent) roommate ( who posted that comment under my account ... ) about it being the 'poorest' book ever, I will admit it's not very good. The reasoning is that none of the example programs are very well laid out. They show the code, but they don't explain where its going ,what are programs and what are actually libraries, how to run them, etc.

It's all just theories, and doesn't provide any sort of implementation that can be easily learned from (as a general rule, people learn best by example).
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11956994
What I mean is ....(A) You cannot compile and use the sample programs ( believe me, I'm sure it's possible ,but the text sure doesn't help with it ) (B) It just jumps from theory to code without any sort of preparation. You're reading about the concepts one minute, then you're writing long code files the next ...

It references things that it says it will cover in greater detail later in the book ..and then never does ...

It never completes itself. For instance, the first example is gearing you towards a remoting application, then suddenly switches it to a web service without any resolution..
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11957028
eh I didnt have that many problems but I also came into it with some background ...
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11957071
It must be designed for .NET 1.0.

I can't get the Chapter 3 Example to run 'AT ALL'.

I follow the Readme.txt with the CD/Folder for it, and it just plain doesn't work (The CAO one).

Is there anything in specific I need to do to make it work right?

Copying the Client.exe and the ClientCAO.exe into the Server.exe runtime directory doesn't help ...I get nothing but a lot of security errors, and I'm very lost. I spent three hours on that chapter alone trying to make that thing run and I finally gave up and went on with the rest of the book.
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11957091
it is designed for 1.0
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11957096
Great. That's just dandy.

Can it run on 1.1?
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11957107
i believe on the books website (I forget where it is listed in the book and I dont have it in front of me) that there is updated code versions for 1.1
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11957116
I've looked pretty hard for the books website and found only the reference to the book itself on MSPress
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11957208
just downloaded the stuff from there .. my mistake, I guess I still had 1.0 then.
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11957428
What exactly is the difference in 1.0 and 1.1?
0
 
LVL 37

Expert Comment

by:gregoryyoung
ID: 11957447
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11957453
I see. Well, thanks again for your help. I guess I'll keep trying until I figure out what's wrong ...
0
 
LVL 2

Author Comment

by:derekthornton
ID: 11957722
Argh ..I just don't understand..

I've got a different remoting example and it's much along the same lines. it works FINE on the local machine, it works FINE on the laptop (remoting client on my desktop, server on my laptop) but when I put it on my web server it just DOESN"T WORK!

I KNOW the port I am using is opened. What else could go wrong?
0

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

Suggested Solutions

Summary: Persistence is the capability of an application to store the state of objects and recover it when necessary. This article compares the two common types of serialization in aspects of data access, readability, and runtime cost. A ready-to…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now