0

I am using C# remoting for accessing a Label on the server system and the text of that label is to be changed by the click of a button on the client system. I have made a remotable object in a class library naming RemoteObject and added the reference of this class library to both client and server system but when debugging both the server system and client system I am getting the exception "Only one usage of each socket address (protocol/network address/port) is normally permitted"

Please help me in this to rectify this issue..

RemotableObject.dll

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace RemoteObject
{
    public class Class1 : MarshalByRefObject
    {
        public Class1()
        {
        }

        public void setText()
        {
            ServerClass bs = new ServerClass();
            Label lbl = bs.Controls["label1"] as Label;
            lbl.Text = "New Text";
        }
    }
}

Server Side Code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using RemoteObject;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace Server
{
    public partial class ServerClass : Form
    {
        private Class1 myremoteobject;
        public ServerClass()
        {
            InitializeComponent();
            myremoteobject = new Class1();
            TcpChannel channel = new TcpChannel(30000);
            ChannelServices.RegisterChannel(channel, true);
            RemotingConfiguration.RegisterWellKnownServiceType(typeof(Class1), "CSBU", WellKnownObjectMode.SingleCall);
        }

    }
}

Client Side Code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using RemoteObject;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;


namespace Client
{
    public partial class ClientClass : Form
    {
        private Class1 remoteobject = new Class1();
        public ClientClass()
        {
            InitializeComponent();
            TcpChannel chan = new TcpChannel();
            ChannelServices.RegisterChannel(chan,true);
            remoteobject = (Class1)Activator.GetObject(typeof(Class1), "tcp://localhost:30000/CSBU");
        }

        private void changeTextBtn_Click(object sender, EventArgs e)
        {
            remoteobject.setText();
        }
    }
}

Please someone help me with the solution for this exception asap.

Asad Ansari
  • 3
  • 1
  • 4
  • 2
    My remoting knowledge is a bit rusty but the general explanation for this is that the port you are trying to use is already in use for something else. Do a "netstat -ano" from the command line to see if anything is already listening on port 30000. – 500 - Internal Server Error Feb 07 '13 at 18:06

1 Answers1

3

It looks like the problem is that your Class1.setText method creates a new ServerClass. Now since the thing that creates an instance of Class1, and makes it available via remoting, is ServerClass, presumably that means that by the time your setText method is invoked remotely, you've already got one instance of ServerClass, so that'll end up creating a second.

This means that all the code in the ServerClass constructor runs twice. The second time it runs, it'll be attempting to register a new TcpChannel on port 30000 for the second time. Since the channel you created first time round is already running, you get the error you describe.

Try putting a breakpoint on your ServerClass constructor, and I'd expect that you'll see it run twice. The first time you won't get the error, but the second time you will.

The solution will be to avoid creating a new ServerClass in that setText method. If your intention is that the remoting call was supposed to modify a label in the form already on screen, then you need to pass a reference to the existing ServerClass instance to the Class1 instance. (E.g., make the Class1 constructor take a ServerClass as an argument, and store that in a field. Use that instead of constructing a new one in setText.)

By the way, remoting is more or less deprecated. WCF is the usual way to do remote access in .NET these days. Either that or Web API.

Update:013/02/12

Sorry, it has been a while since I used Remoting because, as I mentioned, Remoting is deprecated and you shouldn't be using it!

So, the thing I forgot: Remoting doesn't make it straightforward to register a particular well known instance - you register well-known types and Remoting wants to construct the type for you. So you'll need to come up with some other way of passing the ServerClass to your Class1.

E.g.:

public class Class1 : MarshalByRefObject
{
    public static ServerClass MyServer { get; set; }

    public void setText()
    {
        ServerClass bs = MyServer;
        Label lbl = bs.Controls["label1"] as Label;
        lbl.Text = "New Text";
    }
}

and then your ServerClass becomes:

public partial class ServerClass : Form
{
    public ServerClass()
    {
        InitializeComponent();

        Class1.MyServer = this;

        TcpChannel channel = new TcpChannel(30000);
        ChannelServices.RegisterChannel(channel, true);
        RemotingConfiguration.RegisterWellKnownServiceType(typeof(Class1), "CSBU", WellKnownObjectMode.SingleCall);
    }
}

Then you'll hit your next error, an InvalidOperationException with the error "Cross-thread operation not valid: Control 'label1' accessed from a thread other than the thread it was created on."

This is because Remoting calls come in on worker threads, but you must only update Windows Forms UI elements from their owning UI thread.

Again, please don't use this because, as I may have mentioned before REMOTING IS DEPRECATED AND YOU SHOULDN'T BE USING IT; USE WCF but for what it's worth, here's how to deal with that:

public void setText()
{
    MyServer.BeginInvoke((Action) setTextOnUiThread);
}

private void setTextOnUiThread()
{
    ServerClass bs = MyServer;
    Label lbl = bs.Controls["label1"] as Label;
    lbl.Text = "New Text";
}

And that should work.

And one more time, DON'T DO THIS - REMOTING IS NO LONGER A CURRENT TECHNOLOGY. LOOK AT WCF INSTEAD

Ian Griffiths
  • 14,302
  • 2
  • 64
  • 88