我理解如何通过C#中的TCP连接发送对象;我正在使用StreamWriter
。
如何从另一方接收此物品?
我尝试了StreamReader
,但它不包含object
类型的参数。
如何做到这一点?
答案 0 :(得分:1)
IMO最佳解决方案是使用BinaryReader
从流中读取。此外,您应该使用BinaryWriter
类编写流。
如果您的对象不是基本类型之一,则必须在通过TCP连接发送之前进行序列化。
答案 1 :(得分:0)
我真的可以建议使用ProtoBuf将对象转换为可以通过网络发送的字节(即序列化)。它比内置的二进制序列化器(速度,版本控制)要好得多。
它还有友好的帮助器,用于在写入的数据中包含长度/大小前缀,这在您需要读取接收方的数据时很有用。
一旦建立了连接,以下是我用来进行序列化和网络通信的辅助类:
public class Messenger
{
private readonly TcpClient client;
private readonly NetworkStream stream;
public IPEndPoint RemoteEndPoint { get { return (IPEndPoint) client.Client.RemoteEndPoint; } }
public Messenger( TcpClient client )
{
this.client = client;
stream = client.GetStream();
}
#region Send and Receive
public TResponse SendReceive<TRequest, TResponse>( TRequest request ) where TRequest : Message where TResponse : Message
{
Send( request );
return Receive<TResponse>();
}
public void Send<TRequest>( TRequest request ) where TRequest : Message
{
using( var ms = new MemoryStream())
{
Serializer.SerializeWithLengthPrefix( ms, request, PrefixStyle.Fixed32 );
stream.Write( ms.GetBuffer(), 0, (int) ms.Length );
stream.Flush();
}
}
public TResponse Receive<TResponse>() where TResponse : Message
{
try
{
return GetMessage<TResponse>();
}
catch (Exception ex)
{
if (ex is IOException || ex is InvalidOperationException)
{
stream.Dispose();
}
throw;
}
}
#endregion
#region Helpers
private TMessage GetMessage<TMessage>() where TMessage : Message
{
int messageLength = BitConverter.ToInt32(GetBytes(stream, 4), 0);
byte[] data = GetBytes(stream, messageLength);
using (var ms = new MemoryStream(data))
{
return Serializer.Deserialize<TMessage>(ms);
}
}
private static byte[] GetBytes(NetworkStream stream, int length)
{
int bytesRequired = length;
int bytesRead = 0;
var bytes = new byte[length];
do
{
while( !stream.DataAvailable )
Thread.Sleep( 100 );
int read = stream.Read(bytes, bytesRead, bytesRequired);
bytesRequired -= read;
bytesRead += read;
}
while (bytesRequired > 0);
return bytes;
}
#endregion
}
注意:Serializer类来自ProtoBuf库。
答案 2 :(得分:0)
如果您不想或不能使用BinaryFormatter,则必须反序列化对象yoursekf
二进制格式化程序的msdn修改示例:
假设你有模特儿:
[Serializable] //you could also make the class implement ISerializable
class SomeModel
{
public String Name
{
get;
set;
}
}
你有你的NetworkStream:
NetworkStream ns;
如何进行序列化部分的基本示例:
void Serialize()
{
SomeModel myModel = new SomeModel()
{
Name = "mooo"
};
// Construct a BinaryFormatter and use it to serialize the data to the stream.
BinaryFormatter formatter = new BinaryFormatter();
try
{
formatter.Serialize(ns, myModel);
}
catch (SerializationException e)
{
throw e;
}
}
和反序列化
void Deserialize()
{
SomeModel myModel;
try
{
BinaryFormatter formatter = new BinaryFormatter();
// Deserialize the object from the stream and
// assign the reference to the local variable.
myModel = (SomeModel) formatter.Deserialize(ns);
}
catch (SerializationException e)
{
throw e;
}
}
有一件事你必须要小心。 您必须知道何时反序列化,您不能只是将流放入其中并获得某些东西,您必须知道您要阅读的内容实际上是序列化的SomeModel类。 我不知道你的代码是怎么样的,所以我不知道这是不是一个问题,如果它是如何避免它。
无论如何,你的第二选择是使用BinaryReader。 您必须创建对象的实例,并手动读取数据。
using(BinaryWriter writer =
new BinaryWriter(ns))
{
binWriter.Write(myModel.Name);
}
读数:
using(BinaryReader reader =
new BinaryReader(ns))
{
try
{
SomeModel myModel = new SomeModel();
myModel.Name = binReader.ReadString();
}
// If the end of the stream is reached before reading
// the four data values, ignore the error and use the
// default settings for the remaining values.
catch(EndOfStreamException e)
{
Console.WriteLine("{0} caught and ignored. " +
"Using default values.", e.GetType().Name);
}
}