【问题标题】:How to send a serializable object of external library through sockets?如何通过套接字发送外部库的可序列化对象?
【发布时间】:2017-04-24 15:40:42
【问题描述】:

我有一个简单的类库,它只包含一种类型。现在我想通过使用套接字将这种类型的实例从客户端发送到服务器。客户端和服务器对我需要加载程序集的 SendingObject 类型一无所知。

[Serializable]
    public class SendingObject : MarshalByRefObject
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public SendingObject() { }

        public SendingObject(string firstname, string lastname)
        {
            FirstName = firstname;
            LastName = lastname;
        }

        public override string ToString()
        {
            return string.Format($"Firstname: {FirstName}. Lastname: {LastName}.");
        }
    }

这是我的客户的代码:

   class Program
        {
            static void Main(string[] args)
            {   
                string path = @"path to MyLibrary.dll";
                Assembly library = Assembly.LoadFrom(path);

                Type typeObj = library.GetType("MyLibrary.SendingObject");
                var sob = Activator.CreateInstance(typeObj, new object[] {"Bob", "Smith"});

                try
                {
                    SendAddUserMessageFromSocket(83, sob);
                }
                catch (Exception exc)
                {
                    Console.WriteLine(exc.Message);
                }

                Console.WriteLine("\nTap to continue...");
                Console.ReadKey(true);
            }

            static void SendAddUserMessageFromSocket(int port, object obj)
            {    
                byte[] brr = ObjectToByteArray(obj);

                IPHostEntry ipHost = Dns.GetHostEntry("localhost");
                IPAddress ipAddress = ipHost.AddressList[0];
                IPEndPoint endPoint = new IPEndPoint(ipAddress, port);

                Socket sender = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

                sender.Connect(endPoint);

                NetworkStream networkStream = new NetworkStream(sender);

                if (networkStream.CanWrite)
                {
                    networkStream.Write(brr, 0, brr.Length);
                }

                Console.WriteLine("The message was sent.");
            }

            // Convert an object to a byte array
            private static byte[] ObjectToByteArray(Object obj)
            {
                if (obj == null)
                    return null;

                BinaryFormatter bf = new BinaryFormatter();
                MemoryStream ms = new MemoryStream();
                bf.Serialize(ms, obj);

                return ms.ToArray();
            }

            // Convert a byte array to an Object
            private static Object ByteArrayToObject(byte[] arrBytes)
            {
                MemoryStream memStream = new MemoryStream();
                BinaryFormatter binForm = new BinaryFormatter();
                memStream.Write(arrBytes, 0, arrBytes.Length);
                memStream.Seek(0, SeekOrigin.Begin);
                Object obj = (Object)binForm.Deserialize(memStream);

                return obj;
            }
        }

这个接收到的对象将在服务器端反序列化。这是我的服务器的代码。

class Program
    {
        static void Main(string[] args)
        {
            IPHostEntry ipHost = Dns.GetHostEntry("localhost");
            IPAddress ipAddr = ipHost.AddressList[0];
            IPEndPoint endPoint = new IPEndPoint(ipAddr, 83);
            Socket socketListener = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

            string path = @"path to MyLibrary.dll";
            Assembly library = Assembly.LoadFrom(path);
            Type typeObj = library.GetType("MyLibrary.SendingObject");

            try
            {
                socketListener.Bind(endPoint);
                socketListener.Listen(10);
                bool isWorkFinished = false;

                while (!isWorkFinished)
                {
                    Socket handler = socketListener.Accept();
                    byte[] bytes = new byte[2064];

                    // Get data from connected socket to buffer
                    int numberOfReceivedBytes = handler.Receive(bytes);

                    byte[] bytesObject = new byte[numberOfReceivedBytes];

                    for (int i = 0; i < numberOfReceivedBytes; i++)
                        bytesObject[i] = bytes[i];

                    object res = ByteArrayToObject(bytesObject);
                }
            }
            catch (Exception exc)
            {
                Console.WriteLine(exc.Message);
            }

            Console.WriteLine("\nTap to continue...");
            Console.ReadKey(true);
        }

        // Convert an object to a byte array
        private static byte[] ObjectToByteArray(Object obj)
        {
            if (obj == null)
                return null;

            BinaryFormatter bf = new BinaryFormatter();
            MemoryStream ms = new MemoryStream();
            bf.Serialize(ms, obj);

            return ms.ToArray();
        }

        // Convert a byte array to an Object
        private static Object ByteArrayToObject(byte[] arrBytes)
        {
            MemoryStream memStream = new MemoryStream();
            BinaryFormatter binForm = new BinaryFormatter();
            memStream.Write(arrBytes, 0, arrBytes.Length);
            memStream.Seek(0, SeekOrigin.Begin);
            Object obj = (Object)binForm.Deserialize(memStream);

            return obj;
        }
    }

我的 SendingObject 可以序列化,但尝试反序列化时出错。

找不到程序集:"MyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"。

【问题讨论】:

  • 2 件事。两个项目都必须使用相同的序列化和反序列化库(例如Me.Packet.Deserializer.dll),这两个项目都必须使用相同版本的外部库。这就是你目前拥有的吗?
  • 如果您不从两个项目中引用公共接口 DLL,这确实是您所得到的。 PS:我会避免使用套接字和序列化来处理这样的事情; WCF 很好地完成了这一切。

标签: c# sockets serialization


【解决方案1】:

好的,.NET 中的基本二进制序列化使用一些信息(如TypeAssembly 等)注销这些二进制文件。这就是为什么对于初学者来说,制作用于序列化数据的库会更容易对所有对象的引用。这里的例子:

// assume this is Me.Serialization.Library.csproj
public class MeSerializer {
    public static byte[] Serialize<T>(T obj){
        // serialization method
    }

    public static T Deserialize<T>(byte[] buffer) {
        // deserialization method
    }
}

现在在其他两个项目(比如说客户端和服务器)中,您只需使用来自Me.Serialization.Library 的方法,例如:

static void SendAddUserMessageFromSocket(int port, object obj)
{    
    // simple edit :
    string path = @"path to MyLibrary.dll";
    Assembly library = Assembly.LoadFrom(path);

    Type typeObj = library.GetType("MyLibrary.SendingObject")
    MethodInfo method = typeof(Me.Serialization.Library.MeSerializer).GetMethod("Serialize");
    MethodInfo generic = method.MakeGenericMethod(typeObj);
    byte[] brr = (byte[])generic.Invoke(null, obj); // null because it is static

    // remember to add reference to Me.Serialization.Library
    // byte[] brr = Me.Serialization.Library.MeSerializer.Serialize<MyLibrary.SendingObject>(obj);

    IPHostEntry ipHost = Dns.GetHostEntry("localhost");
    IPAddress ipAddress = ipHost.AddressList[0];
    // skipped rest of the code for readability
}

在另一个项目中做同样的事情:

// simple edit :
string path = @"path to MyLibrary.dll";
Assembly library = Assembly.LoadFrom(path);

Type typeObj = library.GetType("MyLibrary.SendingObject")
MethodInfo method = typeof(Me.Serialization.Library.MeSerializer).GetMethod("Deserialize");
MethodInfo generic = method.MakeGenericMethod(typeObj);

// remember to add reference to Me.Serialization.Library
while (!isWorkFinished)
{
    Socket handler = socketListener.Accept();
    byte[] bytes = new byte[2064];

    // Get data from connected socket to buffer
    int numberOfReceivedBytes = handler.Receive(bytes);

    byte[] bytesObject = new byte[numberOfReceivedBytes];

    for (int i = 0; i < numberOfReceivedBytes; i++)
        bytesObject[i] = bytes[i];

    object brr = (object)generic.Invoke(null, bytesObject);
    // object res = (object)Me.Serialization.Library.MeSerializer.Deserialize<MeLibrary.SendingObject>(bytesObject);
}

【讨论】:

  • 问题是我没有SendingObject的类型。这就是我从加载的程序集中获取它的原因:Assembly library = Assembly.LoadFrom(path); Type typeObj = library.GetType("MyLibrary.SendingObject"); 我不能将此变量用作Serialize &lt;T&gt; 方法的通用参数。
  • 哦,但这不是问题。我会编辑我的答案:)
  • 对不起,我还是不明白。 Serialize&lt;MyLibrary.SendingObject&gt; 我对MyLibrary 没有任何参考。我只有Me.Serialization.Library(里面没有任何参考)。如何将泛型参数设置为Serialize 方法?
  • 正如我所说,看看我编辑的答案。这应该可以正常工作。但如果不是,请在评论中告诉我。
  • 它适合你吗?我在Deserialize 方法中遇到了异常。找不到程序集:“MyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”。
猜你喜欢
  • 2011-06-21
  • 1970-01-01
  • 2011-11-26
  • 2011-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-29
  • 2012-03-13
相关资源
最近更新 更多