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