forked from KNEMC/KLALB
405 lines
11 KiB
Java
405 lines
11 KiB
Java
package org.kne.cloud.network.klalb;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.net.BindException;
|
|
import java.net.ConnectException;
|
|
import java.net.Inet4Address;
|
|
import java.net.Inet6Address;
|
|
import java.net.InetAddress;
|
|
import java.net.InetSocketAddress;
|
|
import java.net.NoRouteToHostException;
|
|
import java.net.SocketAddress;
|
|
import java.net.SocketException;
|
|
import java.net.SocketImpl;
|
|
import java.net.SocketOptions;
|
|
import java.net.SocketTimeoutException;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.channels.ReadableByteChannel;
|
|
import java.nio.channels.WritableByteChannel;
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
import java.util.TimerTask;
|
|
import java.util.concurrent.ArrayBlockingQueue;
|
|
import java.util.concurrent.BlockingQueue;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.ScheduledFuture;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
import java.util.concurrent.locks.Lock;
|
|
import java.util.concurrent.locks.LockSupport;
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
import java.util.zip.Deflater;
|
|
import java.util.zip.DeflaterOutputStream;
|
|
import java.util.zip.Inflater;
|
|
import java.util.zip.InflaterInputStream;
|
|
|
|
import org.kne.cloud.clock.HighAccuracyClock;
|
|
import org.kne.cloud.network.NetworkPacket;
|
|
import org.kne.cloud.network.PortPair;
|
|
import org.kne.cloud.network.SpeedLimiter;
|
|
import org.kne.cloud.network.VirtualSocketImpl;
|
|
import org.kne.cloud.network.congestion.CongestionAlgorithm;
|
|
import org.kne.cloud.network.congestion.DCTCP2CongestionAlgorithm;
|
|
import org.kne.cloud.network.congestion.ReceivePacketSlidingWindow;
|
|
import org.kne.cloud.network.congestion.SendPacketSlidingWindow;
|
|
import org.kne.cloud.network.ipv6.IPv6Address;
|
|
import org.kne.cloud.network.ipv6.PacketID;
|
|
import org.kne.cloud.network.ipv6.PacketIDGenerator;
|
|
import org.kne.cloud.network.kltp.KLTPInputStream;
|
|
import org.kne.cloud.network.kltp.KLTPOutputStream;
|
|
import org.kne.cloud.network.kltp.KLTPSessionPacket;
|
|
import org.kne.cloud.network.monitor.SpeedAndTrafficAndDelayMonitorDataImpl;
|
|
import org.kne.concurrent.SpinLock;
|
|
import org.kne.concurrent.ThreadParker;
|
|
import org.kne.debug.TimeDebugger;
|
|
|
|
import com.github.f4b6a3.uuid.UuidCreator;
|
|
|
|
public class KLALBVirtualSocketImpl extends VirtualSocketImpl implements BindableConsumer<KLTPSessionPacket>{
|
|
|
|
private static final boolean debug = false;
|
|
|
|
private static final int DEFAULT_TIMEOUT=10000;
|
|
|
|
private KLALBController controller;
|
|
|
|
protected IPv6Address remoteaddr;
|
|
protected IPv6Address localaddr=IPv6Address.UNSPECIFIED;
|
|
|
|
private volatile KLTPInputStream vin;
|
|
private volatile KLTPOutputStream vout;
|
|
|
|
|
|
|
|
private boolean ignoreBindCheck;
|
|
|
|
private boolean connected;
|
|
|
|
private boolean closed;
|
|
|
|
private BlockingQueue<KLTPSessionPacket> backlogQueue;
|
|
|
|
protected boolean isListening() {
|
|
return backlogQueue != null;
|
|
}
|
|
|
|
|
|
public KLALBController getController() {
|
|
return controller;
|
|
}
|
|
public KLALBVirtualSocketImpl(KLALBController kc) {
|
|
super();
|
|
this.controller = kc;
|
|
}
|
|
|
|
@Override
|
|
public void setOption(int optID, Object value) throws SocketException {
|
|
switch(optID) {
|
|
/*case SocketOptions.TCP_NODELAY:
|
|
nodelay=(boolean) value;
|
|
break;
|
|
case SocketOptions.SO_RCVBUF:
|
|
inputchachesize=(int) value;
|
|
break;
|
|
case SocketOptions.SO_SNDBUF:
|
|
outputchachesize=(int)value;
|
|
break;*/
|
|
case SocketOptions.SO_TIMEOUT:
|
|
vin.setSoTimeout((int)value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Object getOption(int optID) throws SocketException {
|
|
switch(optID) {
|
|
/*case SocketOptions.TCP_NODELAY:
|
|
return nodelay;
|
|
case SocketOptions.SO_RCVBUF:
|
|
return inputchachesize;
|
|
case SocketOptions.SO_SNDBUF:
|
|
return outputchachesize;*/
|
|
case SocketOptions.SO_TIMEOUT:
|
|
return vin.getSoTimeout();
|
|
case SocketOptions.SO_BINDADDR:
|
|
return localaddr;
|
|
default:
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
@Override
|
|
protected void create(boolean stream) throws IOException {
|
|
if (!stream) {
|
|
throw new RuntimeException("please use KLALBVirtualDatagramSocket to process udp packet");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void connect(String host, int port) throws IOException {
|
|
connect(InetAddress.getByName(host), port);
|
|
}
|
|
|
|
@Override
|
|
protected void connect(InetAddress address, int port) throws IOException {
|
|
connect(new InetSocketAddress(address, port), DEFAULT_TIMEOUT);
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
protected void connect(SocketAddress address, int timeout) throws IOException {
|
|
if(!ignoreBindCheck)
|
|
if (!controller.getKLTPregister().checkIsBind(this)) {
|
|
bind(Inet6Address.getByName("::0"), 0);
|
|
}
|
|
port = ((InetSocketAddress) address).getPort();
|
|
this.address = (Inet6Address) ((InetSocketAddress) address).getAddress();
|
|
this.remoteaddr=IPv6Address.valueOf(this.address);
|
|
|
|
if(connected)
|
|
throw new SocketException("already connected");
|
|
controller.getKLTPregister().connect(this);
|
|
|
|
|
|
try {
|
|
vout=new KLTPOutputStream(controller, remoteaddr, KLALBUtils.createGlobalUUID());
|
|
KLTPSessionPacket session=new KLTPSessionPacket(localport,port);
|
|
session.writeToChannel(vout);
|
|
vout.flush();
|
|
System.out.println("发起连接:"+session+" "+localport);
|
|
if(timeout==0) {
|
|
timeout=DEFAULT_TIMEOUT;
|
|
}
|
|
long start=System.nanoTime();
|
|
while(vin==null) {
|
|
long curr=System.nanoTime();
|
|
if(curr-start>(timeout*1000000L)) {
|
|
throw new SocketTimeoutException("connect time out");
|
|
}
|
|
}
|
|
|
|
connected=true;
|
|
|
|
|
|
|
|
}catch(NoRouteToHostException e) {
|
|
throw e;
|
|
}catch(SocketException e) {
|
|
e.printStackTrace();
|
|
throw new ConnectException("connect refused");
|
|
}finally {
|
|
//connectionPending=false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void bind(InetAddress host, int port) throws IOException {
|
|
bind(host,port,false);
|
|
}
|
|
protected void bind(InetAddress host, int port,boolean ignoreBindCheck) throws IOException {
|
|
if (host.equals(Inet4Address.getByName("0.0.0.0"))) {
|
|
host = Inet6Address.getByName("::0");
|
|
}
|
|
if (!(host instanceof Inet6Address)) {
|
|
throw new IllegalArgumentException("invalid address type, KLALB socket can only use IPV6 virtualaddress");
|
|
}
|
|
if ((!host.isAnyLocalAddress()) && (! IPv6Address.valueOf( host).equals(controller.getSelf().getAddress()))) {
|
|
throw new BindException("must bind to self");
|
|
}
|
|
localaddr = IPv6Address.valueOf( host);
|
|
localport=port;
|
|
this.ignoreBindCheck=ignoreBindCheck;
|
|
if(!ignoreBindCheck) {
|
|
controller.getKLTPregister().bind(this);
|
|
}
|
|
//System.out.println("绑定:"+host.getHostAddress()+":"+localport);
|
|
}
|
|
|
|
@Override
|
|
public int getPort() {
|
|
return super.getPort();
|
|
}
|
|
|
|
@Override
|
|
protected void listen(int backlog) throws IOException {
|
|
backlogQueue = new ArrayBlockingQueue<>(backlog);
|
|
controller.getKLTPregister().listen(this);
|
|
address=localaddr.toInet6Address();
|
|
}
|
|
|
|
@Override
|
|
protected void accept(SocketImpl s) throws IOException {
|
|
//System.out.println("Enter accept=============================");
|
|
KLALBVirtualSocketImpl kvsi = (KLALBVirtualSocketImpl) s;
|
|
|
|
KLTPSessionPacket p=null;
|
|
while(true) {
|
|
if (isClosed())
|
|
throw new SocketException("Socket is closed");
|
|
p=backlogQueue.poll();
|
|
if(p!=null) {
|
|
break;
|
|
}
|
|
try {
|
|
Thread.sleep(1);
|
|
} catch (InterruptedException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
System.out.println(p);
|
|
KLTPInputStream input=p.getInputstream();
|
|
kvsi.address = input.getRemoteAddress().toInet6Address();
|
|
kvsi.remoteaddr=input.getRemoteAddress();
|
|
|
|
kvsi.bind(localaddr.toInet6Address(), localport,true);
|
|
kvsi.accept(p);
|
|
kvsi.connect(new InetSocketAddress(kvsi.address, p.getSrcPort()),10);
|
|
//System.out.println("Exit accept=============================");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
public InputStream getInputStream() throws IOException {
|
|
|
|
return vin;
|
|
}
|
|
|
|
@Override
|
|
public OutputStream getOutputStream() throws IOException {
|
|
return vout;
|
|
}
|
|
|
|
@Override
|
|
protected int available() throws IOException {
|
|
return getInputStream().available();
|
|
}
|
|
|
|
@Override
|
|
protected void shutdownInput() throws IOException {
|
|
throw new UnsupportedOperationException("Unsupported Shutdown Input");
|
|
//getInputStream().close0();
|
|
}
|
|
|
|
@Override
|
|
protected void shutdownOutput() throws IOException {
|
|
throw new UnsupportedOperationException("Unsupported Shutdown Output");
|
|
//getOutputStream().close0();
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "KLALBVirtualSocketImpl [vin=" + vin + ", vout=" + vout + "]";
|
|
}
|
|
|
|
|
|
@Override
|
|
protected void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
|
|
super.setPerformancePreferences(connectionTime, latency, bandwidth);
|
|
}
|
|
|
|
@Override
|
|
protected void close() throws IOException {
|
|
|
|
//new Exception().printStackTrace();
|
|
closed=true;
|
|
try {
|
|
if(vout!=null)
|
|
vout.close();
|
|
}finally {
|
|
if(vin!=null)
|
|
vin.close();
|
|
|
|
if(!isListening() ) {
|
|
|
|
controller.getKLTPregister().disconnect(this);
|
|
|
|
}else {
|
|
//System.out.println("unlisten"+getLocalPort());
|
|
controller.getKLTPregister().unlisten(this);
|
|
}
|
|
if(!ignoreBindCheck)
|
|
controller.getKLTPregister() .unbind(this);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean isClosed() {//||(vin!=null&&vin.isClosed())&&(vout!=null&&vout.isClosed())
|
|
return closed;
|
|
}
|
|
|
|
@Override
|
|
protected void sendUrgentData(int data) throws IOException {
|
|
throw new UnsupportedOperationException("urgent data unspuuorted");
|
|
|
|
}
|
|
|
|
@Override
|
|
public int getLocalPort() {
|
|
return super.getLocalPort();
|
|
}
|
|
|
|
|
|
@Override
|
|
public void accept(KLTPSessionPacket stream) {
|
|
if(isListening()) {
|
|
if(!backlogQueue.contains(stream)) {
|
|
backlogQueue.add(stream);
|
|
System.out.println("添加到监听队列:"+stream);
|
|
}
|
|
}else {
|
|
System.out.println("匹配反向连接:"+stream);
|
|
if(this.vin==null) {
|
|
this.vin=stream.getInputstream();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
public InetAddress getRemoteInetAddress() {
|
|
return remoteaddr.toInet6Address();
|
|
}
|
|
|
|
@Override
|
|
public InetAddress getLocalInetAddress() {
|
|
return localaddr.toInet6Address();
|
|
}
|
|
|
|
@Override
|
|
public void setLocalPort(int i) {
|
|
localport=i;
|
|
}
|
|
|
|
|
|
public void setCompress(int i) {
|
|
// TODO 自动生成的方法存根
|
|
|
|
}
|
|
public int getCompress() {
|
|
// TODO 自动生成的方法存根
|
|
return 0;
|
|
}
|
|
public boolean isConnectionPending() {
|
|
// TODO 自动生成的方法存根
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|