/** * 维护kcp状态的线程 */ package org.beykery.jkcp; import io.netty.buffer.ByteBuf; import io.netty.channel.socket.DatagramPacket; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; /** * @author beykery */ public class KcpThread extends Thread { private final Output out; private final LinkedBlockingQueue<DatagramPacket> inputs; private volatile boolean running; private final Map<InetSocketAddress, KcpOnUdp> kcps; private final KcpListerner listerner; private int nodelay; private int interval = Kcp.IKCP_INTERVAL; private int resend; private int nc; private int sndwnd = Kcp.IKCP_WND_SND; private int rcvwnd = Kcp.IKCP_WND_RCV; private int mtu = Kcp.IKCP_MTU_DEF; private boolean stream; private int minRto = Kcp.IKCP_RTO_MIN; private long timeout;//idle private final Object lock;//锁 private final InetSocketAddress local;//本地地址 /** * fastest: ikcp_nodelay(kcp, 1, 20, 2, 1) nodelay: 0:disable(default), * 1:enable interval: internal update timer interval in millisec, default is * 100ms resend: 0:disable fast resend(default), 1:enable fast resend nc: * 0:normal congestion control(default), 1:disable congestion control * * @param nodelay * @param interval * @param resend * @param nc */ public void noDelay(int nodelay, int interval, int resend, int nc) { this.nodelay = nodelay; this.interval = interval; this.resend = resend; this.nc = nc; } /** * set maximum window size: sndwnd=32, rcvwnd=32 by default * * @param sndwnd * @param rcvwnd */ public void wndSize(int sndwnd, int rcvwnd) { this.sndwnd = sndwnd; this.rcvwnd = rcvwnd; } /** * change MTU size, default is 1400 * * @param mtu */ public void setMtu(int mtu) { this.mtu = mtu; } /** * kcp工作线程 * * @param out * @param listerner * @param local */ public KcpThread(Output out, KcpListerner listerner, InetSocketAddress local) { this.out = out; this.listerner = listerner; inputs = new LinkedBlockingQueue<>(); kcps = new HashMap<>(); this.lock = new Object(); this.local = local; } /** * 开启线程 */ @Override public synchronized void start() { if (!this.running) { this.running = true; super.start(); } } /** * 关闭线程 */ public void close() { this.running = false; } @Override public void run() { while (this.running) { long st = System.currentTimeMillis(); //input while (!this.inputs.isEmpty()) { DatagramPacket dp = this.inputs.remove(); KcpOnUdp ku = this.kcps.get(dp.sender()); ByteBuf content = dp.content(); if (ku == null) { ku = new KcpOnUdp(this.out, dp.sender(), local, this.listerner);//初始化 ku.noDelay(nodelay, interval, resend, nc); ku.wndSize(sndwnd, rcvwnd); ku.setMtu(mtu); // conv应该在客户端第一次建立时获取 int conv = content.getIntLE(0); ku.setConv(conv); ku.setMinRto(minRto); ku.setStream(stream); ku.setTimeout(timeout); this.kcps.put(dp.sender(), ku); } ku.input(content); } //update KcpOnUdp temp = null; for (KcpOnUdp ku : this.kcps.values()) { if (ku.isClosed()) { temp = ku; } else { ku.update(); } } if (temp != null)//删掉过时的kcp { this.kcps.remove((InetSocketAddress) temp.getKcp().getUser()); } if (inputs.isEmpty())//如果输入为空则考虑wait { long end = System.currentTimeMillis(); if (end - st < this.interval) { synchronized (this.lock) { try { lock.wait(interval - end + st); } catch (InterruptedException e) { } } } } } release(); } /** * 收到输入 */ void input(DatagramPacket dp) { if (this.running) { this.inputs.add(dp); synchronized (this.lock) { lock.notify(); } } else { dp.release(); } } /** * stream mode * * @param stream */ public void setStream(boolean stream) { this.stream = stream; } public boolean isStream() { return stream; } public void setMinRto(int minRto) { this.minRto = minRto; } public void setTimeout(long timeout) { this.timeout = timeout; } public long getTimeout() { return timeout; } /** * 释放所有内存 */ private void release() { for (DatagramPacket dp : this.inputs) { dp.release(); } this.inputs.clear(); for (KcpOnUdp ku : this.kcps.values()) { if (!ku.isClosed()) { ku.release(); } } this.kcps.clear(); } }