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();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|