v10.10.2024.0601 优化Json序列化,支持DateOnly/TimeOnly,支持带时区的时间序列化
石头 编写于 2024-06-01 08:10:50
X
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using NewLife;
using NewLife.Data;
using NewLife.Log;
using NewLife.Messaging;
using NewLife.Net;
using NewLife.Net.Handlers;
using NewLife.Serialization;
using Xunit;

namespace XUnitTest.Net;

public class ISocketRemoteTests
{
    private FileInfo GetFile()
    {
        FileInfo src = null;
        var di = "D:\\Tools".AsDirectory();
        if (di.Exists) src = di.GetFiles().Where(e => e.Length < 10 * 1024 * 1024).OrderByDescending(e => e.Length).FirstOrDefault();
        src ??= "../../".AsDirectory().GetFiles().Where(e => e.Length < 10 * 1024 * 1024).OrderByDescending(e => e.Length).FirstOrDefault();
        src ??= "data/".AsDirectory().GetFiles().Where(e => e.Length < 10 * 1024 * 1024).OrderByDescending(e => e.Length).FirstOrDefault();
        XTrace.WriteLine("发送文件:{0}", src.FullName);
        XTrace.WriteLine("文件大小:{0}", src.Length.ToGMK());

        return src;
    }

    [Fact]
    public void SendFile()
    {
        // 目标文件
        var file = "bigfile.bin".GetFullPath();
        if (File.Exists(file)) File.Delete(file);

        using var target = File.Create(file);

        // 简易版服务端。监听并接收文件数据,e.Message就是文件数据
        using var svr = new NetServer
        {
            Port = 12345,
            Log = XTrace.Log,
        };

        svr.Add<StandardCodec>();
        svr.Received += (s, e) =>
        {
            // 收到的所有数据全部写入文件。用户可以根据自己的协议,识别文件头和文件内容
            if (e.Message is Packet pk)
                pk.CopyTo(target);
        };

        svr.Start();

        // 本地找一个大文件
        var src = GetFile();
        var md5 = src.MD5();

        // 客户端
        var uri = new NetUri($"tcp://127.0.0.3:{svr.Port}");
        var client = uri.CreateRemote();
        client.Log = XTrace.Log;

        client.Add<StandardCodec>();
        client.Open();

        // 不能发送文件以外内容,否则服务端无法识别而直接写入文件
        //client.SendMessage($"Send File {src.Name}");

        var rs = client.SendFile(src.FullName);
        XTrace.WriteLine("分片:{0}", rs);

        //client.SendMessage($"Send File Finished!");

        Thread.Sleep(1000);

        // 验证接收文件是否完整
        target.Flush();
        target.Close();

        var dest = file.AsFile();
        dest.Refresh();
        Assert.Equal(src.Length, dest.Length);
        Assert.Equal(md5.ToHex(), file.AsFile().MD5().ToHex());
    }

    [Fact]
    public void SendFile2()
    {
        // 标准版服务端。可接受文本消息、Json对象和二进制文件数据
        using var svr = new FileServer { Port = 12346, Log = XTrace.Log };
        svr.Start();

        // 客户端
        var uri = new NetUri($"tcp://127.0.0.5:{svr.Port}");
        var client = uri.CreateRemote();
        client.Log = XTrace.Log;

        // 加入Json编码器,用于发送Json对象
        client.Add<StandardCodec>();
        client.Add<JsonCodec>();
        client.Open();

        // 发送文本字符串和对象消息
        var src = GetFile();
        client.SendMessage($"Send File {src.Name}");
        client.SendMessage(new MyFileInfo { Name = src.Name, Length = src.Length });

        var rs = client.SendFile(src.FullName);
        XTrace.WriteLine("分片:{0}", rs);

        // 发送完成消息,也可以是Json消息
        client.SendMessage($"Send File Finished!");
        Thread.Sleep(1000);

        // 验证接收文件是否完整
        var dest = svr.Files[^1].AsFile();
        Assert.Equal(src.Length, dest.Length);
        Assert.Equal(src.MD5().ToHex(), dest.MD5().ToHex());
    }

    class FileServer : NetServer<FileSession>
    {
        public IList<String> Files { get; set; } = [];

        protected override void OnStart()
        {
            // 标准编码器不要返回内部数据包,而是直接返回消息对象,此时e.Message就是DefaultMessage
            Add(new StandardCodec { UserPacket = false });

            base.OnStart();
        }
    }

    class FileSession : NetSession<FileServer>
    {
        private MyFileInfo _info;
        private String _file;
        private Stream _target;

        protected override void OnReceive(ReceivedEventArgs e)
        {
            // 收到消息,识别文件头和文件内容
            if (e.Message is not DefaultMessage dm) return;

            // 借助Flag标记位区分消息类型,可用范围0~63。默认0是字符串,其它二进制
            var kind = (DataKinds)dm.Flag;
            switch (kind)
            {
                case DataKinds.String:
                    var str = dm.Payload.ToStr();
                    XTrace.WriteLine("收到:{0}", str);

                    // 接受文件完成
                    if (str.Contains("Finished"))
                    {
                        _target.SetLength(_target.Position);
                        _target.TryDispose();
                        _target = null;

                        Host.Files.Add(_file);
                    }
                    break;
                case DataKinds.Binary:
                    break;
                case DataKinds.Json:
                    // 开始创建文件
                    _info = dm.Payload.ToStr().ToJsonEntity<MyFileInfo>();
                    _file = _info.Name.GetFullPath();
                    _target = new FileStream(_file, FileMode.OpenOrCreate);
                    break;
                case DataKinds.Packet:
                default:
                    // 持续接受数据写入文件
                    if (_target != null) dm.Payload.CopyTo(_target);
                    break;
            }
        }
    }

    class MyFileInfo
    {
        public String Name { get; set; }

        public Int64 Length { get; set; }

        public String MD5 { get; set; }
    }
}