namespace Shine
{
/** socket基类 */
export class BaseSocket
{
/** 关闭的 */
public static readonly Closed:number=1;
/** 连接中 */
public static readonly Connecting:number=2;
/** 已连接 */
public static readonly Connected:number=3;
private static _connectTimeMax:number=5;
/** 当前状态(主线程写,IO线程读) */
private _state:number=1;
/** 执行索引 */
private _doIndex:number=0;
private _host:string;
private _port:number;
/** 是否析构了 */
private _disposed:boolean=false;
/** 连接计时 */
private _connectTime:number=0;
private _secondLastTime:number=0;
/** 连接内容 */
private _content:BaseSocketContent;
//reconnect
/** 是否允许断线重连 */
private _openReconnect:boolean = false;
/** 断线重连是否有效(是否达到缓存数目上限) */
private _reconnecting:boolean = false;
/** 重连剩余时间(秒) */
private _reconnectLastTime:number=0;
/** 间隔(3秒) */
private _reConnectTime:AffairTimeOut;
private _closeByInitiative:boolean;
/** 接收连接ID */
private _receiveID:number=-1;
/** 接收令牌 */
private _receiveToken:number=-1;
/** 读流 */
private _readStream:BytesReadStream = new BytesReadStream();
/** 写流 */
private _writeStream:BytesWriteStream = new BytesWriteStream();
//ping
/** 心跳计时(秒) */
private _pingKeepTime:number;
/** 心跳时间经过(秒) */
private _pingTimePass:number = 0;
/** ping间隔秒数 */
private _pingIndex:number = 0;
//try
/** 当前尝试次数(-1就是未工作) */
private _currentTryCount:number;
/** 最多尝试次数 */
private _tryCountMax:number = 0;
/** 连接成功回调*/
private _connectCall:Func;
/** 连接失败回调*/
private _connectFailedCall:Func;
/** 连接被关闭回调*/
private _closeCall:Func;
/** 构造协议回调*/
private _createResponseFunc:Func;
//check
/** 发送序号 */
private _sendMsgIndex:number = 0;
/** 接收协议序号 */
private _receiveMsgIndex:number = 0;
/** 发送消息缓存队列 */
private _sendCacheRequestQueue:BaseRequest[];
/** 断线时发送的序号 */
private _disConnectLastSendIndex:number=0;
constructor()
{
this._pingKeepTime = ShineSetting.pingKeepTime;
this._sendCacheRequestQueue=new Array<BaseRequest>(ShineSetting.requestCacheLen);
this._reConnectTime=new AffairTimeOut(Func.create(this,this.reconnectTimeOut),3);
}
/** 连接回调*/
public setConnectCall(func: Func): void
{
this._connectCall = func;
}
/* 连接失败回调 */
public setConnectFailedCall(func: Func): void
{
this._connectFailedCall = func;
}
/** 连接被关闭回调 */
public setCloseCall(func: Func): void
{
this._closeCall = func;
}
/** 构造协议回调 */
public setCreateResponseFunc(func:Func): void
{
this._createResponseFunc = func;
}
/** 清空(回归) */
public clear(): void
{
this._writeStream.clear();
//序号归零
this._sendMsgIndex = 0;
this._receiveMsgIndex=0;
this._reconnecting=false;
this._reconnectLastTime=0;
this._pingTimePass=0;
this._receiveID=-1;
this._receiveToken=-1;
}
/** 每帧 */
public onFrame(delay: number): void
{
this._secondLastTime += delay;
if (this._secondLastTime >= 1000)
{
this._secondLastTime -= 1000;
this.onSecond();
}
this.sendLoopOnce();
}
/** 是否开启断线重连 */
public setOpenReconnect(value: boolean): void
{
this._openReconnect = value;
}
public isOpenReconnect(): boolean
{
return this._openReconnect;
}
/** 是否连接着 */
public isConnect(): boolean
{
return this._state==BaseSocket.Connected;
}
/** 是否连接着 */
public isConnecting(): boolean
{
return this._state==BaseSocket.Connecting;
}
public getDoIndex():number
{
return this._doIndex;
}
/** 获取状态 */
public getState():number
{
return this._state;
}
/** 设置接收连接信息 */
public setReceiveInfo(receiveID:number,token:number):void
{
//已有
if(this._receiveID>0)
return;
this._receiveID=receiveID;
this._receiveToken=token;
}
/** 连接 */
public connect(host:string,port:number): void
{
//连接尝试2次
this._tryCountMax=2;
this.preConnect(host,port);
}
/** 尝试连接(time:秒) */
public tryConnect(host:string,port:number):void
{
//连接尝试3次
this._tryCountMax=0;
this.preConnect(host,port);
}
/** 重新尝试连接 */
public reConnect():void
{
this.connect(this._host, this._port);
}
private retryConnect():void
{
this.toCloseForConnect();
this.toConnectOnce();
}
public preConnectSuccessForIO():void
{
if(!this.isConnecting())
return;
this._state=BaseSocket.Connected;
//重连中
if(this._reconnecting && this._receiveID>0)
{
this.refreshPingTime();
this.sendAbs(SocketReconnectRequest.createSocketReconnect(this._receiveID,this._receiveToken,this._receiveMsgIndex));
this._reConnectTime.start();
}
else
{
this.clear();
this.onConnectSuccess();
}
}
private onConnectSuccess():void
{
if(this._connectCall!=null)
this._connectCall.invoke();
}
private preConnect(host:string,port:number): void
{
this.close();
this._host = host;
this._port = port;
this._currentTryCount = 0;
this.toConnectOnce();
}
public closeForIO(isInitiative:boolean,canRec:boolean=true):void
{
if(this._state==BaseSocket.Closed)
return;
//正在连接中
if(this.isConnecting())
{
this.stopConnect();
}
this._state=BaseSocket.Closed;
this._doIndex++;//序号加
Ctrl.print("连接断开",isInitiative);
if (isInitiative)
{
this.toFlush();
}
this.onDisconnect();
//重连中,直接判定失败
if(!this._reconnecting && this._openReconnect && !isInitiative && canRec && this.canReconnect())
{
Ctrl.log("连接断线,进入重连阶段");
this._reconnecting=true;
this._disConnectLastSendIndex=this._sendMsgIndex;
this._reconnectLastTime=ShineSetting.socketReConnectKeepTime;
this._closeByInitiative=isInitiative;
this.beginReconnect();
}
else
{
this._closeByInitiative=isInitiative;
this.realClose();
}
}
/** 开始重连(io线程) */
protected beginReconnect():void
{
this._tryCountMax=0;
this.reConnect();
}
/** 实际关闭(IO线程) */
protected realClose():void
{
this._receiveID=-1;
this._receiveToken=-1;
this._reConnectTime.stop();
this._reconnecting=false;
this._disConnectLastSendIndex=0;
for(var i:number=this._sendCacheRequestQueue.length-1;i>=0;--i)
{
this._sendCacheRequestQueue[i]=null;
}
if(!this._closeByInitiative)
{
if(this._closeCall!=null)
this._closeCall.invoke();
}
}
/** 是否可重连 */
protected canReconnect():boolean
{
return this._receiveID>0;
}
/** try到时间(io线程) */
private reconnectTimeOut():void
{
this.reconnectFailed();
}
/** 重连失败(IO线程) */
protected reconnectFailed():void
{
if(!this._reconnecting)
return;
if(this.isConnecting())
{
this.stopConnect();
}
this.realClose();
}
/** 停止连接 */
protected stopConnect():void
{
}
private onDisconnect():void
{
if(this._content!=null)
{
this._content.close();
this._content=null;
}
this._reConnectTime.stop();
}
private toCloseForConnect():void
{
if(this._state==BaseSocket.Closed)
return;
this._state=BaseSocket.Closed;
this._doIndex++;//序号加
this.onDisconnect();
}
/** 推送剩余的消息 */
private toFlush():void
{
this.sendLoopOnce();
}
private doConnectFailed():void
{
if(this._connectFailedCall!=null)
this._connectFailedCall.invoke();
}
//被关闭
public beClose():void
{
this.closeForIO(false);
}
/** 关闭(不调closeCall) */
public close():void
{
this.closeForIO(true);
}
/** 闪断测试 */
public closeTest():void
{
this.closeForIO(false);
}
/** 刷ping的时间 */
public refreshPingTime():void
{
this._pingTimePass = 0;
}
/** 真析构 */
public dispose():void
{
this.closeAndClear();
//TODO:后续
}
/** 析构 */
public closeAndClear():void
{
this.close();
this.clear();
}
private onSecond():void
{
switch(this._state)
{
case BaseSocket.Connected:
{
// if(!_socket.Connected)
// {
// //连接被关闭
// beClose();
// }
++this._pingIndex;
if(this._pingIndex>=ShineSetting.pingTime)
{
this._pingIndex=0;
this.sendPingRequet();
}
if(ShineSetting.needPingCut)
{
++this._pingTimePass;
if(this._pingTimePass>ShineSetting.pingKeepTime)
{
Ctrl.warnLog("连接超时,强制关闭");
this.closeForIO(false);
}
}
}
break;
case BaseSocket.Connecting:
{
//连接超时
if((++this._connectTime)>=5)
{
this.connectTimeOut();
}
}
break;
}
}
private toConnectOnce():void
{
this.createNewContent();
this._state=BaseSocket.Connecting;
this._connectTime=0;
++this._currentTryCount;
Ctrl.print("toConnect一次:",this._host);
this._content.connect(this._host, this._port);
}
private createNewContent():void
{
++this._doIndex;
this._content=new BaseSocketContent(this);
this._content.setDoIndex(this._doIndex);
}
public preConnectFailedForIO():void
{
if(!this.isConnecting())
return;
this.toCloseForConnect();
//超出次数限制
if(this._tryCountMax>0 && this._currentTryCount>=this._tryCountMax)
{
this._currentTryCount=-1;
this._connectTime=0;
if(this._connectFailedCall!=null)
this._connectFailedCall.invoke();
}
else
{
this.retryConnect();
}
}
private connectTimeOut():void
{
if(this._state!=BaseSocket.Connecting)
return;
close();
//超出次数限制
if(this._tryCountMax>0 && this._currentTryCount>=this._tryCountMax)
{
this._currentTryCount=-1;
this._connectTime=0;
this.doConnectFailed();
}
else
{
this.retryConnect();
}
}
private sendLoopOnce():void
{
if(!this.isConnect())
return;
//当前没有
if(this._content==null)
return;
if (this._writeStream.length() > 0)
{
this._content.sendOnce(this._writeStream.getByteArray());
this._writeStream.clear();
}
}
private toSendOne(request:BaseRequest):void
{
this.toWriteRequestToStream(this._writeStream,request,request.sendMsgIndex);
}
/** 实际执行写request到stream */
private toWriteRequestToStream(stream:BytesWriteStream,request:BaseRequest,index:number):void
{
var limit:number=request.getMessageLimit();
stream.setWriteLenLimit(limit);
var startPos:number=stream.getPosition();
var mid:number=request.getDataID();
//写协议ID(原生写)
stream.natureWriteUnsignedShort(mid);
if(!BytesControl.isIgnoreMessage(mid))
{
stream.natureWriteShort(index);
}
request.writeToStream(stream);
var endPos:number=stream.getPosition();
var len:number=endPos-startPos;
if(len>=limit)
{
Ctrl.errorLog("request长度超出上限",len);
}
if(ShineSetting.needCustomLengthBasedFrameDecoder)
{
stream.insertLenToPos(startPos,len);
}
else
{
stream.setPosition(startPos);
stream.insertVoidBytes(4,len);
stream.natureWriteInt(len);
stream.setPosition(endPos);
}
}
/** 重连的下一步(主线程) */
protected doReconnectNext(lastReceiveIndex:number,needReMsg:boolean):void
{
//清空
this._writeStream.clear();
var dd:number=this.indexD(this._sendMsgIndex,lastReceiveIndex);
if(dd<0 || dd>=ShineSetting.requestCacheLen)
{
Ctrl.warnLog("重连时,消息缓存已失效");
this.closeForIO(false,false);
return;
}
if(needReMsg)
{
this.sendAbs(SocketReconnectSuccessRequest.createSocketReconnectSuccess(this._receiveMsgIndex));
}
for(var i=lastReceiveIndex;this.indexD(i,this._sendMsgIndex)<0;i++)
{
var request:BaseRequest=this._sendCacheRequestQueue[i & ShineSetting.requestCacheMark];
if(request==null)
{
Ctrl.errorLog("不该找不到消息");
this.closeForIO(false,false);
return;
}
else
{
this.doWriteRequestToStream(request,i);
}
}
this._reconnecting=false;
this._disConnectLastSendIndex=0;
//推送一次
this.toFlush();
Ctrl.log("socket重连成功:");
}
/** 读取一个 */
public onePiece(bytes: ArrayBuffer): void
{
//更新ping时间
this.refreshPingTime();
this.onSocketDataT(bytes);
}
/** 收到一个协议包(IO线程) */
private onSocketDataT(bytes: ArrayBuffer): void
{
this._readStream.setBuf(bytes);
var mid: number = this._readStream.natureReadUnsignedShort();
//检测序号
if(!BytesControl.isIgnoreMessage(mid))
{
var receiveIndex:number=this._readStream.natureReadShort();
var nowIndex:number=this._receiveMsgIndex;
if(receiveIndex!=nowIndex)
{
Ctrl.warnLog("序号检测没对上,"+" nowIndex:"+nowIndex+" receiveIndex:"+receiveIndex+" mid:"+mid);
//视为被动关闭
this.closeForIO(false);
return;
}
++this._receiveMsgIndex;
}
if (this._createResponseFunc != null)
{
var response:BaseResponse = this._createResponseFunc.invoke(mid);
if (response == null)
{
if (ShineSetting.needMessageExistCheck)
{
Ctrl.throwError("未解析mid为" + mid + "的协议");
}
return;
}
response.socket = this;
if ((response = response.readFromStream(this._readStream)) != null)
{
this.toRunResponseOne(response);
}
}
}
/** 读失败 */
private onError(msg: string): void
{
Ctrl.print("socket读取失败:" + msg);
//断开连接
close();
}
//心跳回复(io线程)
private onRePing(): void
{
this.refreshPingTime();
}
//执行消息
private makeResponse(mid:number): void
{
}
/** 执行response们 */
private toRunResponseOne(response:BaseResponse):void
{
if (ShineSetting.needShowMessage && !BytesControl.isIgnoreMessage(response.getDataID()))
{
Ctrl.debugLog("收消息:","mid:"+response.getDataID(),ShineSetting.needShowMessageDetail ? response.toDataString() : response.getDataClassName());
}
if (ShineSetting.needError)
{
response.run();
}
else
{
try
{
response.run();
}
catch (err)
{
Ctrl.throwError(err);
}
}
}
/** 发送ping消息 */
private sendPingRequet(): void
{
this.sendAbs(PingRequest.createPing());
}
//check
/** 发送到连接 */
public send(request:BaseRequest): void
{
if (ShineSetting.needShowMessage)
{
var mid:number = request.dataID;
Ctrl.debugLog("发消息:","mid:"+mid,ShineSetting.needShowMessageDetail ? request.toDataString() : request.getDataClassName());
}
//重连中
if(this.checkRequestNeedSend(request))
{
request.preSend();
this.sendRequestForIO(request);
}
}
/** 发送request(IO线程) */
protected sendRequestForIO(request:BaseRequest):void
{
if(!this.checkRequestNeedSend(request))
return;
var needIndex:boolean=false;
var index:number=0;
if(!BytesControl.isIgnoreMessage(request.getDataID()))
{
needIndex=true;
index=this._sendMsgIndex++;
if(this._sendMsgIndex>32767)
this._sendMsgIndex-=65536;
//存起
this._sendCacheRequestQueue[index & ShineSetting.requestCacheMark]=request;
}
if(this._reconnecting)
{
if(needIndex)
{
//失效
if(this.indexD(index,this._disConnectLastSendIndex)>=ShineSetting.requestCacheLen)
{
this.reconnectFailed();
}
}
}
else
{
this.doWriteRequestToStream(request,index);
}
}
/** 发abs消息 */
public sendAbs(request:BaseRequest):void
{
request.preSend();
if(this.isConnect())
{
this.doWriteRequestToStream(request,0);
}
}
private checkRequestNeedSend(request:BaseRequest):boolean
{
//重连中
if(this._reconnecting)
{
//不保留的
if(!request.needCacheOnDisConnect())
return false;
}
else
{
//还未连接
if(!this.isConnect())
return false;
}
return true;
}
/** 执行写入流(io线程) */
private doWriteRequestToStream(request:BaseRequest,index:number):void
{
this.toWriteRequestToStream(this._writeStream,request,index);
}
/** 返回a1-a2 */
public indexD(a1:number,a2:number):number
{
var re:number=a1 - a2;
if(re>32767)
re-=65536;
if(re<-32768)
re+=65536;
return re;
}
/** 收到重连成功(IO消息) */
public onReconnectSuccess(lastReceiveIndex:number):void
{
//已不在重连中
if(!this._reconnecting)
{
this.closeForIO(false,false);
return;
}
this.doReconnectNext(lastReceiveIndex,false);
}
}
}