C#Remoting异常:通常只允许使用每个套接字地址(协议/网络地址/端口)

时间:2013-02-07 18:01:20

标签: c# exception remoting

我正在使用C#remoting访问服务器系统上的Label,并且只需单击客户端系统上的按钮即可更改该标签的文本。我在命名RemoteObject的类库中创建了一个远程对象,并将此类库的引用添加到客户端和服务器系统,但在调试服务器系统和客户端系统时,我得到异常“每个套接字地址只有一次使用(协议/网络地址/端口通常是允许的“

请帮我解决这个问题。

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";
        }
    }
}

服务器端代码

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);
        }

    }
}

客户端代码

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();
        }
    }
}

请尽快帮助我解决此异常的解决方案。

1 个答案:

答案 0 :(得分:3)

看起来问题是您的Class1.setText方法会创建新的ServerClass。现在,由于创建Class1实例并通过远程处理使其可用的内容为ServerClass,大概这意味着当您远程调用setText方法时,您已经已经有一个ServerClass的实例,所以最终会创建第二个。

这意味着ServerClass构造函数中的所有代码都会运行两次。第二次运行时,它将尝试第二次在端口30000上注册新的TcpChannel。由于您第一次创建的频道已经在运行,因此您会收到您描述的错误。

尝试在ServerClass构造函数上放置一个断点,我希望你会看到它运行两次。第一次你不会得到错误,但第二次你会。

解决方案是避免在ServerClass方法中创建新的setText。如果您的意图是远程调用应该修改屏幕上已有的表单中的标签,那么您需要将对现有ServerClass实例的引用传递给Class1实例。 (例如,让Class1构造函数将ServerClass作为参数,并将其存储在一个字段中。使用它而不是在setText中构建一个新的。)

顺便说一句,远程处理或多或少已被弃用。 WCF是目前在.NET中进行远程访问的常用方法。无论是那个还是Web API。

<强>更新:013/02/12

很抱歉,我使用Remoting已经有一段时间了,因为正如我所提到的, Remoting已被弃用,您不应该使用它!

所以,我忘了这件事:Remoting并不能直接注册一个众所周知的实例 - 你注册了众所周知的类型并且Remoting想要构建你的类型。因此,您需要提出一些将ServerClass传递给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";
    }
}

然后您的ServerClass变为:

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);
    }
}

然后你将点击你的下一个错误InvalidOperationException,错误“跨线程操作无效:控制'label1'从除创建它之外的线程访问。”

这是因为远程处理调用进入工作线程,但您只能从其拥有的UI线程更新Windows窗体UI元素。

同样,请不要使用这个,因为,正如我之前提到的那样重新开始远程操作并且您不应该使用它;使用WCF 但是对于它的价值,这里是如何处理它:

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

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

这应该有用。

又一次, 不要这样做 - 重新启动不再是当前的技术。查看WCF INSTEAD