390 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			390 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
using System;
 | 
						||
using System.Collections.Generic;
 | 
						||
using System.Threading;
 | 
						||
using DotNetty.Buffers;
 | 
						||
using DotNetty.Common.Utilities;
 | 
						||
using base_kcp;
 | 
						||
 | 
						||
namespace fec
 | 
						||
{
 | 
						||
    public class LatencySimulator : KcpOutput
 | 
						||
    {
 | 
						||
        private static long long2Uint(long n)
 | 
						||
        {
 | 
						||
            return n & 0x00000000FFFFFFFFL;
 | 
						||
        }
 | 
						||
 | 
						||
        private long current;
 | 
						||
 | 
						||
        /**
 | 
						||
         * 丢包率
 | 
						||
         **/
 | 
						||
        private int lostrate;
 | 
						||
        private int rttmin;
 | 
						||
        private int rttmax;
 | 
						||
        private LinkedList<DelayPacket> p12 = new LinkedList<DelayPacket>();
 | 
						||
        private LinkedList<DelayPacket> p21 = new LinkedList<DelayPacket>();
 | 
						||
        private Random r12 = new Random();
 | 
						||
        private Random r21 = new Random();
 | 
						||
 | 
						||
        private Random _random = new Random();
 | 
						||
 | 
						||
 | 
						||
        // lostrate: 往返一周丢包率的百分比,默认 10%
 | 
						||
        // rttmin:rtt最小值,默认 60
 | 
						||
        // rttmax:rtt最大值,默认 125
 | 
						||
        //func (p *LatencySimulator)Init(int lostrate = 10, int rttmin = 60, int rttmax = 125, int nmax = 1000):
 | 
						||
        public void init(int lostrate, int rttmin, int rttmax)
 | 
						||
        {
 | 
						||
            this.current = DateTime.Now.Ticks/10000;
 | 
						||
            this.lostrate = lostrate / 2; // 上面数据是往返丢包率,单程除以2
 | 
						||
            this.rttmin = rttmin / 2;
 | 
						||
            this.rttmax = rttmax / 2;
 | 
						||
        }
 | 
						||
 | 
						||
 | 
						||
        // 发送数据
 | 
						||
        // peer - 端点0/1,从0发送,从1接收;从1发送从0接收
 | 
						||
        public int send(int peer, IByteBuffer data)
 | 
						||
        {
 | 
						||
            int rnd;
 | 
						||
            if (peer == 0)
 | 
						||
            {
 | 
						||
                rnd = r12.Next(100);
 | 
						||
            }
 | 
						||
            else
 | 
						||
            {
 | 
						||
                rnd = r21.Next(100);
 | 
						||
            }
 | 
						||
 | 
						||
            //println("!!!!!!!!!!!!!!!!!!!!", rnd, p.lostrate, peer)
 | 
						||
            if (rnd < lostrate)
 | 
						||
            {
 | 
						||
                return 0;
 | 
						||
            }
 | 
						||
 | 
						||
            DelayPacket pkt = new DelayPacket();
 | 
						||
            pkt.init(data);
 | 
						||
            current = DateTime.Now.Ticks/10000;
 | 
						||
            int delay = rttmin;
 | 
						||
            if (rttmax > rttmin)
 | 
						||
            {
 | 
						||
                delay += _random.Next(10000) % (rttmax - rttmin);
 | 
						||
            }
 | 
						||
 | 
						||
            pkt.setTs(current + delay);
 | 
						||
            if (peer == 0)
 | 
						||
            {
 | 
						||
                p12.AddLast(pkt);
 | 
						||
            }
 | 
						||
            else
 | 
						||
            {
 | 
						||
                p21.AddLast(pkt);
 | 
						||
            }
 | 
						||
 | 
						||
            return 1;
 | 
						||
        }
 | 
						||
 | 
						||
        // 接收数据
 | 
						||
        public int recv(int peer, IByteBuffer data)
 | 
						||
        {
 | 
						||
            DelayPacket pkt;
 | 
						||
            if (peer == 0)
 | 
						||
            {
 | 
						||
                if (p21.Count == 0)
 | 
						||
                {
 | 
						||
                    return -1;
 | 
						||
                }
 | 
						||
 | 
						||
                pkt = p21.First.Value;
 | 
						||
            }
 | 
						||
            else
 | 
						||
            {
 | 
						||
                if (p12.Count == 0)
 | 
						||
                {
 | 
						||
                    return -1;
 | 
						||
                }
 | 
						||
 | 
						||
                pkt = p12.First.Value;
 | 
						||
            }
 | 
						||
 | 
						||
            current = DateTime.Now.Ticks/10000;
 | 
						||
 | 
						||
            if (current < pkt.getTs())
 | 
						||
            {
 | 
						||
                return -2;
 | 
						||
            }
 | 
						||
 | 
						||
            if (peer == 0)
 | 
						||
            {
 | 
						||
                p21.RemoveFirst();
 | 
						||
            }
 | 
						||
            else
 | 
						||
            {
 | 
						||
                p12.RemoveFirst();
 | 
						||
            }
 | 
						||
 | 
						||
            int maxsize = pkt.getPtr().ReadableBytes;
 | 
						||
//            IByteBuffer data1 = data;
 | 
						||
//            IByteBuffer data2 = pkt.getPtr();
 | 
						||
//            Console.WriteLine(data1.AddressOfPinnedMemory().ToString());
 | 
						||
//            Console.WriteLine(data2.AddressOfPinnedMemory().ToString());
 | 
						||
 | 
						||
            data.WriteBytes(pkt.getPtr());
 | 
						||
//            data2.Release();
 | 
						||
            pkt.Release();
 | 
						||
//            Console.WriteLine(data.ReferenceCount);
 | 
						||
            return maxsize;
 | 
						||
        }
 | 
						||
 | 
						||
 | 
						||
        public static void Main(String[] args)
 | 
						||
        {
 | 
						||
//            LatencySimulator latencySimulator = new LatencySimulator();
 | 
						||
//            try
 | 
						||
//            {
 | 
						||
////                //latencySimulator.test(0);
 | 
						||
////                //latencySimulator.test(1);
 | 
						||
//                latencySimulator.test(2);
 | 
						||
//            }
 | 
						||
//            catch (Exception e)
 | 
						||
//            {
 | 
						||
//                Console.WriteLine(e);
 | 
						||
//            }
 | 
						||
 | 
						||
//            latencySimulator.BenchmarkFlush();
 | 
						||
        }
 | 
						||
 | 
						||
 | 
						||
        //测试flush性能
 | 
						||
        public void BenchmarkFlush()
 | 
						||
        {
 | 
						||
            Kcp kcp = new Kcp(1, new LatencySimulator());
 | 
						||
            for (int i = 0; i < 1000; i++)
 | 
						||
            {
 | 
						||
                Segment segment = Segment.createSegment(null);
 | 
						||
                kcp.sndBuf.AddLast(segment);
 | 
						||
            }
 | 
						||
 | 
						||
            foreach (var seg in kcp.sndBuf)
 | 
						||
            {
 | 
						||
                seg.Xmit = 1;
 | 
						||
                seg.Resendts = kcp.currentMs() + 10000;
 | 
						||
            }
 | 
						||
 | 
						||
            //预热
 | 
						||
            for (int i = 0; i < 1000000; i++)
 | 
						||
            {
 | 
						||
                kcp.flush(false, kcp.currentMs());
 | 
						||
            }
 | 
						||
 | 
						||
            long start = kcp.currentMs();
 | 
						||
            for (int i = 0; i < 200000; i++)
 | 
						||
            {
 | 
						||
                kcp.flush(false, kcp.currentMs());
 | 
						||
            }
 | 
						||
 | 
						||
            Console.WriteLine((kcp.currentMs() - start) / 200000);
 | 
						||
        }
 | 
						||
 | 
						||
 | 
						||
 | 
						||
        class TestOutPut:KcpOutput
 | 
						||
        {
 | 
						||
            private LatencySimulator vnet;
 | 
						||
            private int id;
 | 
						||
 | 
						||
            public TestOutPut(LatencySimulator vnet,int id)
 | 
						||
            {
 | 
						||
                this.vnet = vnet;
 | 
						||
                this.id = id;
 | 
						||
            }
 | 
						||
 | 
						||
            public void outPut(IByteBuffer data, Kcp kcp)
 | 
						||
            {
 | 
						||
                vnet.send(id, data);
 | 
						||
                data.Release();
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
 | 
						||
 | 
						||
 | 
						||
        public void test(int mode)
 | 
						||
        {
 | 
						||
            LatencySimulator vnet = new LatencySimulator();
 | 
						||
            vnet.init(20, 600, 600);
 | 
						||
            TestOutPut output1 = new TestOutPut(vnet, 0);
 | 
						||
            TestOutPut output2 = new TestOutPut(vnet, 1);
 | 
						||
 | 
						||
 | 
						||
            Kcp kcp1 = new Kcp(0x11223344, output1);
 | 
						||
            Kcp kcp2 = new Kcp(0x11223344, output2);
 | 
						||
            //kcp1.setAckMaskSize(8);
 | 
						||
            //kcp2.setAckMaskSize(8);
 | 
						||
 | 
						||
            current = long2Uint(kcp1.currentMs());
 | 
						||
            long slap = current + 20;
 | 
						||
            int index = 0;
 | 
						||
            int next = 0;
 | 
						||
            long sumrtt = 0;
 | 
						||
            int count = 0;
 | 
						||
            int maxrtt = 0;
 | 
						||
            kcp1.RcvWnd = 512;
 | 
						||
            kcp1.SndWnd = 512;
 | 
						||
            kcp2.RcvWnd = 512;
 | 
						||
            kcp2.SndWnd = 512;
 | 
						||
 | 
						||
            // 判断测试用例的模式
 | 
						||
            if (mode == 0)
 | 
						||
            {
 | 
						||
                // 默认模式
 | 
						||
                kcp1.initNodelay(false, 10, 0, false);
 | 
						||
                kcp2.initNodelay(false, 10, 0, false);
 | 
						||
            }
 | 
						||
            else if (mode == 1)
 | 
						||
            {
 | 
						||
                // 普通模式,关闭流控等
 | 
						||
                kcp1.initNodelay(false, 10, 0, true);
 | 
						||
                kcp2.initNodelay(false, 10, 0, true);
 | 
						||
            }
 | 
						||
            else
 | 
						||
            {
 | 
						||
                // 启动快速模式
 | 
						||
                // 第二个参数 nodelay-启用以后若干常规加速将启动
 | 
						||
                // 第三个参数 interval为内部处理时钟,默认设置为 10ms
 | 
						||
                // 第四个参数 resend为快速重传指标,设置为2
 | 
						||
                // 第五个参数 为是否禁用常规流控,这里禁止
 | 
						||
                kcp1.initNodelay(true, 10, 2, true);
 | 
						||
                kcp2.initNodelay(true, 10, 2, true);
 | 
						||
                kcp1.RxMinrto = 10;
 | 
						||
                kcp1.Fastresend = 1;
 | 
						||
            }
 | 
						||
 | 
						||
            int hr;
 | 
						||
            long ts1 = kcp1.currentMs();
 | 
						||
 | 
						||
            //写数据 定时更新
 | 
						||
            for (;;)
 | 
						||
            {
 | 
						||
                current = long2Uint(kcp1.currentMs());
 | 
						||
                Thread.Sleep(1);
 | 
						||
                long now = kcp1.currentMs();
 | 
						||
                kcp1.update(now);
 | 
						||
                kcp2.update(now);
 | 
						||
 | 
						||
 | 
						||
                //每隔 20ms,kcp1发送数据
 | 
						||
                for (; current >= slap; slap += 20)
 | 
						||
                {
 | 
						||
                    IByteBuffer buf = PooledByteBufferAllocator.Default.Buffer();
 | 
						||
                    buf.WriteIntLE(index);
 | 
						||
                    index++;
 | 
						||
                    buf.WriteIntLE((int) current);
 | 
						||
                    kcp1.send(buf);
 | 
						||
                    buf.Release();
 | 
						||
                }
 | 
						||
 | 
						||
                //处理虚拟网络:检测是否有udp包从p1->p2
 | 
						||
                for (;;)
 | 
						||
                {
 | 
						||
                    IByteBuffer buffer = PooledByteBufferAllocator.Default.DirectBuffer(2000);
 | 
						||
//                    Console.WriteLine("buffer:" +buffer.AddressOfPinnedMemory().ToString());
 | 
						||
                    try
 | 
						||
                    {
 | 
						||
                        hr = vnet.recv(1, buffer);
 | 
						||
                        if (hr < 0)
 | 
						||
                        {
 | 
						||
                            break;
 | 
						||
                        }
 | 
						||
 | 
						||
                        kcp2.input(buffer, true, kcp1.currentMs());
 | 
						||
                    }
 | 
						||
                    finally
 | 
						||
                    {
 | 
						||
                        buffer.Release();
 | 
						||
                    }
 | 
						||
                }
 | 
						||
 | 
						||
                // 处理虚拟网络:检测是否有udp包从p2->p1
 | 
						||
                for (;;)
 | 
						||
                {
 | 
						||
                    IByteBuffer buffer = PooledByteBufferAllocator.Default.Buffer(2000);
 | 
						||
                    try
 | 
						||
                    {
 | 
						||
                        hr = vnet.recv(0, buffer);
 | 
						||
                        if (hr < 0)
 | 
						||
                        {
 | 
						||
                            break;
 | 
						||
                        }
 | 
						||
 | 
						||
                        // 如果 p1收到udp,则作为下层协议输入到kcp1
 | 
						||
                        kcp1.input(buffer, true, kcp1.currentMs());
 | 
						||
                    }
 | 
						||
                    finally
 | 
						||
                    {
 | 
						||
                        buffer.Release();
 | 
						||
                    }
 | 
						||
                }
 | 
						||
 | 
						||
                // kcp2接收到任何包都返回回去
 | 
						||
                List<IByteBuffer> bufList = new List<IByteBuffer>();
 | 
						||
                kcp2.recv(bufList);
 | 
						||
                foreach (var byteBuf in bufList)
 | 
						||
                {
 | 
						||
                    kcp2.send(byteBuf);
 | 
						||
                    byteBuf.Release();
 | 
						||
                }
 | 
						||
 | 
						||
                // kcp1收到kcp2的回射数据
 | 
						||
                bufList = new List<IByteBuffer>();
 | 
						||
                kcp1.recv(bufList);
 | 
						||
                foreach (var byteBuf in bufList){
 | 
						||
                    long sn = byteBuf.ReadIntLE();
 | 
						||
                    long ts = byteBuf.ReadUnsignedIntLE();
 | 
						||
                    long rtt = 0;
 | 
						||
                    rtt = current - ts;
 | 
						||
                    Console.WriteLine("rtt :" + rtt);
 | 
						||
 | 
						||
 | 
						||
                    if (sn != next)
 | 
						||
                    {
 | 
						||
                        // 如果收到的包不连续
 | 
						||
                        //for i:=0;i<8 ;i++ {
 | 
						||
                        //println("---", i, buffer[i])
 | 
						||
                        //}
 | 
						||
                        Console.WriteLine("ERROR sn " + count + "<->" + next + sn);
 | 
						||
                        return;
 | 
						||
                    }
 | 
						||
 | 
						||
                    next++;
 | 
						||
                    sumrtt += rtt;
 | 
						||
                    count++;
 | 
						||
                    if (rtt > maxrtt)
 | 
						||
                    {
 | 
						||
                        maxrtt = (int) rtt;
 | 
						||
                    }
 | 
						||
 | 
						||
                    byteBuf.Release();
 | 
						||
                }
 | 
						||
                if (next > 1000)
 | 
						||
                {
 | 
						||
                    break;
 | 
						||
                }
 | 
						||
            }
 | 
						||
 | 
						||
            ts1 = kcp1.currentMs() - ts1;
 | 
						||
            String[] names = new String[] {"default", "normal", "fast"};
 | 
						||
            Console.WriteLine(names[mode]+" mode result :"+ts1+" \n");
 | 
						||
            Console.WriteLine("avgrtt="+(sumrtt / count)+" maxrtt="+maxrtt+" \n");
 | 
						||
            Console.WriteLine("lost percent: " + (Snmp.snmp.RetransSegs)+"\n");
 | 
						||
            Console.WriteLine("snmp: " + (Snmp.snmp.ToString()));
 | 
						||
        }
 | 
						||
 | 
						||
 | 
						||
        public void outPut(IByteBuffer data, Kcp kcp)
 | 
						||
        {
 | 
						||
            throw new NotImplementedException();
 | 
						||
        }
 | 
						||
    }
 | 
						||
} |