首页 > 解决方案 > TcpClient.BeginRead 工作一次

问题描述

这是我的客户端代码。包裹只发送和接收一次。在这段代码中,我在一个端口上同时使用了两个 TCP 和 UDP 协议。我的 UDP 协议运行良好,但我的 TCP 协议有问题,只能发送和接收一次数据包。提前感谢您的指导。

这是我的代码:

private void TryClientConnectToServerCallback(IAsyncResult result)
        {
             m_TcpClient.EndConnect(result);
            if (!m_TcpClient.Connected)
            {
                return;
            }
            else
            {
                m_MyStream = m_TcpClient.GetStream();
                m_MyStream.BeginRead(m_ReceiveBuffer, 0, 4096 * 2,new AsyncCallback(ReceiveDataCallBackTcp), null);
                //m_TcpClient.Client.BeginSend(m_ReceiveBuffer, 0, 4096 * 2, SocketFlags.None, ReceiveDataCallBackUdp, null);
                m_UdpClient.BeginReceive(ReceiveDataCallBackUdp, null);
                print(m_UdpClient.Client.Connected);
            }
        }
    
    private void ReceiveDataCallBackTcp(IAsyncResult result)
            {
                
                try
                {
                    print("Data Received");
                    int m_ReadBytes = m_MyStream.EndRead(result);
                    if (m_ReadBytes <= 0)
                    {
                        return; //no take any data from client
                    }
    
                    jsonObject = new JsonObject();
                    byte[] m_NewByte = new byte[m_ReadBytes];
                    Buffer.BlockCopy(m_ReceiveBuffer, 0, m_NewByte, 0, m_ReadBytes);
    
                    string jsonData = DataConverter.ConvertToString(m_ReceiveBuffer);
    
                    SendAndReceiveSizeDataLogger(false, m_NewByte, "TCP");
                    //Console.WriteLine("Data receive form client {0}", jsonData);
                    jsonObject = JsonConvert.DeserializeObject<JsonObject>(jsonData);
                    Debug.Log(jsonObject.FunctionName);
    
                    if (m_Events.ContainsKey(jsonObject.FunctionName))
                    {
                        for (int i = 0; i < m_Events[jsonObject.FunctionName].Count; i++)
                        {
                            m_Events[jsonObject.FunctionName][i](jsonObject);
                        }
                    }
                    m_MyStream.BeginRead(m_ReceiveBuffer, 0, 4096 * 2, new AsyncCallback(ReceiveDataCallBackTcp), null);
                    //m_TcpClient.Client.BeginSend(m_ReceiveBuffer, 0, 4096 * 2, SocketFlags.None, ReceiveDataCallBackUdp, null);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }

此代码为客户端发送数据

public void SendTCP(JsonObject jsonObject)
        {
            if (!m_Socket.Connected)
            {
                Console.WriteLine("Failed to send data to server, you are not connected to the server");
                return;
            }

            //convert jsonObject class to jsonObject for send to server
            string jsonData = JsonConvert.SerializeObject(jsonObject);
            //Console.WriteLine("Data Serialized " + jsonData);
            byte[] data = new byte[4096];
            //convert data to byte array for send to server
            data = DataConverter.ConvertToByteArray(jsonData);

            Console.WriteLine("Sent Method " + m_Socket.Client.RemoteEndPoint.ToString());
            m_MyStream.BeginWrite(data, 0, jsonData.Length, SendTCPCallBack, null);

            #region Data Property
            int dataSendSize = 0;
            for (int i = 0; i < data.Length; i++)
            {
                if (data[i] > 0)
                {
                    dataSendSize++;
                }
            }
            //Console.WriteLine("Data size send to client : " + dataSendSize);
            #endregion
        }  

标签: c#.net-core

解决方案


目前还不清楚这段代码试图做什么,但它确实有几个问题。很明显,它试图从 UDP 或 TCP 客户端异步接收和反序列化数据,但它以非常复杂的方式执行此操作。

唯一清楚的是它使用的是 Json.NET,它不能异步反序列化对象。

简单地读取和反序列化 UPD 消息只需要几行:

var result=await udpClient.ReceiveAsync();
var jsonData=Encoding.UTF8.GetString(result.Buffer);
var jsonObject = JsonConvert.DeserializeObject<JsonObject>(jsonData);

TCP虽然没有消息,但它是一个连续的字节流。如果连接仅用于检索单个文档,我们可以一直读取直到流关闭并反序列化数据:

await tcpClient.ConnectAsync(...);
using var stream=tcpClient.GetStream();
//Uses UTF8 by default
using var sr=new StreamReader(stream);
var serializer = new JsonSerializer();
var jsonObject=serializer.Deserialize<JsonObject>(jsonData);

如果服务器发送多个 JSON 文档,我们需要一种方法来识别它们,并逐个反序列化它们。发送多个文档的一种非常常见的方法是使用未缩进的单行文档并用换行符分隔它们,例如:

{"EventId":123,"Category":"Business","Level":"Information"}
{"EventId":123,"Category":"Business","Level":"Information"}
{"EventId":123,"Category":"Business","Level":"Information"}

在这种情况下,我们可以使用 a 一次StreamReader读取一行并反序列化它们:

await tcpClient.ConnectAsync(...);
using var stream=tcpClient.GetStream();
//Uses UTF8 by default
using var sr=new StreamReader(stream);
while(true)
{
    var line=await sr.ReadLineAsync();
    var jsonObject=JsonConvert.DeserializeObject<JsonObject>(jsonData);
    ...
}

与 System.Text.Json 完全异步

.NET Core 3.1 引入了完全异步的 System.Text.Json。可以重写 TCP 代码以使用JsonSerializer.DeserializeAsync

await tcpClient.ConnectAsync(...);
using var utf8Stream=tcpClient.GetStream();
var jsonObject=await JsonSerializer.DeserializeAsync<JsonObject>(utf8Stream);

System.Text.Json仅适用于 UTF-8,毕竟这是所有 Web 应用程序的事实上的标准编码。

没有DeserializeAsync重载接受字符串,因为当字符串已经在内存中时不涉及 IO。UDP 或流式 JSON 代码类似于 JSON.NET:

await tcpClient.ConnectAsync(...);
using var stream=tcpClient.GetStream();
//Uses UTF8 by default
using var sr=new StreamReader(stream);
while(true)
{
    var line=await sr.ReadLineAsync();
    var jsonObject=JsonSerializer.Deserialize<JsonObject>(jsonData);
    ...
}

推荐阅读