C# 如何通过TCP流发送对象?

C# 如何通过TCP流发送对象?,c#,tcp,C#,Tcp,我正在尝试设置一个接口,以便使用TCP连接在客户端和服务器之间发送序列化对象 我使用以下类和扩展方法从流中读取和写入这些对象: public class NetworkTransmittedUpdate { /// <summary> /// The type of update that is being sent /// </summary> public UpdateType UpdateType { get; init; }

我正在尝试设置一个接口,以便使用TCP连接在客户端和服务器之间发送序列化对象

我使用以下类和扩展方法从流中读取和写入这些对象:

public class NetworkTransmittedUpdate
{
    /// <summary>
    /// The type of update that is being sent
    /// </summary>
    public UpdateType UpdateType { get; init; }

    /// <summary>
    /// The type that UpdateObject should be deserialized as. Should match one of the types in SerializedTypes
    /// </summary>
    public string UpdateObjectType { get; init; } = string.Empty;

    /// <summary>
    /// Any information that accompanies the update. Each update type has an associated UpdateObject type, which is
    /// usually but not always the same for both frontend and backend. UpdateObject may be null if only the event
    /// happening must be conveyed.
    /// </summary>
    public object? UpdateObject { get; init; }
}

public static class StreamExtensions
{
    /// <summary>
    /// Attempts to read a serialized object from the stream, convert it, and return it
    /// </summary>
    /// <param name="stream">The stream to read from</param>
    /// <typeparam name="T">The type of object to deserialize</typeparam>
    /// <returns>An instance of the object, or null if it could not be created</returns>
    public static T? ReadObject<T>(this Stream stream) where T : class
    {
        // Get length of incoming object
        var sizeBuffer = new byte[sizeof(int)];
        var bytesRead = 0;
        Debug.Assert(stream.CanRead);
        while (bytesRead < sizeBuffer.Length)
            bytesRead += stream.Read(sizeBuffer, bytesRead, sizeBuffer.Length - bytesRead);
        // Create a buffer for serialized data
        var serializationLength = BitConverter.ToInt32(sizeBuffer);
        var serializedDataBuffer = new byte[serializationLength];
        // Get data from the stream
        bytesRead = 0;
        while (bytesRead < serializationLength)
            bytesRead += stream.Read(serializedDataBuffer, bytesRead, serializationLength - bytesRead);
        // Deserialize data into an object
        var json = Encoding.UTF8.GetString(serializedDataBuffer);

        // Deserialize the wrapped type correctly using reflection
        var deserializedObject = JsonSerializer.Deserialize<T>(json);
        if (deserializedObject is NetworkTransmittedUpdate {UpdateObject: { }} dynamicUpdateWrapper)
        {
            // In this block, we know we are wrapping data. The deserializer doesn't choose the write type by default, so we need to create a new 
            var innerData = dynamicUpdateWrapper.UpdateObject!.ToString();
            var innerDataType = Type.GetType(dynamicUpdateWrapper.UpdateObjectType);
            return new NetworkTransmittedUpdate
            {
                UpdateType = dynamicUpdateWrapper.UpdateType,
                UpdateObjectType = dynamicUpdateWrapper.UpdateObjectType,
                UpdateObject = JsonSerializer.Deserialize(innerData ?? string.Empty, innerDataType!)
            } as T;
        }

        return deserializedObject;
    }

    /// <summary>
    /// Serializes and writes an object to a stream
    /// </summary>
    /// <param name="stream">The stream to write UTF-8 data to</param>
    /// <param name="value">An object to serialize and write</param>
    /// <typeparam name="T">A serializable type</typeparam>
    public static void WriteObject<T>(this Stream stream, T value)
    {
        var json = JsonSerializer.Serialize(value);
        var bytes = Encoding.UTF8.GetBytes(json);
        // Always send a number of bytes ahead, so the other side knows how much to read
        stream.Write(BitConverter.GetBytes(bytes.Length));
        // Send serialized data
        stream.Write(bytes);
        stream.Flush();
    }
}
公共类网络传输数据
{
/// 
///正在发送的更新的类型
/// 
public UpdateType UpdateType{get;init;}
/// 
///UpdateObject应反序列化为的类型。应与SerializedTypes中的一种类型匹配
/// 
公共字符串UpdateObjectType{get;init;}=string.Empty;
/// 
///随更新而来的任何信息。每个更新类型都有一个关联的UpdateObject类型,即
///对于前端和后端,通常是相同的,但并不总是相同的。如果只有
///必须传达发生的事情。
/// 
公共对象?更新对象{get;init;}
}
公共静态类StreamExtensions
{
/// 
///尝试从流中读取序列化对象,将其转换并返回
/// 
///要读取的流
///要反序列化的对象的类型
///对象的实例,如果无法创建,则为null
公共静态T?ReadObject(此流),其中T:class
{
//获取传入对象的长度
var sizeBuffer=新字节[sizeof(int)];
var bytesRead=0;
Assert(stream.CanRead);
while(字节读取
由于某种原因,下面的测试在读取要发送的对象的长度时被卡住

[Fact]
public void TestChatMessageNetworkPackets()
{
    // Setup
    var listener = new TcpListener(IPAddress.Any, 12321);
    listener.Start();
    using var client = new TcpClient("localhost", 12321);
    using var server = listener.AcceptTcpClient();
    using var clientStream = client.GetStream();
    using var serverStream = server.GetStream();
    
    // Send and receive chat message
    clientStream.WriteObject(new NetworkTransmittedUpdate()
    {
        UpdateObject = new string("Test message"),
        UpdateType = UpdateType.ChatMessage,
        UpdateObjectType = typeof(string).FullName!
    });

    var update = clientStream.ReadObject<NetworkTransmittedUpdate>();
}
[事实]
public void TestChatMessageNetworkPackets()
{
//设置
var listener=newtcplistener(IPAddress.Any,12321);
listener.Start();
使用var client=newtcpclient(“localhost”,12321);
使用var server=listener.AcceptTcpClient();
使用var clientStream=client.GetStream();
使用var serverStream=server.GetStream();
//发送和接收聊天信息
clientStream.WriteObject(新的NetworkTransmittedDupDate()
{
UpdateObject=新字符串(“测试消息”),
UpdateType=UpdateType.ChatMessage,
UpdateObjectType=typeof(string).FullName!
});
var update=clientStream.ReadObject();
}
更改此选项:

var update = clientStream.ReadObject<NetworkTransmittedUpdate>();
var update=clientStream.ReadObject();
为此:

var update = serverStream.ReadObject<NetworkTransmittedUpdate>();
var update=serverStream.ReadObject();

你想在客户端流上写,然后在服务器流上读,因为服务器流在围栏的另一端。

你将它们序列化为字节,发送它们并在另一端反序列化。与其发明自己的第7层网络协议,为什么不使用现成的东西,比如HTTP?