Exemplo n.º 1
0
        private async Task ProcessHeartbeatProtocol(TcpClient client)
        {
            //1.读取clientID

            NetworkStream nstream         = client.GetStream();
            int           heartBeatLength = 2;

            byte[] appRequestBytes = new byte[heartBeatLength];
            int    resultByte      = await nstream.ReadAsync(appRequestBytes, 0, appRequestBytes.Length, Global.DefaultConnectTimeout);

            if (resultByte < 1)
            {
                CloseClient(client);
                return;
            }

            int clientID = StringUtil.DoubleBytesToInt(appRequestBytes[0], appRequestBytes[1]);

            ServerContext.ClientConnectCount += 1;
            //2.更新最后更新时间
            if (ServerContext.Clients.ContainsKey(clientID))
            {
                //2.2 响应ACK
                await nstream.WriteAndFlushAsync(new byte[] { 0x01 }, 0, 1);

                ServerContext.Clients[clientID].LastUpdateTime = DateTime.Now;
            }
            else
            {
                Server.Logger.Debug($"clientId为{clientID}客户端已经被清除。");
            }

            //3.接收完立即关闭
            client.Close();
        }
Exemplo n.º 2
0
        private async Task ProcessHeartbeatProtocol(TcpClient client)
        {
            //1.读取clientID

            NetworkStream nstream         = client.GetStream();
            int           heartBeatLength = 2;

            byte[] appRequestBytes = new byte[heartBeatLength];
            int    resultByte      = await nstream.ReadAsync(appRequestBytes);

            //Server.Logger.Debug("appRequestBytes received.");
            if (resultByte == 0)
            {
                CloseClient(client);
                return;
            }
            //1.2 响应ACK
            await nstream.WriteAndFlushAsync(new byte[] { 0x01 }, 0, 1);

            int clientID = StringUtil.DoubleBytesToInt(appRequestBytes[0], appRequestBytes[1]);

            Server.Logger.Debug($"Now processing {clientID}'s Heartbeat protocol....");
            //2.更新最后更新时间
            if (ConnectionManager.Clients.ContainsKey(clientID))
            {
                ConnectionManager.Clients[clientID].LastUpdateTime = DateTime.Now;
            }
            else
            {
                Server.Logger.Debug($"clientId为{clientID}客户端已经被清除。");
            }

            //3.接收完立即关闭
            //client.Close();
        }
Exemplo n.º 3
0
        private async Task ProcessHeartbeatProtocol(TcpClient client)
        {
            //1.读取clientID

            NetworkStream nstream         = client.GetStream();
            int           heartBeatLength = 2;

            byte[] appRequestBytes = new byte[heartBeatLength];
            int    resultByte      = await nstream.ReadAsync(appRequestBytes, 0, appRequestBytes.Length, Global.DefaultConnectTimeout);

            if (resultByte < 1)
            {
                CloseClient(client);
                return;
            }

            int clientID = StringUtil.DoubleBytesToInt(appRequestBytes[0], appRequestBytes[1]);

            ServerContext.ClientConnectCount += 1;
            //2.更新最后更新时间
            if (ServerContext.Clients.ContainsKey(clientID))
            {
                //2.2 响应ACK
                await nstream.WriteAndFlushAsync(new byte[] { (byte)ControlMethod.TCPTransfer });

                var nspClient = ServerContext.Clients[clientID];
                nspClient.LastUpdateTime = DateTime.Now;

                //2.3 TODO 5 发送保活数据包,一旦建立了这种机制就不需要iocontrol的keepalive了
                try
                {
                    //Server.Logger.Debug($"尝试对{clientID}发送保活数据包..");//TODO 待删除
                    foreach (var kv in nspClient.AppMap)
                    {
                        TcpClient peekedClient = kv.Value.TcpClientBlocks.Peek();
                        if (peekedClient != null)
                        {
                            //发送保活数据
                            await peekedClient.GetStream().WriteAndFlushAsync(new byte[] { (byte)ControlMethod.KeepAlive });
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.Debug($"{clientID}保活失败尝试切断连接");
                    throw ex;
                }
            }
            else
            {
                Server.Logger.Debug($"clientId为{clientID}客户端已经被清除。");
            }


            //3.接收完立即关闭,心跳协议无需使用nagle算法
            client.NoDelay = true;
            client.Close();
        }
Exemplo n.º 4
0
        /// <summary>
        /// 通过token获取clientid
        /// 返回0说明失败,返回-1说明用户被禁
        /// </summary>
        /// <param name="client"></param>
        /// <returns></returns>
        private async Task <int> GetClientIdFromNextTokenBytes(TcpClient client)
        {
            NetworkStream nstream           = client.GetStream();
            int           clientIdFromToken = 0;
            //1.1 获取token长度
            int tokenLengthLength = 2;

            byte[] tokenLengthBytes = new byte[tokenLengthLength];
            int    resultByte01     = await nstream.ReadAsyncEx(tokenLengthBytes);

            Server.Logger.Debug("tokenLengthBytes received.");
            if (resultByte01 < 1)
            {
                CloseClient(client);
                return(0);
            }

            //1.2 获取token
            int tokenLength = StringUtil.DoubleBytesToInt(tokenLengthBytes);

            byte[] tokenBytes   = new byte[tokenLength];
            int    resultByte02 = await nstream.ReadAsyncEx(tokenBytes);

            Server.Logger.Debug("tokenBytes received.");
            if (resultByte02 < 1)
            {
                CloseClient(client);
                return(0);
            }

            string token = tokenBytes.ToASCIIString();

            if (token != Global.NO_TOKEN_STRING)
            {
                var tokenClaims = StringUtil.ConvertStringToTokenClaims(token);
                var userJson    = DbOp.Get(tokenClaims.UserKey);
                if (userJson == null)
                {
                    Server.Logger.Debug("token验证失败");
                }
                else
                {
                    var userId = userJson.ToObject <User>().userId;
                    if (ServerContext.ServerConfig.BoundConfig.UsersBanlist.Contains(userId))
                    {
                        Server.Logger.Debug("用户被禁用");
                        return(-1);
                    }
                    else
                    {
                        clientIdFromToken = int.Parse(userId);
                    }
                }
            }

            return(clientIdFromToken);
        }
Exemplo n.º 5
0
        private async Task ProcessCloseClientProtocol(TcpClient client)
        {
            Server.Logger.Debug("Now processing CloseClient protocol....");
            NetworkStream nstream           = client.GetStream();
            int           closeClientLength = 2;

            byte[] appRequestBytes = new byte[closeClientLength];
            int    resultByte      = await nstream.ReadAsync(appRequestBytes);

            //Server.Logger.Debug("appRequestBytes received.");
            if (resultByte == 0)
            {
                CloseClient(client);
                return;
            }

            int clientID = StringUtil.DoubleBytesToInt(appRequestBytes[0], appRequestBytes[1]);

            //2.更新最后更新时间
            ServerContext.CloseAllSourceByClient(clientID);
            //3.接收完立即关闭
            client.Close();
        }
Exemplo n.º 6
0
        //通过客户端的id请求,分配好服务端端口和appid交给客户端
        //arrange ConfigId from top 4 bytes which received from client.
        //response:
        //   2          1       1       1           1        ...N
        //  clientid    appid   port    appid2      port2
        //request:
        //   2          2
        //  clientid    count
        //  methodType  value = 0
        public byte[] ArrageConfigIds(byte[] appRequestBytes, byte[] consumerPortBytes)
        {
            // byte[] arrangedBytes = new byte[256];
            ClientModel clientModel = new ClientModel();
            int         clientId    = (appRequestBytes[0] << 8) + appRequestBytes[1];
            int         appCount    = (int)appRequestBytes[2];

            if (clientId == 0)
            {
                lock (_lockObject)
                {
                    byte[] tempClientIdBytes = new byte[2];
                    //分配clientid
                    for (int i = 0; i < 10000; i++)
                    {
                        _rand.NextBytes(tempClientIdBytes);
                        int tempClientId = (tempClientIdBytes[0] << 8) + tempClientIdBytes[1];
                        if (!Clients.ContainsKey(tempClientId))
                        {
                            clientModel.ClientId = tempClientId;
                            clientId             = tempClientId;

                            break;
                        }
                    }
                }
            }
            else
            {
                clientModel.ClientId = clientId;
            }

            //注册客户端
            Clients.RegisterNewClient(clientModel.ClientId);
            lock (_lockObject2)
            {
                //注册app
                clientModel.AppList = new List <App>(appCount);
                for (int i = 0; i < appCount; i++)
                {
                    int startPort = StringUtil.DoubleBytesToInt(consumerPortBytes[2 * i], consumerPortBytes[2 * i + 1]);

                    int arrangedAppid = Clients[clientId].RegisterNewApp();
                    //查找port的起始端口如果未指定,则设置为20000
                    if (startPort == 0)
                    {
                        startPort = 20000;
                    }
                    int    port = NetworkUtil.FindOneAvailableTCPPort(startPort);
                    NSPApp app  = Clients[clientId].AppMap[arrangedAppid];
                    app.ClientId       = clientId;
                    app.AppId          = arrangedAppid;
                    app.ConsumePort    = port;
                    app.Tunnels        = new List <TcpTunnel>();
                    app.ReverseClients = new List <TcpClient>();
                    PortAppMap[port]   = app;

                    clientModel.AppList.Add(new App
                    {
                        AppId = arrangedAppid,
                        Port  = port
                    });

                    Logger.Info(port);
                    //配置时触发
                    AppTcpClientMapConfigConnected(this, new AppChangedEventArgs()
                    {
                        App = app
                    });
                }
                Logger.Debug(" <=端口已分配。");
            }
            return(clientModel.ToBytes());
        }
        //通过客户端的id请求,分配好服务端端口和appid交给客户端
        //arrange ConfigId from top 4 bytes which received from client.
        //response:
        //   2          1       1       1           1        ...N
        //  clientid    appid   port    appid2      port2
        //request:
        //   2          2
        //  clientid    count
        //  methodType  value = 0
        public byte[] ArrangeConfigIds(byte[] appRequestBytes, byte[] consumerPortBytes, int highPriorityClientId)
        {
            // byte[] arrangedBytes = new byte[256];
            ClientModel clientModel = new ClientModel();
            int         clientId;

            //apprequestbytes里本身有clientId,但是如果传了highPriorityClientId,那就用这个clientId
            if (highPriorityClientId != 0)
            {
                clientId = highPriorityClientId;
            }
            else
            {
                clientId = StringUtil.DoubleBytesToInt(appRequestBytes[0], appRequestBytes[1]);
            }

            //2.分配clientid //TODO 这一段可能不会再用到了
            int appCount = (int)appRequestBytes[2];

            if (clientId == 0)
            {
                lock (_lockObject)
                {
                    byte[] tempClientIdBytes = new byte[2];
                    //分配clientid
                    for (int i = 0; i < 10000; i++)
                    {
                        _rand.NextBytes(tempClientIdBytes);
                        int tempClientId = (tempClientIdBytes[0] << 8) + tempClientIdBytes[1];
                        if (!ServerContext.Clients.ContainsKey(tempClientId))
                        {
                            clientModel.ClientId = tempClientId;
                            clientId             = tempClientId;

                            break;
                        }
                    }
                }
            }
            else
            {
                clientModel.ClientId = clientId;
            }

            //注册客户端
            ServerContext.Clients.RegisterNewClient(clientModel.ClientId);
            int oneEndpointLength = 2 + 1 + 1024;

            lock (_lockObject2)
            {
                //注册app
                clientModel.AppList = new List <App>(appCount);
                for (int i = 0; i < appCount; i++)
                {
                    int      offset        = oneEndpointLength * i;
                    int      startPort     = StringUtil.DoubleBytesToInt(consumerPortBytes[offset], consumerPortBytes[offset + 1]);
                    int      arrangedAppid = ServerContext.Clients[clientId].RegisterNewApp();
                    Protocol protocol      = (Protocol)consumerPortBytes[offset + 2];
                    string   host          = Encoding.ASCII.GetString(consumerPortBytes, offset + 3, 1024).TrimEnd('\0');
                    //查找port的起始端口如果未指定,则设置为20000
                    if (startPort == 0)
                    {
                        startPort = Global.StartArrangedPort;
                    }
                    int port = 0;
                    //如果端口是指定的并且是绑定的,不加任何检测
                    bool hasListened = false;
                    if (IsBoundedByUser(clientId, startPort))
                    {
                        port = startPort;
                    }
                    else
                    {
                        int relocatedPort = NetworkUtil.FindOneAvailableTCPPort(startPort);
                        if (protocol == Protocol.TCP)
                        {
                            port = relocatedPort; //TODO 2 如果是共享端口协议,如果找不到端口则不进行侦听
                        }
                        else if (protocol == Protocol.HTTP)
                        {
                            //兼容http侦听端口公用
                            if (port != relocatedPort)
                            {
                                //http协议下如果portappmap已经有值,说明已经发起过侦听,接下来不必再侦听
                                if (ServerContext.PortAppMap.ContainsKey(startPort))
                                {
                                    port        = startPort;
                                    hasListened = true;
                                }
                                else
                                {
                                    port = relocatedPort;
                                }
                            }
                        }
                    }
                    NSPApp app = ServerContext.Clients[clientId].AppMap[arrangedAppid];
                    app.ClientId       = clientId;
                    app.AppId          = arrangedAppid;
                    app.ConsumePort    = port;
                    app.AppProtocol    = protocol;
                    app.Host           = host;
                    app.Tunnels        = new List <TcpTunnel>();
                    app.ReverseClients = new List <TcpClient>();
                    //app.Host = host;
                    //TODO 设置app的host和protocoltype
                    if (!ServerContext.PortAppMap.ContainsKey(port))
                    {
                        ServerContext.PortAppMap[port] = new NSPAppGroup();
                    }

                    if (protocol == Protocol.HTTP)
                    {
                        ServerContext.PortAppMap[port].Add(host, app);
                    }

                    ServerContext.PortAppMap[port].ActivateApp = app;
                    //ServerContext.PortAppMap[port] = nspAppGroup;

                    clientModel.AppList.Add(new App
                    {
                        AppId = arrangedAppid,
                        Port  = port
                    });

                    Logger.Info(port);
                    //配置时触发
                    if (!hasListened)
                    {
                        AppTcpClientMapConfigConnected(this, new AppChangedEventArgs()
                        {
                            App = app
                        });                                                                           //触发listener侦听
                    }
                }
                Logger.Debug(" <=端口已分配。");
            }
            return(clientModel.ToBytes());
        }
Exemplo n.º 8
0
        /// <summary>
        /// udp的传输流,只需要处理客户端到服务端的数据流
        /// </summary>
        /// <param name="consumerUdpClient"></param>
        /// <param name="receiveResultRemoteEndPoint"></param>
        /// <param name="providerStream"></param>
        /// <param name="activateAppIsCompress"></param>
        /// <param name="ct"></param>
        /// <param name="consumerEndPoint"></param>
        /// <returns></returns>
        private async Task OpenUdpTransmission(IPEndPoint FXXXconsumerEndPoint,//这个endpoint没有意义
                                               NetworkStream providerStream, NSPAppGroup nspAppGrp, CancellationToken ct)
        {
            try
            {
                //byte[] buffer = new byte[Global.ServerTunnelBufferSize];
                //int bytesRead;
                var nspApp    = nspAppGrp.ActivateApp;
                var udpClient = nspAppGrp.UdpClient;
                //var buffer = new byte[Global.ServerUdpBufferSize];

                while (true)
                {
                    //读取endpoint,必须保证如下数据包原子性
                    //IPv4/IPv6(D)         port    returnbuffer(D)
                    //X                    2       X
                    byte[] ipByte = await providerStream.ReadNextDLengthBytes();//read,(只会存活很短时间不是这个)

                    byte[] portByte     = new byte[2];
                    byte[] returnBuffer = null;

                    await providerStream.ReadAsync(portByte, 0, 2);

                    returnBuffer = await providerStream.ReadNextDLengthBytes();

                    //if (readBytes > 0)
                    //{
                    _ = udpClient.SendAsync(returnBuffer,
                                            returnBuffer.Length,
                                            new IPEndPoint(IPAddress.Parse(Encoding.ASCII.GetString(ipByte)),
                                                           StringUtil.DoubleBytesToInt(portByte)));
                    // }
                }
            }
            catch (Exception ex)
            {
                Logger.Debug("udp隧道传输失败");
                Logger.Debug(ex.ToString());
                throw;
            }


            //    }
            //while ((bytesRead =
            //           await providerStream.ReadAsync(buffer, 0, buffer.Length, ct).ConfigureAwait(false)) != 0)
            //{
            //TODO 8 读取封包
            //读取客户端传过来的封包:consumer源 EndPoint 目的 EndPoint
            //new udpclient发送给相应的udpclient
            //if (isCompress)
            //{
            //    var compressBuffer = StringUtil.DecompressInSnappy(buffer, 0, bytesRead);
            //    bytesRead = compressBuffer.Length;

            //    await consumerUdpClient.SendAsync(compressBuffer, bytesRead, consumerEndPoint);
            //    //await toStream.WriteAsync(compressBuffer, 0, bytesRead, ct).ConfigureAwait(false);
            //}
            //else
            //{
            //    await consumerUdpClient.SendAsync(buffer, bytesRead, consumerEndPoint);
            //    //await toStream.WriteAsync(buffer, 0, bytesRead, ct).ConfigureAwait(false);
            //}
            //ServerContext.TotalSentBytes += bytesRead; //上行
            //}
        }
Exemplo n.º 9
0
        //通过客户端的id请求,分配好服务端端口和appid交给客户端
        //arrange ConfigId from top 4 bytes which received from client.
        //response:
        //   2          1       1       1           1        ...N
        //  clientid    appid   port    appid2      port2
        //request:
        //   2          2
        //  clientid    count
        //  methodType  value = 0
        public byte[] ArrageConfigIds(byte[] appRequestBytes, byte[] consumerPortBytes)
        {
            // byte[] arrangedBytes = new byte[256];
            ClientModel clientModel = new ClientModel();
            int         clientId    = (appRequestBytes[0] << 8) + appRequestBytes[1];
            int         appCount    = (int)appRequestBytes[2];

            if (clientId == 0)
            {
                lock (_lockObject)
                {
                    byte[] tempClientIdBytes = new byte[2];
                    //分配clientid
                    for (int i = 0; i < 10000; i++)
                    {
                        _rand.NextBytes(tempClientIdBytes);
                        int tempClientId = (tempClientIdBytes[0] << 8) + tempClientIdBytes[1];
                        if (!RegisteredClient.ContainsKey(tempClientId))
                        {
                            clientModel.ClientId = tempClientId;
                            clientId             = tempClientId;
                            //注册客户端
                            RegisteredClient.Add(tempClientId, new List <ClientIDAppID>());
                            break;
                        }
                    }
                }
            }
            else
            {
                clientModel.ClientId = clientId;
            }
            lock (_lockObject2)
            {
                //循环获取appid,appid是元素下标+1
                int maxAppCount = RegisteredClient[clientId].Count;
                //增加请求的客户端
                //int[] ports = NetworkUtil.FindAvailableTCPPorts(20000, appCount);
                //foreach (var oneport in ports) Logger.Info(oneport + " ");
                clientModel.AppList = new List <App>(appCount);
                for (int i = 0; i < appCount; i++)
                {
                    int startPort     = StringUtil.DoubleBytesToInt(consumerPortBytes[2 * i], consumerPortBytes[2 * i + 1]);
                    int arrangedAppid = maxAppCount + i + 1;
                    if (arrangedAppid > 255)
                    {
                        throw new Exception("Stack overflow.");
                    }
                    //查找port的起始端口如果未指定,则设置为20000
                    if (startPort == 0)
                    {
                        startPort = 20000;
                    }
                    int port = NetworkUtil.FindOneAvailableTCPPort(startPort);
                    RegisteredClient[clientId].Add(new ClientIDAppID
                    {
                        ClientID = clientId,
                        AppID    = arrangedAppid
                    });
                    clientModel.AppList.Add(new App
                    {
                        AppId = arrangedAppid,
                        Port  = port
                    });
                    var appClient = PortAppMap[port] = new AppModel()
                    {
                        ClientIdAppId = new ClientIDAppID()
                        {
                            ClientID = clientId,
                            AppID    = arrangedAppid
                        },
                        Tunnels        = new List <TcpTunnel>(),
                        ReverseClients = new List <TcpClient>()
                    };
                    Logger.Info(port);
                    //配置时触发
                    AppTcpClientMapConfigConnected(this, new AppChangedEventArgs()
                    {
                        App = appClient.ClientIdAppId
                    });
                }
                Logger.Debug(" <=端口已分配。");
            }
            return(clientModel.ToBytes());
        }
Exemplo n.º 10
0
        private async Task <bool> ProcessAppRequestProtocol(TcpClient client, bool IsReconnect = false)
        {
            Server.Logger.Debug("Now processing request protocol....");
            NetworkStream nstream = client.GetStream();


            //1.读取配置请求1
            //如果是重连请求,则读取接下来5个字符,清
            //空服务端所有与该client相关的所有连接配置
            if (IsReconnect)
            {
                int    clientIdRequestLength = 2;
                byte[] clientRequestBytes    = new byte[clientIdRequestLength];
                int    resultByte0           = await nstream.ReadAsync(clientRequestBytes);

                if (resultByte0 == 0)
                {
                    CloseClient(client);
                    return(true);
                }
                //
                //CloseClient(
                CloseAllSourceByClient(StringUtil.DoubleBytesToInt(clientRequestBytes));
            }

            int configRequestLength = 3;

            byte[] appRequestBytes = new byte[configRequestLength];
            int    resultByte      = await nstream.ReadAsync(appRequestBytes);

            Server.Logger.Debug("appRequestBytes received.");
            if (resultByte == 0)
            {
                CloseClient(client);
                return(true);
            }


            //2.根据配置请求1获取更多配置信息
            int appCount = (int)appRequestBytes[2];

            byte[] consumerPortBytes = new byte[appCount * 2];
            int    resultByte2       = await nstream.ReadAsync(consumerPortBytes);

            Server.Logger.Debug("consumerPortBytes received.");
            if (resultByte2 == 0)
            {
                CloseClient(client);
                return(true);
            }

            //NSPClient nspClient;
            //3.分配配置ID,并且写回给客户端
            try
            {
                byte[] arrangedIds = ConnectionManager.ArrageConfigIds(appRequestBytes, consumerPortBytes);
                Server.Logger.Debug("apprequest arranged");
                await nstream.WriteAsync(arrangedIds);
            }
            catch (Exception ex)
            {
                Logger.Debug(ex.ToString());
            }
            finally
            {
                client.Close();
            }

            ////4.给NSPClient关联configclient
            //nspClient.LastUpdateTime
            Logger.Debug("arrangedIds written.");

            return(false);
        }
Exemplo n.º 11
0
        //通过客户端的id请求,分配好服务端端口和appid交给客户端
        //arrange ConfigId from top 4 bytes which received from client.
        //response:
        //   2          1       1       1           1        ...N
        //  clientid    appid   port    appid2      port2
        //request:
        //   2          2
        //  clientid    count
        //  methodType  value = 0
        public byte[] ArrangeConfigIds(byte[] appRequestBytes, byte[] consumerPortBytes, int highPriorityClientId)
        {
            // byte[] arrangedBytes = new byte[256];
            ClientModel clientModel = new ClientModel();
            int         clientId;

            //apprequestbytes里本身有clientId,但是如果传了highPriorityClientId,那就用这个clientId
            if (highPriorityClientId != 0)
            {
                clientId = highPriorityClientId;
            }
            else
            {
                clientId = StringUtil.DoubleBytesToInt(appRequestBytes[0], appRequestBytes[1]);
            }


            //2.分配clientid //TODO 这一段可能不会再用到了
            int appCount = (int)appRequestBytes[2];

            if (clientId == 0)
            {
                lock (_lockObject)
                {
                    byte[] tempClientIdBytes = new byte[2];
                    //分配clientid
                    for (int i = 0; i < 10000; i++)
                    {
                        _rand.NextBytes(tempClientIdBytes);
                        int tempClientId = (tempClientIdBytes[0] << 8) + tempClientIdBytes[1];
                        if (!ServerContext.Clients.ContainsKey(tempClientId))
                        {
                            clientModel.ClientId = tempClientId;
                            clientId             = tempClientId;

                            break;
                        }
                    }
                }
            }
            else
            {
                clientModel.ClientId = clientId;
            }

            //注册客户端
            ServerContext.Clients.RegisterNewClient(clientModel.ClientId);
            lock (_lockObject2)
            {
                //注册app
                clientModel.AppList = new List <App>(appCount);
                for (int i = 0; i < appCount; i++)
                {
                    int startPort     = StringUtil.DoubleBytesToInt(consumerPortBytes[2 * i], consumerPortBytes[2 * i + 1]);
                    int arrangedAppid = ServerContext.Clients[clientId].RegisterNewApp();
                    //查找port的起始端口如果未指定,则设置为20000
                    if (startPort == 0)
                    {
                        startPort = 20000;
                    }
                    int port = 0;
                    //TODO QQQ 如果端口是指定的并且是绑定的,则直接使用该端口即可
                    if (IsBoundedByUser(clientId, startPort))
                    {
                        port = startPort;
                    }
                    else
                    {
                        port = NetworkUtil.FindOneAvailableTCPPort(startPort);
                    }
                    NSPApp app = ServerContext.Clients[clientId].AppMap[arrangedAppid];
                    app.ClientId                   = clientId;
                    app.AppId                      = arrangedAppid;
                    app.ConsumePort                = port;
                    app.Tunnels                    = new List <TcpTunnel>();
                    app.ReverseClients             = new List <TcpClient>();
                    ServerContext.PortAppMap[port] = app;

                    clientModel.AppList.Add(new App
                    {
                        AppId = arrangedAppid,
                        Port  = port
                    });

                    Logger.Info(port);
                    //配置时触发
                    AppTcpClientMapConfigConnected(this, new AppChangedEventArgs()
                    {
                        App = app
                    });
                }
                Logger.Debug(" <=端口已分配。");
            }
            return(clientModel.ToBytes());
        }