diff --git a/Interact/AttrNames.cs b/Interact/AttrNames.cs deleted file mode 100644 index 0c7e985..0000000 --- a/Interact/AttrNames.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Interact -{ - //Json数据的字段访问字符串 - public static class AttrNames - { - //基本信息 - public const string Token = "Token"; - public const string Operation = "Operation"; - - //处理结果信息 - public const string Error = "Error"; - - //获取用户信息 - public const string User = "User"; - public const string NickName = "NickName"; - public const string Motto = "Motto"; - public const string UGroup = "UGroup"; - public const string Photo = "Photo"; - public const string Total = "Total"; - public const string Count = "Count"; - - //消息 - public const string When = "When"; - public const string From = "From"; - public const string To = "To"; - } -} diff --git a/Interact/Client-Fields.cs b/Interact/Client-Fields.cs deleted file mode 100644 index e3aaaf5..0000000 --- a/Interact/Client-Fields.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Net.Sockets; -using System.Collections.Concurrent; -using System.Threading; -using System.Collections.Generic; - -//定义StormClient私有字段 - -namespace Interact -{ - public partial class StormClient - { - //TCP客户端 - private static TcpClient tcpClient; - //消息发送队列 - private static BlockingCollection sendQueue; - //数据读取线程 - private static Thread readLoopThread; - //数据发送线程 - private static Thread sendLoopThread; - //某些请求需要持续的等待服务器返回数据包,然后将所有数据包处理后提供给用户, - // 此字段用于暂存这些未接收完全的数据包。它的key是对应请求的Token字串。 - private static Dictionary workingDictionary; - - //属性控制字段,任何地方都不应该修改它们 - //发送消息时是否要求服务器返回处理结果。由DoesSendMessageReturn属性控制,不要直接修改这个字段 - private static volatile bool doesSendMessageReturnResult = false; - //当前连接状态。由Status属性控制,不要直接修改这个字段 - private static volatile ClientStatus clientStatus = ClientStatus.Uninitialized; - - //线程安全-锁 - //更改连接状态锁 - private static object statusLocker = new object(); - //"发送消息时是否要求服务器返回处理结果"Falg锁 - private static object doesSendMessageReturnLocker = new object(); - } -} diff --git a/Interact/Client-Handle.cs b/Interact/Client-Handle.cs deleted file mode 100644 index 4d66c96..0000000 --- a/Interact/Client-Handle.cs +++ /dev/null @@ -1,194 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Drawing; -using System.IO; - -//StormClient对数据的处理相关内容 - -namespace Interact -{ - public partial class StormClient - { - //处理连接中断异常。 - private static void HandleConnectionBroken(Exception ex) - { - lock (statusLocker) - { - if (Status != ClientStatus.Running) - return; - statusLocker = ClientStatus.Stopped; - tcpClient.Close(); - OnDisconnect?.Invoke(ex); - } - } - - #region 处理服务器数据 - //根据包头信息处理数据 - private static void HandleAll(byte[] head, byte[] data) - { - string headStr = Encoding.UTF8.GetString(head); - JObject jsonObj = (JObject)JsonConvert.DeserializeObject(headStr); - switch (jsonObj[AttrNames.Operation].ToString()) - { - case Operations.Panic: - HandlePanic(GetResultHead(jsonObj)); - break; - case Operations.Login: - HandleLoginResult(jsonObj, data); - break; - case Operations.TransMessage: - HandleMessage(jsonObj, data); - break; - case Operations.SendMessage: - HandleSendMessageResult(GetResultHead(jsonObj)); - break; - case Operations.Offline: - HandlePanic(GetResultHead(jsonObj)); - break; - case Operations.UpdateUserInfo: - HandleUpdateUserInfoDone(GetResultHead(jsonObj)); - break; - case Operations.GetUsers: - HandleGetUsersPack(jsonObj, data); - break; - default: - break; - } - } - //提取基本结果包头结构 - private static ResultHead GetResultHead(JObject head) - { - ResultHead resultHead = new ResultHead - { - Token = head[AttrNames.Token].ToString(), - Operation = head[AttrNames.Operation].ToString(), - }; - if (head[AttrNames.Error] != null) - resultHead.Error = head[AttrNames.Error].ToString(); - return resultHead; - } - - //处理服务器异常断开连接消息 - private static void HandlePanic(ResultHead head) - { - SetStatus(ClientStatus.Stopped); - OnPanic?.Invoke(head); - } - - //处理登录反馈消息 - private static void HandleLoginResult(JObject head, byte[] data) - { - //提取结果包头 - ResultHead resultHead = GetResultHead(head); - //发起登录完成事件 - if (head[AttrNames.Error].ToString() != "") - { - OnLoginDone?.Invoke(resultHead, null); - return; - } - User user = new User - { - Name = head[AttrNames.User].ToString(), - NickName = head[AttrNames.NickName].ToString(), - Motto = head[AttrNames.Motto].ToString(), - Group = (UserGroup)Enum.Parse(typeof(UserGroup), head[AttrNames.UGroup].ToString()), - Photo = User.DefaultPhoto - }; - if (int.Parse(head[AttrNames.Photo].ToString()) > 0) - { - MemoryStream ms = new MemoryStream(data); - user.Photo = Image.FromStream(ms); - } //用户头像数据 - OnLoginDone?.Invoke(resultHead, user); - } - - //收到消息 - private static void HandleMessage(JObject head, byte[] data) - { - //提取结果包头 - ResultHead resultHead = GetResultHead(head); - Message message = new Message() - { - When = DateTime.Parse(head[AttrNames.When].ToString()), - From = User.FromUserName(head[AttrNames.From].ToString()), - To = User.Me, - Text = Encoding.UTF8.GetString(data) - }; - OnMessage?.Invoke(message); - } - - //发送消息执行结果 - private static void HandleSendMessageResult(ResultHead head) - { - OnSendMessageDone?.Invoke(head); - } - - //处理掉线、强制登出 - private static void HandleOffiline(ResultHead head) - { - SetStatus(ClientStatus.Stopped); - OnOffline?.Invoke(head); - } - - //处理更改信息结果 - private static void HandleUpdateUserInfoDone(ResultHead head) - { - OnUpdateUserInfoDone?.Invoke(head); - } - - //处理获取用户列表的不连续返回包。当所有包接收完毕后返回给客户 - private static void HandleGetUsersPack(JObject head, byte[] data) - { - ResultHead result = GetResultHead(head); - if (head[AttrNames.Error].ToString() != string.Empty) - { - OnGetUserListDone?.Invoke(result, new User[0]); - return; - } //获取失败 - else if (int.Parse(head[AttrNames.Count].ToString()) <= 0) - { - User[] us; - if (workingDictionary.ContainsKey(result.Token)) - us = workingDictionary[result.Token] as User[]; - else - us = new User[0]; - User.Users = us; - OnGetUserListDone?.Invoke(result, us); - return; - } //获取完成 - //获取用户数据 - User user = new User - { - Name = head[AttrNames.User].ToString(), - NickName = head[AttrNames.NickName].ToString(), - Motto = head[AttrNames.Motto].ToString(), - Group = (UserGroup)Enum.Parse(typeof(UserGroup), head[AttrNames.UGroup].ToString()), - Photo = User.DefaultPhoto - }; //基础数据 - if (int.Parse(head[AttrNames.Photo].ToString()) > 0) - { - MemoryStream ms = new MemoryStream(data); - user.Photo = Image.FromStream(ms); - } //头像数据 - //加入临时用户列表缓存 - User[] users; - int total = int.Parse(head[AttrNames.Total].ToString()); //总用户数 - int count = int.Parse(head[AttrNames.Count].ToString()); //已接收用户数 - if (count > total) - return; - if (workingDictionary.ContainsKey(head[AttrNames.Token].ToString())) - users = workingDictionary[result.Token] as User[]; - else - users = new User[total]; - users[count - 1] = user; - workingDictionary[result.Token] = users; - } - #endregion - - } - -} diff --git a/Interact/Client-Read.cs b/Interact/Client-Read.cs deleted file mode 100644 index 79c2cf8..0000000 --- a/Interact/Client-Read.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Net; -using System.Net.Sockets; - -//StormClient读取数据相关内容 -namespace Interact -{ - public partial class StormClient - { - // 数据读取线程,从tcp连接读取数据 - private static void ReadLoop() - { - NetworkStream stream = StormClient.tcpClient.GetStream(); - try - { - while (StormClient.Status == ClientStatus.Running) - { - byte[] head = ReadDataWithLength(stream); - byte[] data = ReadDataWithLength(stream); - HandleAll(head, data); - } - } - catch (System.IO.IOException ex) - { - HandleConnectionBroken(ex); - } - catch (ObjectDisposedException ex) - { - HandleConnectionBroken(ex); - } - } - // 从网络流读取数据 - private static byte[] ReadDataWithLength(NetworkStream stream) - { - byte[] lenBuffer = new byte[4]; //数据长度 - //获取数据长度 - stream.Read(lenBuffer, 0, 4); - int length = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(lenBuffer, 0)); - if (length <= 0) - return null; - //读取数据 - byte[] data = new byte[length]; - stream.Read(data, 0, length); - return data; - } - } -} diff --git a/Interact/Client-Send.cs b/Interact/Client-Send.cs deleted file mode 100644 index 589655f..0000000 --- a/Interact/Client-Send.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Newtonsoft.Json; -using System.Net; -using System.Net.Sockets; - -//StormClient发送数据相关内容 -namespace Interact -{ - public partial class StormClient - { - // 消息写入线程,向tcp连接写入数据 - private static void SendLoop() - { - Packet packet = null; - NetworkStream stream = tcpClient.GetStream(); - //json序列化时忽略值为null的项 - JsonSerializerSettings setting = new JsonSerializerSettings(); - setting.NullValueHandling = NullValueHandling.Ignore; - try - { - //阻塞式的从发送队列中取出数据 - foreach (Packet i in sendQueue.GetConsumingEnumerable()) - { - packet = i; //为了在代码块外引用i - //结束循环 - if (StormClient.Status != ClientStatus.Running) - break; - //向tcp连接写数据 - byte[] lenBuffer; //数据长度缓存 - byte[] headData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(packet.Head, setting)); - //写包头 - lenBuffer = BitConverter.GetBytes((UInt32)IPAddress.HostToNetworkOrder(headData.Length)); - stream.Write(lenBuffer, 0, 4); - stream.Write(headData, 0, headData.Length); - //写数据 - int length = 0; - if (packet.Data != null) - length = packet.Data.Length; - lenBuffer = BitConverter.GetBytes((UInt32)IPAddress.HostToNetworkOrder(length)); - stream.Write(lenBuffer, 0, 4); - if (length > 0) - stream.Write(packet.Data, 0, packet.Data.Length); - //发送成功回调 - ResultHead result = new ResultHead - { - Token = packet.Head.Token, - Operation = packet.Head.Operation, - Error = "" - }; - packet.CallBack?.Invoke(result); - } - } - catch (System.IO.IOException ex) - { - HandleConnectionBroken(ex); - } - catch (ObjectDisposedException ex) - { - HandleConnectionBroken(ex); - } - finally - { - List packets = new List(sendQueue.Count + 1); - if (packet != null) - packets.Add(packet); - while (sendQueue.TryTake(out packet)) - { - if (packet != null) - packets.Add(packet); - } - foreach (Packet p in packets) - { - //发送失败回调 - ResultHead result = new ResultHead - { - Token = packet.Head.Token, - Operation = packet.Head.Operation, - Error = "Client has stopped running." - }; - packet.CallBack?.Invoke(result); - } - } - } - //发送数据 - private static bool Send(Packet packet) - { - if (sendLoopThread == null || !sendLoopThread.IsAlive) - return false; - //加入数据发送队列等待 - sendQueue.Add(packet); - return true; - } - } -} diff --git a/Interact/Client.cs b/Interact/Client.cs deleted file mode 100644 index c0560c5..0000000 --- a/Interact/Client.cs +++ /dev/null @@ -1,260 +0,0 @@ -using System; -using System.Text; -using System.Net.Sockets; -using System.Threading; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; - -namespace Interact -{ - //连接中断 - public delegate void ConnectionBrokenHandler(Exception ex); - //通用请求结果处理 - public delegate void ResultHandler(ResultHead head); - //消息到达事件处理 - public delegate void MessageHandler(Message msg); - //登录反馈到达 - public delegate void LoginDoneHandler(ResultHead head, User user); - //获取用户列表结果处理 - public delegate void GetUserListDoneHandler(ResultHead head, User[] users); - - /// - /// 与服务器进行数据通信,并向外部提供一系列通信接口 - /// - public static partial class StormClient - { - /// - /// 连接中断事件。注意,此事件并不保证连接中断时总是触发,例如主动关闭连接、登出操作 - /// - public static event ConnectionBrokenHandler OnDisconnect; - /// - /// 服务器异常消息处理事件 - /// - public static event ResultHandler OnPanic; - /// - /// 服务器强制注销登陆事件 - /// - public static event ResultHandler OnOffline; - /// - /// 消息到达事件 - /// - public static event MessageHandler OnMessage; - /// - /// 发送消息处理完成反馈事件(仅在DoesSendMessageReturn为true时有效) - /// - public static event ResultHandler OnSendMessageDone; - /// - /// 更新用户信息结果处理事件 - /// - public static event ResultHandler OnUpdateUserInfoDone; - /// - /// 登录结果处理事件 - /// - public static event LoginDoneHandler OnLoginDone; - /// - /// 获取用户列表结果处理事件 - /// - public static event GetUserListDoneHandler OnGetUserListDone; - - //当前连接状态 - public static ClientStatus Status - { - get { lock (statusLocker) { return clientStatus; } } - } - //发送消息时是否要求服务器返回处理结果 - public static bool DoesSendMessageReturn - { - get { lock (doesSendMessageReturnLocker) { return doesSendMessageReturnResult; } } - set { lock (doesSendMessageReturnLocker) { doesSendMessageReturnResult = value; } } - } - - - static StormClient() - { - tcpClient = new TcpClient(); - sendQueue = new BlockingCollection(); - workingDictionary = new Dictionary(); - SetStatus(ClientStatus.Uninitialized); - } - - //设置当前连接状态 - internal static void SetStatus(ClientStatus value) - { - lock (statusLocker) { clientStatus = value; } - } - - /// - /// 初始化客户端,连接服务器。如果成功则启动数据接收和发送线程。 - /// 注意,当网络不稳定时此方法可能会阻塞。 - /// - /// 如果已连接到服务器返回true,否则返回false。 - public static bool Initialize() - { - if (StormClient.tcpClient.Connected) - return true; - try - { - StormClient.tcpClient.Connect(Interact.Properties.Resources.RemoteServerAddr, int.Parse(Interact.Properties.Resources.RemoteServerPort)); - } - catch (Exception) - { - return false; - } - workingDictionary?.Clear(); //清空临时记录 - if (sendLoopThread != null) - sendLoopThread.Abort(); - if (readLoopThread != null) - readLoopThread.Abort(); - //启动数据收发线程 - sendLoopThread = new Thread(new ThreadStart(SendLoop)); - readLoopThread = new Thread(new ThreadStart(ReadLoop)); - sendLoopThread.IsBackground = readLoopThread.IsBackground = true; - SetStatus(ClientStatus.Running); - sendLoopThread.Start(); - readLoopThread.Start(); - return true; - } - - /// - /// 登录。此操作为异步操作,服务器返回结果后产生OnLoginDone事件。 - /// - /// 用户名 - /// 密码 - /// 数据发送完毕回调函数。注意,结果处理回调请设置OnLoginDone事件 - /// 成功将请求加入发送队列返回true,否则返回false。 - public static bool QueueLogin(string user, string password, Action callback = null) - { - JsonLogInfoHead logInfo = new JsonLogInfoHead() - { - Token = Guid.NewGuid().ToString(), - Operation = Operations.Login, - User = user, - Pwd = password - }; - Packet packet = new Packet - { - Head = logInfo, - Data = null, - CallBack = callback - }; - return Send(packet); - } - - /// - /// 请求发送一条消息。异步,此方法将请求放入请求队列后返回。 - /// - /// 要发送的消息文本 - /// 消息接收者 - /// 数据发送完毕时回调函数。 - /// 成功将请求加入发送队列返回true,否则返回false。 - public static bool QueueSendMessage(Message message, Action callback = null) - { - JsonSendMessageHead jsonObj = new JsonSendMessageHead() - { - Token = "", - Operation = Operations.SendMessage, - To = message.To.Name, - NeedResult = DoesSendMessageReturn ? "1" : "0" - }; - Packet packet = new Packet - { - Head = jsonObj, - Data = Encoding.UTF8.GetBytes(message.Text), - CallBack = callback - }; - return Send(packet); - } - - /// - /// 请求登出。异步,此方法将请求放入请求队列后返回。 - /// - /// 数据发送完毕时回调函数。 - /// 成功将请求加入发送队列返回true,否则返回false。 - public static bool QueueLogout(Action callback = null) - { - BaseHead jsonObj = new BaseHead() - { - Token = "", - Operation = Operations.Logout, - }; - Packet packet = new Packet - { - Head = jsonObj, - Data = null, - CallBack = callback - }; - bool success = Send(packet); - SetStatus(ClientStatus.Stopped); - return success; - } - - /// - /// 请求用户列表。异步,此方法将请求放入请求队列后返回。 - /// - /// 数据发送完毕时回调函数。 - /// 成功将请求加入发送队列返回true,否则返回false。 - public static bool QueueGetUserList(Action callback = null) - { - BaseHead jsonObj = new BaseHead() - { - Token = "", - Operation = Operations.GetUsers, - }; - Packet packet = new Packet - { - Head = jsonObj, - Data = null, - CallBack = callback - }; - return Send(packet); - } - - /// - /// 请求更新当前登录用户信息。异步,此方法将请求放入请求队列后返回。 - /// - /// 新的用户信息,如果某项值为null,则不改变该项。 - /// 数据发送完毕时回调函数。 - /// 成功将请求加入发送队列返回true,否则返回false。 - public static bool QueueUpdateUserInfo(UserInfo userInfo, Action callback = null) - { - byte[] photoData = null; //头像数据 - //将头像数据转换到字符数组 - if (userInfo.Photo != null) - { - MemoryStream memStream = new MemoryStream(); - userInfo.Photo.Save(memStream, userInfo.Photo.RawFormat); - photoData = new byte[memStream.Length]; - memStream.Seek(0, SeekOrigin.Begin); - memStream.Read(photoData, 0, photoData.Length); - } - //构建数据包 - JsonUserInfoHead jsonObj = new JsonUserInfoHead() - { - Token = Guid.NewGuid().ToString(), - Operation = Operations.UpdateUserInfo, - NickName = userInfo.NickName, - Password = userInfo.Password, - Motto = userInfo.Motto, - Photo = userInfo.Photo == null ? 0 : photoData.Length - }; - Packet packet = new Packet - { - Head = jsonObj, - Data = photoData, - CallBack = callback - }; - return Send(packet); - } - - /// - /// 客户端连接状态 - /// - public enum ClientStatus - { - Uninitialized, //未启动 - Running, //运行中 - Stopped //已停止 - } - } -} diff --git a/Interact/Exception.cs b/Interact/Exception.cs deleted file mode 100644 index 6634055..0000000 --- a/Interact/Exception.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Interact -{ - public class ConnectionBrokenException : Exception - { - public ConnectionBrokenException(string message) : base(message) { } - } -} diff --git a/Interact/Interact.csproj b/Interact/Interact.csproj deleted file mode 100644 index 7b2a3cb..0000000 --- a/Interact/Interact.csproj +++ /dev/null @@ -1,71 +0,0 @@ - - - - - Debug - AnyCPU - {CEF054AF-95B7-4B88-934E-02705FEEA554} - Library - Properties - Interact - Interact - v4.0 - 512 - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - .\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - True - True - Resources.resx - - - - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - \ No newline at end of file diff --git a/Interact/JsonObject.cs b/Interact/JsonObject.cs deleted file mode 100644 index d70a7c1..0000000 --- a/Interact/JsonObject.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -//定义Json序列化与反序列化时使用的数据结构,仅内部使用 - -namespace Interact -{ - //登录信息包头 - internal class JsonLogInfoHead : BaseHead - { - public string User; - public string Pwd; - } - - //发送消息包头 - internal class JsonSendMessageHead : BaseHead - { - public string To; //消息接收者用户名 - public string NeedResult = "0"; //是否要求服务器返回处理结果。否传“0” - } - - //更新用户信息包头 - internal class JsonUserInfoHead : BaseHead - { - public string NickName = null; - public string Password = null; - public string Motto = null; - public int Photo = 0; //新头像大小 - } -} diff --git a/Interact/Message.cs b/Interact/Message.cs deleted file mode 100644 index 26df5b0..0000000 --- a/Interact/Message.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Interact -{ - //定义一则用户接收到的消息 - public class Message - { - public Message() { } - public Message(string text, User Receiver) - { - When = DateTime.Now; - From = User.Me; - To = Receiver; - Text = text; - } - - /*保留 - public const UInt32 MaxHeadLength = 4096; //最大包头长度 - public const UInt32 MaxMessageLength = 0x6400000; //最大数据长度 - */ - public DateTime When; //服务器接收时间 - public User From; //消息发送者 - public User To; //消息接收者 - public string Text; //消息内容 - - //将消息保存到数据库(保留) - protected bool Save() { return false; } - } - - //从服务器接收的原始数据封包 - internal class Packet - { - public BaseHead Head; //包头 - public byte[] Data; //包内容 - public Action CallBack; //数据发送完成的回调函数。注意,此时仅保证数据已发送, - //服务器可能并没有处理完成请求 - } - //允许请求的操作列表。具体定义见设计文档 - public static class Operations - { - public const string Panic = "Panic"; - public const string TransMessage = "TransMessage"; - public const string SendMessage = "SendMessage"; - public const string Login = "Login2"; - public const string Logout = "Logout"; - public const string Offline = "Offline"; - public const string UpdateUserInfo = "UpdateUserInfo"; - public const string GetUsers = "GetUsers"; - } - - #region 定义数据交互用到的数据结构(可能作为事件或回调函数的参数) - //包头基本结构 - public class BaseHead - { - //请求ID,由发送方定义的随机字符串。如果接受方有返回数据应包含相同的Token - public string Token; - //操作类型,决定数据的其他内容 - public string Operation; - } - // 接受方返回的执行结果反馈包头,包含错误文本 - public class ResultHead : BaseHead - { - //错误文本,为空则执行成功 - public string Error; - } - //获取单一用户信息结果包头 - #endregion -} diff --git a/Interact/Newtonsoft.Json.dll b/Interact/Newtonsoft.Json.dll deleted file mode 100644 index 5b3a1eb..0000000 Binary files a/Interact/Newtonsoft.Json.dll and /dev/null differ diff --git a/Interact/Properties/AssemblyInfo.cs b/Interact/Properties/AssemblyInfo.cs deleted file mode 100644 index 49a4d20..0000000 --- a/Interact/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Interact")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Interact")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("cef054af-95b7-4b88-934e-02705feea554")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Interact/Properties/Resources.Designer.cs b/Interact/Properties/Resources.Designer.cs deleted file mode 100644 index 66f8f97..0000000 --- a/Interact/Properties/Resources.Designer.cs +++ /dev/null @@ -1,81 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Interact.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Interact.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to mattuy.top. - /// - internal static string RemoteServerAddr { - get { - return ResourceManager.GetString("RemoteServerAddr", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to 3727. - /// - internal static string RemoteServerPort { - get { - return ResourceManager.GetString("RemoteServerPort", resourceCulture); - } - } - } -} diff --git a/Interact/Properties/Resources.resx b/Interact/Properties/Resources.resx deleted file mode 100644 index f7b9790..0000000 --- a/Interact/Properties/Resources.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - mattuy.top - - - 3727 - - \ No newline at end of file diff --git a/Interact/User.cs b/Interact/User.cs deleted file mode 100644 index 5976cc1..0000000 --- a/Interact/User.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Drawing; - -namespace Interact -{ - //用户 - public class User - { - //根据用户名匹配用户。如果用户不存在,则返回一个仅用户名有效的实例 - internal static User FromUserName(string userName) - { - if(User.Users != null) - { - foreach (User user in User.Users) - { - if (user.Name == userName) - return user; - } - } - User unidentifiedUser = new User - { - Name = userName, - NickName = "", - Motto = "", - Group = UserGroup.User, - Photo = User.DefaultPhoto - }; - return unidentifiedUser; - } - internal static User[] Users; //用户列表。仅Interact内部访问 - - public static User Me; //当前用户 - public static Image DefaultPhoto; //默认头像 - - public string Name; //用户名 - public string NickName; //昵称 - public string Motto; //个性签名 - public UserGroup Group; //用户组 - public Image Photo; //头像 - } - - //更新用户信息时新的用户信息。如果项值为null则不更改相应的信息 - public class UserInfo - { - public string Password = null; //新密码 - public string NickName = null; //新昵称 - public string Motto = null; //新签名 - public Image Photo = null; //新头像 - } - - //用户类型 - public enum UserGroup - { - User, //普通用户 - Vip, //会员 - Admin, //管理员 - Group //群组 - } -} diff --git a/READEME.md b/READEME.md new file mode 100644 index 0000000..a89629d --- /dev/null +++ b/READEME.md @@ -0,0 +1,230 @@ +# StormChat + +## 简介 +  前段时间心血来潮想学习最近的明星编程语言Golang。于是想做个聊天小程序实践一下。程序基于TCP协议通信,更详细的设计见设计思路 + +  用Golang实现了服务端程序,同时代码抄抄改改做了个Golang客户端,又由于输出问题,用C++写了个Windows控制台的简陋的[输出控制库](https://github.com/mattuylee/conctrl),于是客户端在Windows上能看了(不过朋友说很丑>_<)。 + +  在朋友([@Billows](https://github.com/KaniuBillows))的鼓励下,我们决定用C#写一个客户端程序,正好他在学习WPF编程,于是界面采用了WPF编写。我负责后台数据交互,他负责前台界面逻辑。 + +  **C#客户端还使用到了JSON格式化库Newtonsoft.Json,特此说明。** + + +## 设计思路 +### 通信流程 +1. 发送方(客户端或者服务器)发送数据包; +2. 接收方(服务器或者客户端)处理数据; +3. 如果需要反馈,接收方发送反馈数据包; +4. 发送方处理反馈数据(如果有)。 + +### 通信数据包结构 +数据通信基于TCP协议,每次通信的数据包应包含【包头域+数据域】。包头域总是JSON格式、UTF-8编码的字符串,数据域由包头决定,可以为空。通信数据包结构如图。 + +数据包结构图 + +HEAD(包头域)至少包含两个参数: +* Toekn +* Operation + +Token参数是一次通信的标识文本,由请求方随机生成,处理方返回数据包的Token参数应和请求方的Token参数一致(如果有返回数据)。 + +Operation参数指定本次通信的请求。它决定了数据包的其他数据。Operation具体行为定义见这里。 + +本来做了设计图表,不过第一次用starUML,画了一半才发现完全是鬼画桃符,没有掌握正确的作图方式,也没什么热情重新画了。因此这里不展示完整的设计文件了(本来也不完整),上面的链接是截取的关键部分(才知道starUML可以导出html文档)。 + +至于原文件...留着权当做个纪念,当作自己学习的见证吧(我也很无奈啊(* ̄︿ ̄))。实在是不嫌弃不怕被误导的话,也可以看看。文件是starUML3的设计文件(.mdj),需要starUML软件打开(付费的,不过可以免费使用)。这是设计文件。 + +哦,还有服务器端的数据库结构,见文件stormchat-server/res/stormchat.sql。 + +## 环境配置 + +1. ### 开发配置 + 服务器端:Windows/Linux Golang 1.11 + 客户端-Go:Windows x64,C++,Golang 1.11 + 客户端-C#:.NET4.0,WPF,Newtonsoft.Json for .NET4.0 + +2. ### 运行配置 + 服务器端:Linux/Windows, MySQL5.5+/MariaDB10.0+ + 客户端:Windows 7以上 + + +## 程序配置 +由于懒癌发作,一些参数设置只能在编译时指定好,没有运行中指定参数的功能。 + +Go语言的部分(服务器程序和golang客户端程序)这块都在control.go文件中,C#客户端这边也没什么可配置的,也就是连接服务器的地址和端口。下面列出部分配置参数: + +### 服务器端 +* **serveMode** + 服务(后台)模式。如果此参数为true,日志输出到str_log_file参数指定的文件中,且Debug()函数将不输出内容。如果为false,日志输出和Debug()函数都将直接向控制台或终端输出。 + +* max_head_length + 最大包头长度,单位字节,如果包头长度超过此参数将被抛弃。 + +* max_message_length + 最大消息长度,单位字节,如果消息长度超过此参数将被抛弃。 + +* max_photo_size + 最大头像大小,单位字节,如果头像数据长度超过此参数将被抛弃。 + +* timeout_message + 客户端连接最大数据等待时间,单位秒。如果超过此时间客户端没有数据到达则断开连接(此参数当前未启用)。 + +* str_log_file + 日志文件。仅在serveMode为true时有效。注意,程序并不会自动清理此文件。 + +* **str_db_conn_str** + MySQL数据库连接字符串。 + +### 客户端(Go) +* server_addr + 服务器地址和端口。 + +### 客户端(C#) +资源定义在StormChat解决方案,Interact项目的属性->资源中。 +* RemoteServerAddr + 服务器地址。 + +* RemoteServerPort + 服务器端口。 + + +## 如何编译 +首先安装git和golang,这一步请自行解决; + +克隆项目到本地: +> `git clone -b master https://github.com/mattuylee/stormchat.git` + +假设项目已克隆到本地*DIR*目录,命令行切换到stormchat目录: + +> `cd DIR/stormchat` + +1. ### 服务器端 +#### 切换到服务器端工程目录: +> `cd stormchat-server` + +#### 克隆mysql操作库 +Go标准库里是没有数据库操作的库的,因此先添加mysql操作库到项目中。创建目录*sotormchat-server/src/github.com/go-sql-driver/*,然后克隆MySQL操作库: +> `cd src/github.com/go-sql-driver` +> `git clone https://github.com/go-sql-driver/mysql.git` + +#### 配置环境变量GOPATH +Windows下: +如果环境变量GOPATH存在,则在GOPATH后添加 ";DIR/stormchat/stormchat-server"(不含引号,注意分号),如果不存在添加GOPATH环境变量并将其设置为*DIR/stormchat/stormchat-server*即可,注意把DIR换成git仓库所在目录。 +Linux下: +先查看GOPATH环境变量的值,再添加项目目录到GOPATH: +> `echo $GOPATH` +如果值为空: +> `export GOPATH=DIR/stormchat/stormchat-server` +如果值不为空: +> `export GOPATH=$GOPATH:DIR/stormchat/stormchat-server` + +#### 编译程序 +切换到stormchat-server/src/stormchat/目录,然后编译: +> `cd DIR/stormchat/stormchat-server/src/stormchat` +> `go build` + +#### 配置MySQL +登录mysql: +> `mysql -uroot -p` + +为stormchat创建数据库: +> `CREATE DATABASE stormchat CHARSET=UTF8;` + +导入数据库结构: +> `SOURCE DIR/stormchat/stormchat-server/res/stormchat.sql;` + +创建用户并授权: +> `CREATE USER 'stormchat'@'localhost' IDENTIFIED BY 'stormchat';` +> `GRANT ALL ON stormchat.* TO 'stormchat'@'localhost';` +> `FLUSH PRIVILEGES;` + +#### 启动服务器程序 +Linux下执行下列命令以守护进程运行: +> `nohup ./stormchat &` + +Windows请自行探索。 + +2. ### 客户端-Go +> `cd DIR/stormchat/stormchat-client-golang/src` +> `go build` + +注意,**客户端运行时需要conctrl_x64.dll在运行目录**下,此文件在stormchat-client-golang/res/目录下。 + +3. ### 客户端-C# +Visual Studio 2015以上版本打开项目,直接编译即可。 + + +## 注意事项 +* 没有设计注册账户的API,只能在强插数据库。emmmm,这个坑懒得填了。 +* 服务器端的日志文件不会自动清除(反正也没什么日志要写)。 +* 其他的,想到再补充。 + + +## 目录结构(主要部分) +
+stormchat
+│  .gitattributes
+│  .gitignore
+│  LICENSE          //许可证
+│  READEME.md       //此帮助文件
+│  
+├─design            //设计文件
+│    message.png
+│    operations.svg
+│    stormchat.mdj       //starUML设计文件
+│      
+│
+├─stormchat-server  //服务器端工程目录
+│  ├─res
+│  │     stormchat.sql  //数据库结构
+│  │      
+│  └─src                //代码文件
+│      └─stormchat
+│            control.go
+│            err.go
+│            main.go
+│            message.go
+│            session-deprecated.go  //已不推荐使用的接口
+│            session.go
+│            storm-server.go
+│            user.go
+│─stormchat-client-golang  //Go客户端工程目录
+│  ├─res
+│  │     conctrl_x64.dll            //Winodws控制台输出库
+│  │     icon.ico                   //客户端图标
+│  │      
+│  └─src
+│        control.go                 //参数控制
+│        main.go
+│        session.go
+│        stormchat-client.syso      //资源文件(图标资源)
+│        user.go
+│        win-console.go             //输出控制,调用conctrl库
+│               
+└─stormchat-client-csharp   //C#客户端工程目录
+   ├─Interact                   //数据交互模块
+   │  │  Interact.csproj            //Visual Studio项目文件
+   │  │  Newtonsoft.Json.dll        //JSON格式化库
+   │  │  AttrNames.cs               //一些字符串常量
+   │  │  Client.cs                  //提供给数据表现模块的静态类
+   │  │  Client-Fields.cs           //部分类,定义内部字段
+   │  │  Client-Handle.cs           //部分类,处理服务器数据的方法
+   │  │  Client-Read.cs             //部分类,接收服务器数据的方法
+   │  │  Client-Send.cs             //部分类,向服务器发送数据的方法
+   │  │  Exception.cs
+   │  │  jsonObject.cs              //定义一些内部使用的结构,用于JSON格式化
+   │  │  Message.cs                 //定义一些数据结构,用于数据交换
+   │  │  User.cs                    //定义用户
+   │  │  
+   │  └─Properties                  //项目属性和资源
+   │          AssemblyInfo.cs
+   │          Resources.Designer.cs
+   │          Resources.resx
+   │          
+   └─StormChatWPF               //数据表现和用户交互模块*
+
+ +## 使用截图 +### Golang客户端 + + diff --git a/StormChatWPF/App.config b/StormChatWPF/App.config deleted file mode 100644 index 74ade9d..0000000 --- a/StormChatWPF/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/StormChatWPF/App.xaml b/StormChatWPF/App.xaml deleted file mode 100644 index 1fc1979..0000000 --- a/StormChatWPF/App.xaml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - diff --git a/StormChatWPF/App.xaml.cs b/StormChatWPF/App.xaml.cs deleted file mode 100644 index 55f30e6..0000000 --- a/StormChatWPF/App.xaml.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Data; -using System.Linq; -using System.Threading.Tasks; -using System.Windows; - -namespace StormChatWPF -{ - /// - /// App.xaml 的交互逻辑 - /// - public partial class App : Application - { - } -} diff --git a/StormChatWPF/Chat.cs b/StormChatWPF/Chat.cs deleted file mode 100644 index 0e54842..0000000 --- a/StormChatWPF/Chat.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Interact; -using System.Drawing; -using System.Windows; - -namespace StormChatWPF -{ - class Chat - { - public static List ContactsList;//联系人集合 - public static Chat chat=new Chat(); - private Chat() - { - StormClient.OnLoginDone += OnHaveLogin; - StormClient.OnGetUserListDone += OnGetContactsList; - StormClient.OnMessage +=OnMessage; - } - internal static User CurrentContact { get; set; }//设置当前联系人对象 - /// - /// 获取联系人列表(完成后打开主窗体) - /// - private void OnGetContactsList(ResultHead head, User[] users) - { - if (head.Error != "") - { - MessageBox.Show("无法获取联系人列表"); - return; - } - App.Current.Dispatcher.Invoke( - (Action)delegate () - { - ContactsList = new List(users); - MainWindow mainWindow = new MainWindow(); - mainWindow.Show(); - LogWindow.Instence.Close(); - }); - } - private void OnHaveLogin(ResultHead head, User user) - { - if (head.Error == "") - { - User.Me = user; - StormClient.QueueGetUserList(); - } - else - { - MessageBox.Show("请核对账号密码!"); - } - }//登录完成 - private void OnMessage(Message message) - { - if (MainWindow.Instence != null) - { - MainWindow.Instence.ShowMessage(message); - } - }//接受到新消息 - - internal void SendMessage(string text,User target) - { - Message msg = new Message(text,target); - Action f = delegate (ResultHead head) - { - MainWindow.Instence.ShowMessage(msg); - }; - StormClient.QueueSendMessage(msg,f); - }//发送消息 - internal static void Log(string user, string password) - { - if (StormClient.Initialize()) - { - if (!StormClient.QueueLogin(user, password)) - { - MessageBox.Show("登录失败"); - } - } - else - { - MessageBox.Show("连接服务器失败!"); - } - }//登录 - - } -} diff --git a/StormChatWPF/LogWindow.xaml b/StormChatWPF/LogWindow.xaml deleted file mode 100644 index 86d517f..0000000 --- a/StormChatWPF/LogWindow.xaml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - -