完善网络框架,增加服务器断开连接回调

This commit is contained in:
ALEXTANG
2023-07-18 17:36:48 +08:00
parent a473971cfb
commit 526baf45fa
39 changed files with 559 additions and 308 deletions

View File

@@ -1,75 +0,0 @@
#if TENGINE_NET
namespace TEngine.Core;
public static class ExcelDefine
{
/// <summary>
/// 项目跟目录路径
/// </summary>
private const string ProjectPath = "../../..";
/// <summary>
/// 配置文件根目录
/// </summary>
public static string ProgramPath = $"{ProjectPath}/Config/Excel/";
/// <summary>
/// 版本文件Excel
/// </summary>
public static string ExcelVersionFile = $"{ProgramPath}Version.txt";
/// <summary>
/// 服务器代码生成文件夹
/// </summary>
public static string ServerFileDirectory = $"{ProjectPath}/Server/TEngine.Model/Generate/ConfigTable/Entity/";
/// <summary>
/// 客户端代码生成文件夹
/// </summary>
public static string ClientFileDirectory = $"{ProjectPath}/Client/Unity/Assets/Scripts/TEngine/TEngine.Model/Generate/ConfigTable/Entity/";
/// <summary>
/// 服务器二进制数据文件夹
/// </summary>
public static string ServerBinaryDirectory = $"{ProjectPath}/Config/Binary/";
/// <summary>
/// 客户端二进制数据文件夹
/// </summary>
public static string ClientBinaryDirectory = $"{ProjectPath}/Client/Unity/Assets/Bundles/Config/";
/// <summary>
/// 服务器Json数据文件夹
/// </summary>
public static string ServerJsonDirectory = $"{ProjectPath}/Config/Json/Server/";
/// <summary>
/// 客户端Json数据文件夹
/// </summary>
public static string ClientJsonDirectory = $"{ProjectPath}/Config/Json/Client/";
/// <summary>
/// 服务器自定义导出代码
/// </summary>
public static string ServerCustomExportDirectory = $"{ProjectPath}/Server/TEngine.Model/Generate/CustomExport/";
/// <summary>
/// 客户端自定义导出代码
/// </summary>
public static string ClientCustomExportDirectory = $"{ProjectPath}/Client/Unity/Assets/Scripts/TEngine/TEngine.Model/Generate/CustomExport/";
/// <summary>
/// 导表支持的类型
/// </summary>
public static readonly HashSet<string> ColTypeSet = new HashSet<string>()
{
"", "0", "bool", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "string", "AttrConfig",
"short[]", "int[]", "long[]", "float[]", "string[]"
};
/// <summary>
/// Excel生成代码模板的位置
/// </summary>
public static string ExcelTemplatePath = $"{ProjectPath}/Config/Template/ExcelTemplate.txt";
/// <summary>
/// 代码模板
/// </summary>
public static string ExcelTemplate
{
get
{
return _template ??= File.ReadAllText(Path.Combine(Environment.CurrentDirectory, $"{ProjectPath}/Config/Template/ExcelTemplate.txt"));
}
}
private static string _template;
}
#endif

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 0dac52ee40f938641a63d624ff19dd3f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -32,7 +32,7 @@ public sealed class ExcelExporter
public ExcelExporter(ExportType exportType)
{
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
var versionFilePath = ExcelDefine.ExcelVersionFile;
var versionFilePath = Define.ExcelVersionFile;
switch (exportType)
{
@@ -47,8 +47,8 @@ public sealed class ExcelExporter
File.Delete(versionFilePath);
}
FileHelper.ClearDirectoryFile(ExcelDefine.ServerFileDirectory);
FileHelper.ClearDirectoryFile(ExcelDefine.ClientFileDirectory);
FileHelper.ClearDirectoryFile(Define.ExcelServerFileDirectory);
FileHelper.ClearDirectoryFile(Define.ExcelClientFileDirectory);
break;
}
}
@@ -63,12 +63,12 @@ public sealed class ExcelExporter
private static void CustomExport()
{
// 清除文件夹
FileHelper.ClearDirectoryFile(ExcelDefine.ServerCustomExportDirectory);
FileHelper.ClearDirectoryFile(ExcelDefine.ClientCustomExportDirectory);
FileHelper.ClearDirectoryFile(Define.ServerCustomExportDirectory);
FileHelper.ClearDirectoryFile(Define.ClientCustomExportDirectory);
// 找到程序集
var assemblyLoadContext = new AssemblyLoadContext("ExporterDll", true);
var dllBytes = File.ReadAllBytes(Path.Combine(Environment.CurrentDirectory, "Logic.dll"));
var pdbBytes = File.ReadAllBytes(Path.Combine(Environment.CurrentDirectory, "Logic.pdb"));
var dllBytes = File.ReadAllBytes($"{Define.CustomExportAssembly}.dll");
var pdbBytes = File.ReadAllBytes($"{Define.CustomExportAssembly}.pdb");
var assembly = assemblyLoadContext.LoadFromStream(new MemoryStream(dllBytes), new MemoryStream(pdbBytes));
// 加载程序集
AssemblyManager.LoadAssembly(int.MaxValue, assembly);
@@ -95,7 +95,7 @@ public sealed class ExcelExporter
/// </summary>
private void Find()
{
var versionFilePath = ExcelDefine.ExcelVersionFile;
var versionFilePath = Define.ExcelVersionFile;
if(File.Exists(versionFilePath))
{
@@ -107,7 +107,7 @@ public sealed class ExcelExporter
_versionDic = new Dictionary<string, long>();
}
var dir = new DirectoryInfo(ExcelDefine.ProgramPath);
var dir = new DirectoryInfo(Define.ExcelProgramPath);
var excelFiles = dir.GetFiles("*", SearchOption.AllDirectories);
if (excelFiles.Length <= 0)
@@ -256,8 +256,8 @@ public sealed class ExcelExporter
continue;
}
if (!ExcelDefine.ColTypeSet.Contains(serverType) ||
!ExcelDefine.ColTypeSet.Contains(clientType))
if (!Define.ColTypeSet.Contains(serverType) ||
!Define.ColTypeSet.Contains(clientType))
{
Exporter.LogError($"配置表 {exportInfo.Name} {col} 列 [{colName}] 客户端类型 {clientType}, 服务端类型 {serverType} 不合法");
continue;
@@ -297,12 +297,12 @@ public sealed class ExcelExporter
writeToClassTask.Add(Task.Run(() =>
{
WriteToClass(excelTable.ServerColInfos, ExcelDefine.ServerFileDirectory, true);
WriteToClass(excelTable.ServerColInfos, Define.ExcelServerFileDirectory, true);
}));
writeToClassTask.Add(Task.Run(() =>
{
WriteToClass(excelTable.ClientColInfos, ExcelDefine.ClientFileDirectory, false);
WriteToClass(excelTable.ClientColInfos, Define.ExcelClientFileDirectory, false);
}));
Task.WaitAll(writeToClassTask.ToArray());
@@ -385,7 +385,7 @@ public sealed class ExcelExporter
}
}
var template = ExcelDefine.ExcelTemplate;
var template = Define.ExcelTemplate;
if (fileBuilder.Length > 0)
{
@@ -407,8 +407,8 @@ public sealed class ExcelExporter
private void ExportToBinary()
{
var exportToBinaryTasks = new List<Task>();
var dynamicServerAssembly = DynamicAssembly.Load(ExcelDefine.ServerFileDirectory);
var dynamicClientAssembly = DynamicAssembly.Load(ExcelDefine.ClientFileDirectory);
var dynamicServerAssembly = DynamicAssembly.Load(Define.ExcelServerFileDirectory);
var dynamicClientAssembly = DynamicAssembly.Load(Define.ExcelClientFileDirectory);
foreach (var (tableName, tableList) in _tables)
{
@@ -465,7 +465,7 @@ public sealed class ExcelExporter
if (serverDynamicInfo?.ConfigData != null)
{
var bytes = ProtoBufHelper.ToBytes(serverDynamicInfo.ConfigData);
var serverBinaryDirectory = ExcelDefine.ServerBinaryDirectory;
var serverBinaryDirectory = Define.ExcelServerBinaryDirectory;
if (!Directory.Exists(serverBinaryDirectory))
{
@@ -476,7 +476,7 @@ public sealed class ExcelExporter
if (serverDynamicInfo.Json.Length > 0)
{
var serverJsonDirectory = ExcelDefine.ServerJsonDirectory;
var serverJsonDirectory = Define.ExcelServerJsonDirectory;
using var sw = new StreamWriter(Path.Combine(serverJsonDirectory, $"{csName}Data.Json"));
sw.WriteLine("{\"List\":[");
sw.Write(serverDynamicInfo.Json.ToString());
@@ -487,7 +487,7 @@ public sealed class ExcelExporter
if (clientDynamicInfo?.ConfigData != null)
{
var bytes = ProtoBufHelper.ToBytes(clientDynamicInfo.ConfigData);
var clientBinaryDirectory = ExcelDefine.ClientBinaryDirectory;
var clientBinaryDirectory = Define.ExcelClientBinaryDirectory;
if (!Directory.Exists(clientBinaryDirectory))
{
@@ -498,7 +498,7 @@ public sealed class ExcelExporter
if (clientDynamicInfo.Json.Length > 0)
{
var clientJsonDirectory = ExcelDefine.ClientJsonDirectory;
var clientJsonDirectory = Define.ExcelClientJsonDirectory;
using var sw = new StreamWriter(Path.Combine(clientJsonDirectory, $"{csName}Data.Json"));
sw.WriteLine("{\"List\":[");
sw.Write(clientDynamicInfo.Json.ToString());

View File

@@ -16,7 +16,7 @@ public sealed class Exporter
public void Start()
{
Console.OutputEncoding = Encoding.UTF8;
var exportType = Define.Options.ExportType;
var exportType = AppDefine.Options.ExportType;
if (exportType != ExportType.None)
{
@@ -39,7 +39,7 @@ public sealed class Exporter
LogInfo("");
exportType = (ExportType) key;
LoadConfig();
// LoadConfig();
switch (exportType)
{
@@ -61,50 +61,52 @@ public sealed class Exporter
Environment.Exit(0);
}
private void LoadConfig()
{
const string settingsName = "TEngineSettings.json";
var currentDirectory = Directory.GetCurrentDirectory();
if (!File.Exists(Path.Combine(currentDirectory, settingsName)))
{
throw new FileNotFoundException($"not found {settingsName} in OutputDirectory");
}
var configurationRoot = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(settingsName)
.Build();
// ProtoBuf文件所在的位置文件夹位置
ProtoBufDefine.ProtoBufDirectory = configurationRoot["Export:ProtoBufDirectory:Value"];
// ProtoBuf生成到服务端的文件夹位置
ProtoBufDefine.ServerDirectory = configurationRoot["Export:ProtoBufServerDirectory:Value"];
// ProtoBuf生成到客户端的文件夹位置
ProtoBufDefine.ClientDirectory = configurationRoot["Export:ProtoBufClientDirectory:Value"];
// ProtoBuf生成代码模板的位置
ProtoBufDefine.ProtoBufTemplatePath = configurationRoot["Export:ProtoBufTemplatePath:Value"];
// Excel配置文件根目录
ExcelDefine.ProgramPath = configurationRoot["Export:ExcelProgramPath:Value"];
// Excel版本文件的位置
ExcelDefine.ExcelVersionFile = configurationRoot["Export:ExcelVersionFile:Value"];
// Excel生成服务器代码的文件夹位置
ExcelDefine.ServerFileDirectory = configurationRoot["Export:ExcelServerFileDirectory:Value"];
// Excel生成客户端代码文件夹位置
ExcelDefine.ClientFileDirectory = configurationRoot["Export:ExcelClientFileDirectory:Value"];
// Excel生成服务器二进制数据文件夹位置
ExcelDefine.ServerBinaryDirectory = configurationRoot["Export:ExcelServerBinaryDirectory:Value"];
// Excel生成客户端二进制数据文件夹位置
ExcelDefine.ClientBinaryDirectory = configurationRoot["Export:ExcelClientBinaryDirectory:Value"];
// Excel生成服务器Json数据文件夹位置
ExcelDefine.ServerJsonDirectory = configurationRoot["Export:ExcelServerJsonDirectory:Value"];
// Excel生成客户端Json数据文件夹位置
ExcelDefine.ClientJsonDirectory = configurationRoot["Export:ExcelClientJsonDirectory:Value"];
// Excel生成代码模板的位置
ExcelDefine.ExcelTemplatePath = configurationRoot["Export:ExcelTemplatePath:Value"];
// 服务器自定义导出代码文件夹位置
ExcelDefine.ServerCustomExportDirectory = configurationRoot["Export:ServerCustomExportDirectory:Value"];
// 客户端自定义导出代码文件夹位置
ExcelDefine.ClientCustomExportDirectory = configurationRoot["Export:ClientCustomExportDirectory:Value"];
}
#if old
// private void LoadConfig()
// {
// const string settingsName = "TEngineSettings.json";
// var currentDirectory = Directory.GetCurrentDirectory();
//
// if (!File.Exists(Path.Combine(currentDirectory, settingsName)))
// {
// throw new FileNotFoundException($"not found {settingsName} in OutputDirectory");
// }
//
// var configurationRoot = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
// .AddJsonFile(settingsName)
// .Build();
// // ProtoBuf文件所在的位置文件夹位置
// ProtoBufDefine.ProtoBufDirectory = configurationRoot["Export:ProtoBufDirectory:Value"];
// // ProtoBuf生成到服务端的文件夹位置
// ProtoBufDefine.ServerDirectory = configurationRoot["Export:ProtoBufServerDirectory:Value"];
// // ProtoBuf生成到客户端的文件夹位置
// ProtoBufDefine.ClientDirectory = configurationRoot["Export:ProtoBufClientDirectory:Value"];
// // ProtoBuf生成代码模板的位置
// ProtoBufDefine.ProtoBufTemplatePath = configurationRoot["Export:ProtoBufTemplatePath:Value"];
// // Excel配置文件根目录
// ExcelDefine.ProgramPath = configurationRoot["Export:ExcelProgramPath:Value"];
// // Excel版本文件的位置
// ExcelDefine.ExcelVersionFile = configurationRoot["Export:ExcelVersionFile:Value"];
// // Excel生成服务器代码的文件夹位置
// ExcelDefine.ServerFileDirectory = configurationRoot["Export:ExcelServerFileDirectory:Value"];
// // Excel生成客户端代码文件夹位置
// ExcelDefine.ClientFileDirectory = configurationRoot["Export:ExcelClientFileDirectory:Value"];
// // Excel生成服务器二进制数据文件夹位置
// ExcelDefine.ServerBinaryDirectory = configurationRoot["Export:ExcelServerBinaryDirectory:Value"];
// // Excel生成客户端二进制数据文件夹位置
// ExcelDefine.ClientBinaryDirectory = configurationRoot["Export:ExcelClientBinaryDirectory:Value"];
// // Excel生成服务器Json数据文件夹位置
// ExcelDefine.ServerJsonDirectory = configurationRoot["Export:ExcelServerJsonDirectory:Value"];
// // Excel生成客户端Json数据文件夹位置
// ExcelDefine.ClientJsonDirectory = configurationRoot["Export:ExcelClientJsonDirectory:Value"];
// // Excel生成代码模板的位置
// ExcelDefine.ExcelTemplatePath = configurationRoot["Export:ExcelTemplatePath:Value"];
// // 服务器自定义导出代码文件夹位置
// ExcelDefine.ServerCustomExportDirectory = configurationRoot["Export:ServerCustomExportDirectory:Value"];
// // 客户端自定义导出代码文件夹位置
// ExcelDefine.ClientCustomExportDirectory = configurationRoot["Export:ClientCustomExportDirectory:Value"];
// }
#endif
public static void LogInfo(string msg)
{

View File

@@ -20,22 +20,22 @@ public abstract class ACustomExport : ICustomExport
{
case CustomExportType.Client:
{
if (!Directory.Exists(ExcelDefine.ClientCustomExportDirectory))
if (!Directory.Exists(Define.ClientCustomExportDirectory))
{
Directory.CreateDirectory(ExcelDefine.ClientCustomExportDirectory);
Directory.CreateDirectory(Define.ClientCustomExportDirectory);
}
File.WriteAllText($"{ExcelDefine.ClientCustomExportDirectory}/{fileName}", fileContent);
File.WriteAllText($"{Define.ClientCustomExportDirectory}/{fileName}", fileContent);
return;
}
case CustomExportType.Server:
{
if (!Directory.Exists(ExcelDefine.ServerCustomExportDirectory))
if (!Directory.Exists(Define.ServerCustomExportDirectory))
{
Directory.CreateDirectory(ExcelDefine.ServerCustomExportDirectory);
Directory.CreateDirectory(Define.ServerCustomExportDirectory);
}
File.WriteAllText($"{ExcelDefine.ServerCustomExportDirectory}/{fileName}", fileContent);
File.WriteAllText($"{Define.ServerCustomExportDirectory}/{fileName}", fileContent);
return;
}
}

View File

@@ -1,28 +0,0 @@
#if TENGINE_NET
namespace TEngine.Core;
public static class ProtoBufDefine
{
public static readonly char[] SplitChars = {' ', '\t'};
/// <summary>
/// 项目跟目录路径
/// </summary>
private const string ProjectPath = "../../..";
/// <summary>
/// ProtoBuf文件夹
/// </summary>
public static string ProtoBufDirectory = $"{ProjectPath}/Config/ProtoBuf/";
/// <summary>
/// 服务端生成文件夹
/// </summary>
public static string ServerDirectory = $"{ProjectPath}/Server/TEngine.Model/Generate/NetworkProtocol/";
/// <summary>
/// 客户端生成文件夹
/// </summary>
public static string ClientDirectory = $"{ProjectPath}/Client/Unity/Assets/Scripts/TEngine/TEngine.Model/Generate/NetworkProtocol/";
/// <summary>
/// 代码模板路径
/// </summary>
public static string ProtoBufTemplatePath = $"{ProjectPath}/Config/Template/ProtoTemplate.txt";
}
#endif

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: d418e7f4b6646504c8a6ca4b381992b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -39,14 +39,14 @@ public sealed class ProtoBufExporter
{
Console.OutputEncoding = Encoding.UTF8;
if (!Directory.Exists(ProtoBufDefine.ServerDirectory))
if (!Directory.Exists(Define.ProtoBufServerDirectory))
{
Directory.CreateDirectory(ProtoBufDefine.ServerDirectory);
Directory.CreateDirectory(Define.ProtoBufServerDirectory);
}
if (!Directory.Exists(ProtoBufDefine.ClientDirectory))
if (!Directory.Exists(Define.ProtoBufClientDirectory))
{
Directory.CreateDirectory(ProtoBufDefine.ClientDirectory);
Directory.CreateDirectory(Define.ProtoBufClientDirectory);
}
var tasks = new Task[2];
@@ -86,9 +86,9 @@ public sealed class ProtoBufExporter
_aRouteRequest = Opcode.OuterRouteRequest;
_aRouteResponse = Opcode.OuterRouteResponse;
opCodeName = "OuterOpcode";
protoFile = $"{ProtoBufDefine.ProtoBufDirectory}OuterMessage.proto";
saveDirectory.Add(ProtoBufDefine.ServerDirectory, _serverTemplate);
saveDirectory.Add(ProtoBufDefine.ClientDirectory, _clientTemplate);
protoFile = $"{Define.ProtoBufDirectory}OuterMessage.proto";
saveDirectory.Add(Define.ProtoBufServerDirectory, _serverTemplate);
saveDirectory.Add(Define.ProtoBufClientDirectory, _clientTemplate);
break;
}
case ProtoBufOpCodeType.Inner:
@@ -101,8 +101,8 @@ public sealed class ProtoBufExporter
_aRouteRequest = Opcode.InnerRouteRequest + 1000;
_aRouteResponse = Opcode.InnerRouteResponse + 1000;
opCodeName = "InnerOpcode";
protoFile = $"{ProtoBufDefine.ProtoBufDirectory}InnerMessage.proto";
saveDirectory.Add(ProtoBufDefine.ServerDirectory, _serverTemplate);
protoFile = $"{Define.ProtoBufDirectory}InnerMessage.proto";
saveDirectory.Add(Define.ProtoBufServerDirectory, _serverTemplate);
break;
}
case ProtoBufOpCodeType.InnerBson:
@@ -115,8 +115,8 @@ public sealed class ProtoBufExporter
_aRouteRequest = Opcode.InnerBsonRouteRequest + 1000;
_aRouteResponse = Opcode.InnerBsonRouteResponse + 1000;
opCodeName = "InnerBsonOpcode";
protoFile = $"{ProtoBufDefine.ProtoBufDirectory}InnerBsonMessage.proto";
saveDirectory.Add(ProtoBufDefine.ServerDirectory, _serverTemplate);
protoFile = $"{Define.ProtoBufDirectory}InnerBsonMessage.proto";
saveDirectory.Add(Define.ProtoBufServerDirectory, _serverTemplate);
break;
}
}
@@ -143,7 +143,7 @@ public sealed class ProtoBufExporter
isMsgHead = true;
opcodeInfo = new OpcodeInfo();
file.AppendLine("\t[ProtoContract]");
className = currentLine.Split(ProtoBufDefine.SplitChars, StringSplitOptions.RemoveEmptyEntries)[1];
className = currentLine.Split(Define.SplitChars, StringSplitOptions.RemoveEmptyEntries)[1];
var splits = currentLine.Split(new[] {"//"}, StringSplitOptions.RemoveEmptyEntries);
if (splits.Length > 1)
@@ -337,7 +337,7 @@ public sealed class ProtoBufExporter
private async Task RouteType()
{
var routeTypeFile = $"{ProtoBufDefine.ProtoBufDirectory}RouteType.Config";
var routeTypeFile = $"{Define.ProtoBufDirectory}RouteType.Config";
var protoFileText = await File.ReadAllTextAsync(routeTypeFile);
var routeTypeFileSb = new StringBuilder();
routeTypeFileSb.AppendLine("namespace TEngine.Core.Network\n{");
@@ -369,8 +369,8 @@ public sealed class ProtoBufExporter
routeTypeFileSb.AppendLine("\t}\n}");
var file = routeTypeFileSb.ToString();
await File.WriteAllTextAsync($"{ProtoBufDefine.ServerDirectory}RouteType.cs", file);
await File.WriteAllTextAsync($"{ProtoBufDefine.ClientDirectory}RouteType.cs", file);
await File.WriteAllTextAsync($"{Define.ProtoBufServerDirectory}RouteType.cs", file);
await File.WriteAllTextAsync($"{Define.ProtoBufClientDirectory}RouteType.cs", file);
}
private void Repeated(StringBuilder file, string newline)
@@ -379,7 +379,7 @@ public sealed class ProtoBufExporter
{
var index = newline.IndexOf(";", StringComparison.Ordinal);
newline = newline.Remove(index);
var property = newline.Split(ProtoBufDefine.SplitChars, StringSplitOptions.RemoveEmptyEntries);
var property = newline.Split(Define.SplitChars, StringSplitOptions.RemoveEmptyEntries);
var type = property[1];
var name = property[2];
var memberIndex = int.Parse(property[4]);
@@ -400,7 +400,7 @@ public sealed class ProtoBufExporter
{
var index = currentLine.IndexOf(";", StringComparison.Ordinal);
currentLine = currentLine.Remove(index);
var property = currentLine.Split(ProtoBufDefine.SplitChars, StringSplitOptions.RemoveEmptyEntries);
var property = currentLine.Split(Define.SplitChars, StringSplitOptions.RemoveEmptyEntries);
var type = property[0];
var name = property[1];
var memberIndex = int.Parse(property[3]);
@@ -451,7 +451,7 @@ public sealed class ProtoBufExporter
private void LoadTemplate()
{
string[] lines = File.ReadAllLines(ProtoBufDefine.ProtoBufTemplatePath, Encoding.UTF8);
string[] lines = File.ReadAllLines(Define.ProtoBufTemplatePath, Encoding.UTF8);
StringBuilder serverSb = new StringBuilder();
StringBuilder clientSb = new StringBuilder();