async Task ProcessSocketAsync(Socket tcpSocket)
        {
TryAgain:

            string connmode = null;

            if (Connector.UseRouterClientPort && DateTime.Now > _stopUseRouterClientPortUntil)
            {
                connmode = "RCP";
            }
            else if (Connector.UseUDPPunching && DateTime.Now > _stopUseRouterClientPortUntil)
            {
                connmode = "UDP";
            }
            else if (Connector.UseServerBandwidth)
            {
                connmode = "USB";
            }
            else
            {
                //TODO:  try ..
                if (Connector.UseUDPPunching)
                {
                    connmode = "UDP";
                }
                else if (Connector.UseRouterClientPort)
                {
                    connmode = "RCP";
                }
                else
                {
                    throw new Exception("No connection mode.");
                }
            }


            Task <KeyValuePair <string, UDPClientListener> > task2 = null;

            if (connmode == "UDP")
            {
                //TODO: shall cache the UDPClientListener ...
                task2 = Task.Run(GetUdpClientAsync);
            }

            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            serverSocket.InitTcp();
            await serverSocket.ConnectAsync(Connector.ServerHost, 6022);

            string            connArgument = null;
            UDPClientListener udp          = null;

            if (task2 != null)
            {
                try
                {
                    var kvp = await task2;
                    connArgument = kvp.Key;
                    udp          = kvp.Value;
                }
                catch (Exception x)
                {
                    OnError(x);
                    LogMessage("UDP Failed , switch ...");
                    if (Connector.UseServerBandwidth)
                    {
                        connmode = "USB";
                    }
                    else
                    {
                        throw new Exception(x.Message, x);
                    }
                }
            }

            bool supportEncrypt = Connector.UseEncrypt;

            byte[] clientKeyIV;

            CommandMessage connmsg = new CommandMessage();

            connmsg.Name = "ConnectorConnect";
            List <string> arglist = new List <string>();

            arglist.Add(this.Connector.License.Key);
            arglist.Add(this.Connector.ServerPort.ToString());
            byte[] encryptedKeyIV, sourceHash;
            Connector.License.GenerateSecureKeyAndHash(out clientKeyIV, out encryptedKeyIV, out sourceHash);
            arglist.Add(Convert.ToBase64String(encryptedKeyIV));
            arglist.Add(Convert.ToBase64String(sourceHash));
            arglist.Add(supportEncrypt ? "1" : "0");
            arglist.Add(connmode);
            arglist.Add(connArgument);
            connmsg.Args = arglist.ToArray();

            await serverSocket.SendAsync(connmsg.Pack(), SocketFlags.None);

            connmsg = await CommandMessage.ReadFromSocketAsync(serverSocket);

            if (connmsg == null)
            {
                LogMessage("Warning:ConnectorWorker : remote closed connection.");
                return;
            }

            //LogMessage("Warning:connmsg : " + connmsg);

            if (connmsg.Name != "ConnectOK")
            {
                return;
            }

            //TODO: add to session list

            if (supportEncrypt && connmsg.Args[1] == "0")
            {
                supportEncrypt = false; LogMessage("Warning:server don't support encryption : " + Connector.ServerHost);
            }

            Stream _sread, _swrite;

            if (connmode == "RCP")
            {
                serverSocket.CloseSocket();

                string ip   = connmsg.Args[2];
                int    port = int.Parse(connmsg.Args[3]);
                if (port < 1)
                {
                    LogMessage("Error:Invalid configuration , remote-client-side don't provide RouterClientPort , stop use RCP for 1min");
                    _stopUseRouterClientPortUntil = DateTime.Now.AddSeconds(60);
                    goto TryAgain;                    //TODO: reuse the serverSocket and switch to another mode
                }

                LogMessage("Warning:" + tcpSocket.LocalEndPoint + " forward to " + ip + ":" + port);
                await tcpSocket.ForwardToAndWorkAsync(ip, port);

                return;
            }


            if (connmode == "UDP")
            {
                LogMessage("MY UDP..." + connArgument + " REMOTE..." + connmsg.Args[2]);

                string   mynat = connArgument;
                string[] pair  = connmsg.Args[2].Split(':');

                serverSocket.CloseSocket();

                try
                {
                    UDPClientStream stream = await UDPClientStream.ConnectAsync(udp, pair[0], int.Parse(pair[1]), TimeSpan.FromSeconds(6));

                    _sread  = stream;
                    _swrite = stream;

                    LogMessage("UDP Connected #" + stream.SessionId + " " + connArgument + " .. " + connmsg.Args[2]);
                }
                catch (Exception x)
                {
                    LogMessage("UDP ERROR " + connArgument + " .. " + connmsg.Args[2] + " " + x.Message);
                    throw;
                }
            }
            else
            {
                if (supportEncrypt)
                {
                    Connector.License.OverrideStream(serverSocket.CreateStream(), clientKeyIV, out _sread, out _swrite);
                }
                else
                {
                    _sread = _swrite = serverSocket.CreateStream();
                }
            }

            TcpMapConnectorSession session = new TcpMapConnectorSession(new SimpleSocketStream(tcpSocket));
            await session.DirectWorkAsync(_sread, _swrite);
        }
        async Task WorkAsync()
        {
            IsConnected = false;
            LogMessage("ClientWorker WorkAsync start");
            int connectedTimes = 0;

            try
            {
                int againTimeout = 125;
StartAgain:

                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _socket.InitTcp();
                _cts_connect = new CancellationTokenSource();
                try
                {
                    await _socket.ConnectAsync(Client.ServerHost, 6022);

                    LogMessage("connected to 6022");
                }
                catch (Exception x)
                {
                    OnError(x);
                    _socket.CloseSocket();
                    _socket      = null;
                    _cts_connect = new CancellationTokenSource();
                    if (!IsStarted)
                    {
                        return;
                    }
                    if (againTimeout < 4)
                    {
                        againTimeout = againTimeout * 2;
                    }
                    if (await _cts_connect.Token.WaitForSignalSettedAsync(againTimeout))
                    {
                        return;
                    }
                    goto StartAgain;
                }

                try
                {
                    bool   supportEncrypt = false;
                    byte[] clientKeyIV;

                    {
                        CommandMessage connmsg = new CommandMessage();
                        connmsg.Name = "ClientConnect";
                        List <string> arglist = new List <string>();
                        arglist.Add(this.Client.License.Key);
                        arglist.Add(this.Client.ServerPort.ToString());
                        byte[] encryptedKeyIV, sourceHash;
                        Client.License.GenerateSecureKeyAndHash(out clientKeyIV, out encryptedKeyIV, out sourceHash);
                        arglist.Add(Convert.ToBase64String(encryptedKeyIV));
                        arglist.Add(Convert.ToBase64String(sourceHash));
                        arglist.Add(supportEncrypt ? "1" : "0");
                        connmsg.Args = arglist.ToArray();

                        await _socket.SendAsync(connmsg.Pack(), SocketFlags.None);

                        //LogMessage("wait for conn msg");

                        connmsg = await CommandMessage.ReadFromSocketAsync(_socket);

                        if (connmsg == null)
                        {
                            TcpMapService.LogMessage("no message ? Connected:" + _socket.Connected);
                            return;
                        }

                        //LogMessage("connmsg : " + connmsg.Name + " : " + string.Join(",", connmsg.Args));

                        if (connmsg.Name != "ConnectOK")
                        {
                            IsStarted = false;                            //don't go until start it again.
                            throw new Exception(connmsg.Name + " : " + string.Join(",", connmsg.Args));
                        }

                        if (connmsg.Args[1] == "0")
                        {
                            supportEncrypt = false;
                        }
                    }

                    IsConnected = true;

                    connectedTimes++;
                    LogMessage("ConnectOK #" + connectedTimes);

                    if (supportEncrypt)
                    {
                        Client.License.OverrideStream(_socket.CreateStream(), clientKeyIV, out _sread, out _swrite);
                    }
                    else
                    {
                        _sread = _swrite = _socket.CreateStream();
                    }

                    for (int i = 0; i < Math.Min(5, Client.PreSessionCount); i++)                    //TODO:const of 5
                    {
                        _ = Task.Run(ProvidePreSessionAsync);
                    }

                    _ = Task.Run(MaintainSessionsAsync);

                    byte[] readbuff = new byte[TcpMapService.DefaultBufferSize];
                    while (IsStarted)
                    {
                        CommandMessage msg;
                        var            cts = new CancellationTokenSource();
                        _ = Task.Run(async delegate
                        {
                            if (await cts.Token.WaitForSignalSettedAsync(16000))
                            {
                                return;
                            }

                            await _swrite.WriteAsync(new CommandMessage("_ping_", "forread").Pack());
                        });
                        try
                        {
                            msg = await CommandMessage.ReadFromStreamAsync(_sread);
                        }
                        finally
                        {
                            cts.Cancel();
                        }

                        if (msg == null)
                        {
                            TcpMapService.LogMessage("no message ? Connected:" + _socket.Connected);
                            return;
                        }

                        //this.LogMessage("TcpMapClientWorker get msg " + msg);

                        switch (msg.Name)
                        {
                        case "StartSession":
                            Task.Run(async delegate
                            {
                                try
                                {
                                    await DoStartSessionAsync(msg);
                                }
                                catch (Exception x)
                                {
                                    OnError(x);
                                }
                            }).ToString();
                            break;

                        case "CloseSession":
                            Task.Run(async delegate
                            {
                                try
                                {
                                    await DoCloseSessionAsync(msg);
                                }
                                catch (Exception x)
                                {
                                    OnError(x);
                                }
                            }).ToString();
                            break;

                        case "_ping_":
                            await _swrite.WriteAsync(new CommandMessage("_ping_result_").Pack());

                            break;

                        case "_ping_result_":
                            break;

                        default:
                            LogMessage("Error: 4 Ignore message " + msg);
                            break;
                        }
                    }
                }
                catch (SocketException)
                {
                    //no log
                }
                catch (Exception x)
                {
                    OnError(x);
                }

                if (IsStarted)
                {
                    _socket.CloseSocket();                    //logic failed..
                    againTimeout = 125;
                    goto StartAgain;
                }
            }
            catch (Exception x)
            {
                OnError(x);
            }

            IsStarted   = false;
            IsConnected = false;

            if (_socket != null)
            {
                try
                {
                    _socket.CloseSocket();
                }
                catch (Exception x)
                {
                    OnError(x);
                }
                _socket = null;
            }
        }
示例#3
0
 protected abstract Task WriteMessageAsync(CommandMessage msg);
示例#4
0
 protected override async Task WriteMessageAsync(CommandMessage msg)
 {
     await _swrite.WriteAsync(msg.Pack());
 }
示例#5
0
        private static async Task ProcesSocketAsync(Socket socket)
        {
            while (true)
            {
                var msg = await CommandMessage.ReadFromSocketAsync(socket);

                if (msg == null)
                {
                    //LogMessage("no message ? Connected:" + socket.Connected);
                    return;
                }

                //LogMessage("new msg : " + msg);

                switch (msg.Name)
                {
                case "ClientConnect":
                case "SessionConnect":
                    await TcpMapServerClient.AcceptConnectAndWorkAsync(socket, msg);

                    return;

                case "ConnectorConnect":
                    var    server    = FindServerWorkerByPort(int.Parse(msg.Args[1]));
                    string failedmsg = null;
                    if (server == null)
                    {
                        failedmsg = "NoPort";
                    }
                    else if (server.Server.IsDisabled)
                    {
                        failedmsg = "IsDisabled";
                    }
                    else if (!server.Server.AllowConnector)
                    {
                        failedmsg = "NotAllow";
                    }
                    else if (server.Server.ConnectorLicense == null)
                    {
                        failedmsg = "NotLicense";
                    }
                    else if (server.Server.ConnectorLicense.Key != msg.Args[0])
                    {
                        failedmsg = "LicenseNotMatch";
                    }
                    if (failedmsg != null)
                    {
                        var resmsg = new CommandMessage("ConnectFailed", failedmsg);
                        await socket.SendAsync(resmsg.Pack(), SocketFlags.None);
                    }
                    else
                    {
                        TcpMapServerConnector connector = new TcpMapServerConnector(server);
                        await connector.AcceptConnectorAndWorkAsync(socket, msg);
                    }
                    break;

                case "":    //other direct request..
                default:
                    throw new Exception("Invaild command " + msg.Name);
                }
            }
        }
示例#6
0
        async Task WorkAsync(CommandMessage connmsg)
        {
            if (connmsg.Name == "SessionConnect")
            {
                _is_client  = false;
                _is_session = true;
            }


            byte[] clientKeyIV;

            _worker = TcpMapService.FindServerWorkerByKey(connmsg.Args[0], int.Parse(connmsg.Args[1]));

            string failedreason = null;

            if (_worker == null)
            {
                failedreason = "NotFound";
            }
            else if (!_worker.Server.IsValidated)
            {
                failedreason = "NotValidated";
            }
            else if (_worker.Server.IsDisabled)
            {
                failedreason = "NotEnabled";
            }

            if (_worker == null || !string.IsNullOrEmpty(failedreason))
            {
                var failedmsg = new CommandMessage("ConnectFailed", failedreason);
                await _socket.SendAsync(failedmsg.Pack(), SocketFlags.None);

                return;
            }

            bool supportEncrypt = _worker.Server.UseEncrypt;

            if (connmsg.Args[4] == "0")
            {
                supportEncrypt = false;
            }

            try
            {
                _worker.Server.License.DescriptSourceKey(Convert.FromBase64String(connmsg.Args[2]), Convert.FromBase64String(connmsg.Args[3]), out clientKeyIV);
            }
            catch (Exception x)
            {
                _worker.OnError(x);
                var failedmsg = new CommandMessage("ConnectFailed", "InvalidSecureKey");
                await _socket.SendAsync(failedmsg.Pack(), SocketFlags.None);

                return;
            }

            var successMsg = new CommandMessage("ConnectOK", "ConnectOK", supportEncrypt ? "1" : "0");
            await _socket.SendAsync(successMsg.Pack(), SocketFlags.None);

            if (supportEncrypt)
            {
                _worker.Server.License.OverrideStream(_socket.CreateStream(), clientKeyIV, out _sread, out _swrite);
            }
            else
            {
                _sread = _swrite = _socket.CreateStream();
            }

            if (_is_session)
            {
                this.SessionId = connmsg.Args[5];
                if (SessionId == null)
                {
                    _cts_wait_upgrade = new CancellationTokenSource();
                }
            }

            _worker.AddClientOrSession(this);
            try
            {
                if (_is_client)
                {
                    _ = _swrite.WriteAsync(new CommandMessage("SetOption", "ClientEndPoint", _socket.RemoteEndPoint.ToString()).Pack());

                    while (true)
                    {
                        var msg = await CommandMessage.ReadFromStreamAsync(_sread);

                        //process it...

                        if (msg == null)
                        {
                            //TcpMapService.LogMessage("no message ? Connected:" + _socket.Connected);
                            throw new SocketException(995);
                        }

                        //_worker.LogMessage("TcpMapServerClient get msg " + msg);

                        switch (msg.Name)
                        {
                        case "SetOption":
                            string optvalue = msg.Args[1];
                            switch (msg.Args[0])
                            {
                            case "RouterClientPort":
                                this.OptionRouterClientPort = int.Parse(optvalue);
                                break;

                            default:
                                _worker.LogMessage("Error:Ignore option " + msg);
                                break;
                            }
                            break;

                        case "StartSessionResult":
                        case "CloseSessionResult":
                        case "CreateUDPNatResult":
                            long reqid = long.Parse(msg.Args[0]);
                            if (reqmap.TryGetValue(reqid, out var ritem))
                            {
                                ritem.Response = msg;
                                ritem.cts.Cancel();
                            }
                            else
                            {
                                _worker.LogMessage("Request Expired : " + msg);
                            }
                            break;

                        case "_ping_":
                            this._lastPingTime = DateTime.Now;
                            await _swrite.WriteAsync(new CommandMessage("_ping_result_").Pack());

                            break;

                        case "_ping_result_":
                            break;

                        default:
                            _worker.LogMessage("Error: 5 Ignore message " + msg);
                            break;
                        }
                    }
                }
                else if (_is_session)
                {
                    if (SessionId == null)
                    {
                        _worker.LogMessage($"Warning:ServerClient*{_scid} Wait for Upgrade To Session ");

                        while (SessionId == null)
                        {
                            if (await _cts_wait_upgrade.Token.WaitForSignalSettedAsync(28000))                              //check the presession closed or not every 28 seconds
                            {
                                if (SessionId != null)
                                {
                                    break;                                      //OK, session attached.
                                }
                                throw new SocketException(995);                 //_cts_wait_upgrade Cancelled , by SessionId is not seted
                            }

                            //if (!_socket.Connected) //NEVER useful..
                            //{
                            //	_worker.LogMessage("Warning:presession exit.");
                            //	throw new SocketException(995);
                            //}

                            if (!_socket.Poll(0, SelectMode.SelectRead))                             //WORKS..
                            {
                                continue;
                            }

                            if (_socket.Available == 0)
                            {
                                _worker.LogMessage("Warning:presession exit!");
                                throw new SocketException(995);
                            }

                            //_worker.LogMessage("Warning:presession send message before upgrade ?"+_socket.Available);
                            if (!_cts_wait_upgrade.IsCancellationRequested)
                            {
                                //TODO:not locked/sync, not so safe it the presession is upgrading
                                var msg = await CommandMessage.ReadFromSocketAsync(_socket);

                                if (msg.Name == "_ping_")
                                {
                                    byte[] resp = new CommandMessage("_ping_result_").Pack();
                                    await _socket.SendAsync(resp, SocketFlags.None);
                                }
                                else
                                {
                                    _worker.LogMessage("Warning:presession unexpected msg : " + msg);
                                }
                            }
                        }

                        _worker.LogMessage($"Warning:ServerClient*{_scid} SessionId:" + SessionId);
                    }

                    int waitMapTimes = 0;
TryGetMap:

                    if (_attachedSession != null)
                    {
                        _worker.LogMessage($"ServerClient*{_scid} use attached Session : {SessionId} *{waitMapTimes}");
                        await _attachedSession.UseThisSocketAsync(_sread, _swrite);
                    }
                    else if (_worker.sessionMap.TryGetValue(SessionId, out var session))
                    {
                        _worker.LogMessage($"ServerClient*{_scid} session server ok : {SessionId} *{waitMapTimes}");
                        await session.UseThisSocketAsync(_sread, _swrite);
                    }
                    else
                    {
                        if (waitMapTimes < 5)
                        {
                            waitMapTimes++;
                            await Task.Delay(10);                            //wait sessionMap be added..

                            goto TryGetMap;
                        }

                        _worker.LogMessage($"Warning:ServerClient*{_scid} session not found : {SessionId}");
                        throw new Exception($"ServerClient*{_scid} session not found : {SessionId}");
                    }
                }
                else
                {
                    throw new InvalidOperationException();
                }
            }
            catch (SocketException)
            {
                //no log
            }
            catch (Exception x)
            {
                _worker.OnError(x);
            }
            finally
            {
                _worker.RemoveClientOrSession(this);
            }

            _worker.LogMessage($"ServerClient*{_scid} WorkAsync END " + SessionId);
        }
示例#7
0
        internal async Task AcceptConnectorAndWorkAsync(Socket clientSock, CommandMessage connmsg)
        {
            bool supportEncrypt = _worker.Server.UseEncrypt;

            if (connmsg.Args[4] == "0")
            {
                supportEncrypt = false;
            }

            byte[] clientKeyIV;
            try
            {
                _worker.Server.ConnectorLicense.DescriptSourceKey(Convert.FromBase64String(connmsg.Args[2]), Convert.FromBase64String(connmsg.Args[3]), out clientKeyIV);
            }
            catch (Exception x)
            {
                _worker.OnError(x);
                var failedmsg = new CommandMessage("ConnectFailed", "InvalidSecureKey");
                await clientSock.SendAsync(failedmsg.Pack(), SocketFlags.None);

                return;
            }

            TcpMapServerClient sclient = _worker.FindClient();

            if (sclient == null)
            {
                var failedmsg = new CommandMessage("ConnectFailed", "NoClient");
                await clientSock.SendAsync(failedmsg.Pack(), SocketFlags.None);

                return;
            }

            string mode         = connmsg.Args[5];
            string connArgument = connmsg.Args[6];

            if (mode == "USB")            //use server bandwidth
            {
                var resmsg = new CommandMessage("ConnectOK", "ConnectOK", supportEncrypt ? "1" : "0");
                await clientSock.SendAsync(resmsg.Pack(), SocketFlags.None);

                Stream _sread, _swrite;
                if (supportEncrypt)
                {
                    _worker.Server.ConnectorLicense.OverrideStream(clientSock.CreateStream(), clientKeyIV, out _sread, out _swrite);
                }
                else
                {
                    _sread = _swrite = clientSock.CreateStream();
                }

                using Socket localsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                string ip = _worker.Server.ServerBind;
                if (ip == "0.0.0.0")
                {
                    ip = "127.0.0.1";
                }
                localsock.InitTcp();
                await localsock.ConnectAsync(ip, _worker.Server.ServerPort);

                TcpMapConnectorSession session = new TcpMapConnectorSession(new SimpleSocketStream(localsock));
                await session.DirectWorkAsync(_sread, _swrite);
            }
            else if (mode == "UDP")
            {
                if (_udpcache == null)
                {
                    lock (typeof(TcpMapServerConnector))
                    {
                        if (_udpcache == null)
                        {
                            var opt = new MemoryCacheOptions();
                            Microsoft.Extensions.Options.IOptions <MemoryCacheOptions> iopt = Microsoft.Extensions.Options.Options.Create(opt);
                            _udpcache = new MemoryCache(iopt);
                        }
                    }
                }

                UdpInfoItem natinfo;
                string      key = connArgument + ":" + sclient.SessionId;
                if (!_udpcache.TryGetValue(key, out natinfo) || natinfo.HasExpired())
                {
                    var udpmsg = await sclient.CreateUDPNatAsync(connArgument);

                    string[] pair = udpmsg.Args[1].Split(':');
                    string   addr = pair[0];
                    int      port = int.Parse(pair[1]);
                    natinfo = new UdpInfoItem(addr + ":" + port);
                    _udpcache.Set(key, natinfo);
                }



                var resmsg = new CommandMessage("ConnectOK", "ConnectOK", supportEncrypt ? "1" : "0", natinfo.NatInfo);
                await clientSock.SendAsync(resmsg.Pack(), SocketFlags.None);

                resmsg = await CommandMessage.ReadFromSocketAsync(clientSock);

                if (resmsg == null)
                {
                    return;
                }
                throw new NotImplementedException("work for " + resmsg);
            }
            else if (mode == "RCP")
            {
                var resmsg = new CommandMessage("ConnectOK", "ConnectOK", supportEncrypt ? "1" : "0"
                                                , ((IPEndPoint)sclient._socket.RemoteEndPoint).Address.ToString(), sclient.OptionRouterClientPort.ToString());
                await clientSock.SendAsync(resmsg.Pack(), SocketFlags.None);
            }
            else
            {
                throw new NotImplementedException();
            }
        }
        private async Task WorkAsync()
        {
            this.IsStarted   = true;
            this.IsConnected = false;
            //LogMessage("ClientSession WorkAsync start");
            int connectedTimes = 0;

            try
            {
                int againTimeout = 125;
StartAgain:
                this.sock_server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                this.sock_server.InitTcp();
                this.cts_connect = new CancellationTokenSource();
                try
                {
                    await this.sock_server.ConnectAsync(this.Client.ServerHost, 6022);

                    //LogMessage("connected to 6022");
                }
                catch (Exception x)
                {
                    this.OnError(x);
                    this.sock_server.CloseSocket();
                    this.sock_server = null;
                    this.cts_connect = new CancellationTokenSource();
                    if (!this.IsStarted)
                    {
                        return;
                    }

                    if (againTimeout < 4)
                    {
                        againTimeout *= 2;
                    }

                    if (await this.cts_connect.Token.WaitForSignalSettedAsync(againTimeout))
                    {
                        return;
                    }

                    goto StartAgain;
                }

                try
                {
                    bool   supportEncrypt = this.Client.UseEncrypt;
                    byte[] clientKeyIV;

                    {
                        CommandMessage connmsg = new CommandMessage
                        {
                            Name = "SessionConnect"
                        };
                        List <string> arglist = new List <string>
                        {
                            this.Client.License.Key,
                            this.Client.ServerPort.ToString()
                        };
                        this.Client.License.GenerateSecureKeyAndHash(out clientKeyIV, out byte[] encryptedKeyIV, out byte[] sourceHash);
                        arglist.Add(Convert.ToBase64String(encryptedKeyIV));
                        arglist.Add(Convert.ToBase64String(sourceHash));
                        arglist.Add(supportEncrypt ? "1" : "0");
                        arglist.Add(this.SessionId);//sessionid at [5]
                        connmsg.Args = arglist.ToArray();

                        await this.sock_server.SendAsync(connmsg.Pack(), SocketFlags.None);

                        //LogMessage("wait for conn msg");

                        connmsg = await CommandMessage.ReadFromSocketAsync(this.sock_server);

                        if (connmsg == null)
                        {
                            TcpMapService.LogMessage("no message ? Connected:" + this.sock_server.Connected);
                            return;
                        }

                        //LogMessage("connmsg : " + connmsg.Name + " : " + string.Join(",", connmsg.Args));

                        if (connmsg.Name != "ConnectOK")
                        {
                            this.IsStarted = false;//don't go until start it again.
                            throw new Exception(connmsg.Name + " : " + string.Join(",", connmsg.Args));
                        }

                        if (supportEncrypt && connmsg.Args[1] == "0")
                        {
                            supportEncrypt = false; this.LogMessage("Warning:server don't support encryption.");
                        }
                    }

                    this.IsConnected = true;

                    connectedTimes++;
                    //LogMessage("ConnectOK #" + connectedTimes);

                    if (supportEncrypt)
                    {
                        this.Client.License.OverrideStream(this.sock_server.CreateStream(), clientKeyIV, out this.sread, out this.swrite);
                    }
                    else
                    {
                        this.sread = this.swrite = this.sock_server.CreateStream();
                    }

                    if (string.IsNullOrEmpty(this.SessionId))
                    {
                        //wait for Upgrade
                        while (this.SessionId == null)
                        {
                            CommandMessage msg;
                            var            cts = new CancellationTokenSource();
                            _ = Task.Run(async delegate
                            {
                                if (await cts.Token.WaitForSignalSettedAsync(16000))
                                {
                                    return;
                                }

                                await this.swrite.WriteAsync(new CommandMessage("_ping_", "forread").Pack());
                            });
                            try
                            {
                                msg = await CommandMessage.ReadFromStreamAsync(this.sread);
                            }
                            finally
                            {
                                cts.Cancel();
                            }

                            if (msg == null)
                            {
                                throw (new SocketException(995));
                            }

                            switch (msg.Name)
                            {
                            case "UpgradeSession":

                                try
                                {
                                    this.sock_local = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                                    this.sock_local.InitTcp();
                                    await this.sock_local.ConnectWithTimeoutAsync(this.Client.ClientHost, this.Client.ClientPort, 12000);
                                }
                                catch (Exception x)
                                {
                                    TcpMapService.OnError(x);
                                    await this.swrite.WriteAsync(new CommandMessage("UpgradeSessionResult", "Failed").Pack());

                                    continue;
                                }
                                this.SessionId = msg.Args[0];
                                if (this.cts_upgrade != null)
                                {
                                    this.cts_upgrade.Cancel();
                                }

                                await this.swrite.WriteAsync(new CommandMessage("UpgradeSessionResult", "OK").Pack());

                                break;

                            case "_ping_":
                                await this.swrite.WriteAsync(new CommandMessage("_ping_result_").Pack());

                                break;

                            case "_ping_result_":
                                break;

                            default:
                                this.LogMessage("Error: 1 Ignore message " + msg);
                                break;
                            }
                        }
                    }

                    await this.WorkAsync(this.sock_local.CreateStream());
                }
                catch (Exception x)
                {
                    this.OnError(x);
                }
            }
            catch (SocketException)
            {
            }
            catch (Exception x)
            {
                this.OnError(x);
            }

            this.IsStarted   = false;
            this.IsConnected = false;
            this.sock_server?.CloseSocket();
            this.sock_local?.CloseSocket();
            this.workend = true;
        }
 protected override async Task WriteMessageAsync(CommandMessage msg)
 {
     this.lastwritetime = DateTime.Now;
     await this.swrite.WriteAsync(msg.Pack());
 }
        private async Task DoCreateUDPNatAsync(CommandMessage msg)
        {
            try
            {
                string[] peerinfo = msg.Args[1].Split(":");
                string   peeraddr = peerinfo[0];
                int      peerport = int.Parse(peerinfo[1]);

                LogMessage("Warning:send whoami to " + Client.ServerHost + ":6023");

                UdpClient udp = new UdpClient();
                udp.Client.ReceiveTimeout = 4321;
                udp.Client.SendTimeout    = 4321;
                udp.Send(Encoding.ASCII.GetBytes("whoami"), 6, Client.ServerHost, 6023);


                LogMessage("Warning:udp.ReceiveAsync");

ReadAgain:
                var rr = await udp.ReceiveAsync();                  //TODO: add timeout..

                if (rr.RemoteEndPoint.Port != 6023)
                {
                    goto ReadAgain;
                }

                string exp = Encoding.ASCII.GetString(rr.Buffer);

                LogMessage("Warning:udp get " + exp);

                if (!exp.StartsWith("UDP="))
                {
                    throw (new Exception("failed"));
                }
                exp         = exp.Remove(0, 4);
                msg.Args[1] = exp;

                //TODO: shall cache and reuse this address ? but always send "hello" to new peer again..
                ClientWorkerUDPConnector udpconn = new ClientWorkerUDPConnector();
                udpconn.Start(this, udp, exp);

                IPEndPoint pperep = new IPEndPoint(IPAddress.Parse(peeraddr), peerport);
                _ = Task.Run(async delegate
                {
                    byte[] msgdata = UDPMeta.CreateSessionIdle(-1);
                    for (int i = 0; i < 10; i++)
                    {
                        if (udpconn.IsEverConnected(pperep))
                        {
                            return;
                        }
                        udp.Send(msgdata, msgdata.Length, pperep);
                        Console.WriteLine("SENT " + pperep + "  via  " + exp);
                        await Task.Delay(100);
                    }
                });
            }
            catch (Exception x)
            {
                OnError(x);
                msg.Args[1] = "Error";
            }
            msg.Name = "CreateUDPNatResult";
            this.LogMessage("TcpMapClientWorker sending " + msg);
            await _swrite.WriteAsync(msg.Pack());
        }