从网络流反序列化数据使用Newtonsoft.Json挂起

时间:2015-12-07 09:33:18

标签: c# json sockets json.net

使用JsonSerializer通过套接字收到的数据:

var stream = new NetworkStream(socket);
var serializer = new JsonSerializer();

using (var sr = new StreamReader(stream, new UTF8Encoding(), false))
using (var jsonTextReader = new JsonTextReader(sr))
{
    var result = serializer.Deserialize(jsonTextReader).ToString();                
}

但是当从套接字接收的数据长度大约是2890字节时,对Deserialize的调用会挂起。

使用此代码可以轻松复制(当jsonArrayElementsCount = 192时 - 好,当193 - 它挂起时):

using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Newtonsoft.Json;

namespace JsonNetHangsTest
{
    class Program
    {
        static void Main(string[] args)
        {
            const int port = 11999;
            const int jsonArrayElementsCount = 193;

            var serverStartedEvent = new ManualResetEvent(false);
            var clientReceivedEvent = new ManualResetEvent(false);

#region server
            ThreadPool.QueueUserWorkItem(work =>
            {
                var server = new TcpListener(IPAddress.Parse("0.0.0.0"), port);
                server.Start();

                serverStartedEvent.Set();

                var serverSocket = server.AcceptSocket();

                var jsonString = "[\r\n" + String.Join(",", Enumerable.Repeat("  \"testdata\"\r\n", jsonArrayElementsCount)) + "]";
                var bytes = new UTF8Encoding().GetBytes(jsonString);
                serverSocket.Send(bytes);
                Console.WriteLine("server send: " + bytes.Length);

                clientReceivedEvent.WaitOne();

            });
#endregion

            serverStartedEvent.WaitOne();

            var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Connect("127.0.0.1", port);

            var stream = new NetworkStream(socket);
            var serializer = new JsonSerializer();

            using (var sr = new StreamReader(stream, new UTF8Encoding(), false))
            using (var jsonTextReader = new JsonTextReader(sr))
            {
                var result = serializer.Deserialize(jsonTextReader).ToString();
                Console.WriteLine("client receive: " + new UTF8Encoding().GetBytes(result).Length);
            }

            clientReceivedEvent.Set();
            Console.ReadKey();
        }
    }
}

我在这里缺少什么?

1 个答案:

答案 0 :(得分:1)

您的问题是您发送的JSON的尾端正在服务器端缓冲。来自docs

  

也无法保证您发送的数据会立即显示在网络上。为了提高网络效率,底层系统可能会延迟传输,直到收集到大量的传出数据。成功完成Send方法意味着底层系统有足够的空间来缓冲网络发送的数据。

因此,您需要确保刷新套接字缓冲区。但是,Socket上没有Flush()方法。那么,该怎么办?

  1. 最简单的方法是在完成后立即处理套接字:

            int byteCount;
            using (var serverSocket = server.AcceptSocket())
            {
                byteCount = serverSocket.Send(bytes);
            }
            Console.WriteLine("server send: " + byteCount);
    

    (一旦完成一次性用品的处理,总是一个好主意。)

  2. 或者,将serverSocket包装在拥有套接字的NetworkStream中,然后处理该流(相当于相同的内容):

            var serverSocket = server.AcceptSocket();
            using (var ns = new NetworkStream(serverSocket, true))
            {
                ns.Write(bytes, 0, bytes.Length);
            }
    

    (但请参阅NetworkStream.Write vs. Socket.Send,其中建议有时需要检查serverSocket.Send(bytes)返回的值。)

  3. 您可能需要考虑处理SocketError.NoBufferSpaceAvailable。以下是有关如何执行此操作的建议:C# Examples: Socket Send and Receive [C#]