public KcpClient(uint conv) { mHandler = new KcpHandler(); mConn = new Kcp(conv, mHandler); mConn.NoDelay(1, 10, 2, 1); //fast mConn.WndSize(64, 64); mConn.SetMtu(512); }
public KcpClient(uint conv, IKcpCallback handle) { if (handle == null) { throw new Exception("The handler must be not null value"); } mHandler = handle; mConn = new Kcp(conv, mHandler); mConn.NoDelay(1, 10, 2, 1); //fast mConn.WndSize(64, 64); mConn.SetMtu(512); }
// 为了减少阅读难度,变量名尽量于 C版 统一 /* * conv 会话ID * mtu 最大传输单元 * mss 最大分片大小 * state 连接状态(0xFFFFFFFF表示断开连接) * snd_una 第一个未确认的包 * snd_nxt 待发送包的序号 * rcv_nxt 待接收消息序号 * ssthresh 拥塞窗口阈值 * rx_rttvar ack接收rtt浮动值 * rx_srtt ack接收rtt静态值 * rx_rto 由ack接收延迟计算出来的复原时间 * rx_minrto 最小复原时间 * snd_wnd 发送窗口大小 * rcv_wnd 接收窗口大小 * rmt_wnd, 远端接收窗口大小 * cwnd, 拥塞窗口大小 * probe 探查变量,IKCP_ASK_TELL表示告知远端窗口大小。IKCP_ASK_SEND表示请求远端告知窗口大小 * interval 内部flush刷新间隔 * ts_flush 下次flush刷新时间戳 * nodelay 是否启动无延迟模式 * updated 是否调用过update函数的标识 * ts_probe, 下次探查窗口的时间戳 * probe_wait 探查窗口需要等待的时间 * dead_link 最大重传次数 * incr 可发送的最大数据量 * fastresend 触发快速重传的重复ack个数 * nocwnd 取消拥塞控制 * stream 是否采用流传输模式 * * snd_queue 发送消息的队列 * rcv_queue 接收消息的队列 * snd_buf 发送消息的缓存 * rcv_buf 接收消息的缓存 * acklist 待发送的ack列表 * buffer 存储消息字节流的内存 * output udp发送消息的回调函数 */ /// <summary> /// create a new kcp control object, 'conv' must equal in two endpoint /// from the same connection. /// </summary> /// <param name="conv_"></param> /// <param name="callback"></param> public Kcp(uint conv_, IKcpCallback callback) { conv = conv_; callbackHandle = callback; snd_wnd = IKCP_WND_SND; rcv_wnd = IKCP_WND_RCV; rmt_wnd = IKCP_WND_RCV; mtu = IKCP_MTU_DEF; mss = mtu - IKCP_OVERHEAD; buffer = CreateBuffer(BufferNeedSize); rx_rto = IKCP_RTO_DEF; rx_minrto = IKCP_RTO_MIN; interval = IKCP_INTERVAL; ts_flush = IKCP_INTERVAL; ssthresh = IKCP_THRESH_INIT; dead_link = IKCP_DEADLINK; }
public void Run(uint conv, IKcpCallback kcpCallback) { _recvFromRemote = new BufferBlock <DataPackage>(); _recvFromLocal = new BufferBlock <DataPackage>(); Conv = conv; _kcp = new Kcp(conv, kcpCallback, KcpRentable.Instacne); _kcp.NoDelay(1, 10, 2, 1); // 极速模式 _kcp.WndSize(128, 128); _kcp.SetMtu(MTU); _tokenSource = new CancellationTokenSource(); // 这样写的目的,在服务器上,压解缩、加解密可以利用多核(多个KCP连接之间的压解缩、加解密是可以并行的) // update Task.Factory.StartNew(async() => { // 检测并Flush数据到远端 while (!_tokenSource.IsCancellationRequested) { try { _kcp.Update(DateTime.UtcNow); await _kcp.CheckAwait(DateTime.UtcNow, _tokenSource.Token); } catch (TaskCanceledException) { break; } catch (ObjectDisposedException e) { // 一般情况下,OutPut时使用Socket发送数据时,而Socket被关闭时会进入此分支;暂时这么处理 if (e.ObjectName == "System.Net.Sockets.Socket") { break; } Debug.LogErrorFormat("Kcp.updateTask: \r\n {0}", e); } } }, _tokenSource.Token); // input from lower level Task.Factory.StartNew(async() => { // 收到 Ack 帧,需要移除 snd_buf;收到数据帧,则需要 Flush Ack while (!_tokenSource.IsCancellationRequested) { DataPackage package = null; try { package = await _recvFromRemote.ReceiveAsync(_tokenSource.Token); var result = _kcp.Input(package.MemoryOwner.Memory.Span.Slice(package.Offset, package.Lenght)); while (result == 0) { var(memoryOwner, avalidLength) = _kcp.TryRecv(); if (memoryOwner == null) { break; } // 在此解压、解密 OnRecvKcpPackage?.Invoke(memoryOwner, avalidLength, this); } ; } catch (TaskCanceledException) { _recvFromRemote.Complete(); break; } catch (Exception e) { Debug.LogErrorFormat("Kcp.inputTask: \r\n {0}", e); } finally { if (package != null) { package.MemoryOwner.Dispose(); DataPackage.Pool.Return(package); } } } }, _tokenSource.Token); // send by user Task.Factory.StartNew(async() => { // 有可能需要发送帧,需要Flush Snd_Queue while (!_tokenSource.IsCancellationRequested) { DataPackage package = null; try { package = await _recvFromLocal.ReceiveAsync(_tokenSource.Token); // 在此压缩、加密 _kcp.Send(package.MemoryOwner.Memory.Span.Slice(0, package.Lenght)); } catch (TaskCanceledException) { _recvFromLocal.Complete(); break; } catch (Exception e) { Debug.LogErrorFormat("Kcp.sendTask: \r\n {0}", e); } finally { if (package != null) { package.MemoryOwner.Dispose(); DataPackage.Pool.Return(package); } } } }, _tokenSource.Token); }
public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null) : base(conv_, callback, rentable) { SegmentManager = Default; }
/// <summary> /// create a new kcp control object, 'conv' must equal in two endpoint /// from the same connection. /// </summary> /// <param name="conv_"></param> /// <param name="callback"></param> /// <param name="rentable">可租用内存的回调</param> public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null) : base(conv_) { callbackHandle = callback; this.rentable = rentable; }