diff --git a/Interact/AttrNames.cs b/Interact/AttrNames.cs index e910385..0c7e985 100644 --- a/Interact/AttrNames.cs +++ b/Interact/AttrNames.cs @@ -15,11 +15,18 @@ namespace Interact //处理结果信息 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.cs b/Interact/Client.cs index d782ff0..70e00e6 100644 --- a/Interact/Client.cs +++ b/Interact/Client.cs @@ -59,6 +59,7 @@ namespace Interact { tcpClient = new TcpClient(); sendQueue = new BlockingCollection(); + workingDictionary = new Dictionary(); Status = ClientStatus.Uninitialized; } @@ -79,8 +80,7 @@ namespace Interact { return false; } - - workingDictionary.Clear(); //清空临时记录 + workingDictionary?.Clear(); //清空临时记录 if (sendLoopThread != null) sendLoopThread.Abort(); if (readLoopThread != null) @@ -102,11 +102,11 @@ namespace Interact /// 密码 /// 数据发送完毕回调函数。注意,结果处理回调请设置OnLoginDone事件 /// 成功将请求加入发送队列返回true,否则返回false。 - public static bool QueueLogin(string user, string password, Action callback = null) + public static bool QueueLogin(string user, string password, Action callback = null) { JsonLogInfoHead logInfo = new JsonLogInfoHead() { - Token = "", + Token = Guid.NewGuid().ToString(), Operation = Operations.Login, User = user, Pwd = password @@ -127,19 +127,19 @@ namespace Interact /// 消息接收者 /// 数据发送完毕时回调函数 /// 成功将请求加入发送队列返回true,否则返回false。 - public static bool QueueSendMessage(string text, User to, Action callback = null) + public static bool QueueSendMessage(Message message, Action callback = null) { JsonSendMessageHead jsonObj = new JsonSendMessageHead() { Token = "", Operation = Operations.SendMessage, - To = to.Name, + To = message.To.Name, NeedResult = DoesSendMessageReturn ? "1" : "0" }; Packet packet = new Packet { Head = jsonObj, - Data = Encoding.UTF8.GetBytes(text), + Data = Encoding.UTF8.GetBytes(message.Text), CallBack = callback }; return Send(packet); @@ -150,7 +150,7 @@ namespace Interact /// /// 数据发送完毕时回调函数 /// 成功将请求加入发送队列返回true,否则返回false。 - public static bool QueueLogout(Action callback = null) + public static bool QueueLogout(Action callback = null) { BaseHead jsonObj = new BaseHead() { @@ -171,7 +171,7 @@ namespace Interact /// /// 数据发送完毕时回调函数 /// 成功将请求加入发送队列返回true,否则返回false。 - public static bool QueueGetUserList(Action callback = null) + public static bool QueueGetUserList(Action callback = null) { BaseHead jsonObj = new BaseHead() { @@ -192,7 +192,7 @@ namespace Interact /// /// 数据发送完毕时回调函数 /// 成功将请求加入发送队列返回true,否则返回false。 - public static bool QueueUpdateUserInfo(UserInfo userInfo, Action callback = null) + public static bool QueueUpdateUserInfo(UserInfo userInfo, Action callback = null) { byte[] photoData = null; //头像数据 //将头像数据转换到字符数组 diff --git a/Interact/Handle.cs b/Interact/Handle.cs index 8574546..0fe1728 100644 --- a/Interact/Handle.cs +++ b/Interact/Handle.cs @@ -28,29 +28,29 @@ namespace Interact #region 处理服务器数据 //根据包头信息处理数据 - private static void HandleAll(byte[] headData, byte[] data) + private static void HandleAll(byte[] head, byte[] data) { - string head = Encoding.UTF8.GetString(headData); - JObject jsonObj = (JObject)JsonConvert.DeserializeObject(head); + string headStr = Encoding.UTF8.GetString(head); + JObject jsonObj = (JObject)JsonConvert.DeserializeObject(headStr); switch (jsonObj[AttrNames.Operation].ToString()) { - case Operations.Login: - HandleLoginResult(jsonObj, data); - break; case Operations.Panic: HandlePanic(GetResultHead(jsonObj)); break; + case Operations.Login: + HandleLoginResult(jsonObj, data); + break; case Operations.TransMessage: + HandleMessage(jsonObj, data); break; case Operations.SendMessage: break; - case Operations.Logout: - break; case Operations.Offline: break; case Operations.UpdateUserInfo: break; case Operations.GetUsers: + HandleGetUsersPack(jsonObj, data); break; default: break; @@ -63,8 +63,9 @@ namespace Interact { Token = head[AttrNames.Token].ToString(), Operation = head[AttrNames.Operation].ToString(), - Error = head[AttrNames.Error].ToString() }; + if (head[AttrNames.Error] != null) + resultHead.Error = head[AttrNames.Error].ToString(); return resultHead; } @@ -102,15 +103,67 @@ namespace Interact 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 HandleGetUsersPack(JObject head, byte[] data) { + ResultHead result = GetResultHead(head); if (head[AttrNames.Error].ToString() != string.Empty) { - OnGetUserListDone?.Invoke(GetResultHead(head), null); + 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/JsonObject.cs b/Interact/JsonObject.cs index 50af897..be58a46 100644 --- a/Interact/JsonObject.cs +++ b/Interact/JsonObject.cs @@ -3,32 +3,10 @@ using System.Collections.Generic; using System.Linq; using System.Text; +//定义Json序列化与反序列化时使用的数据结构,仅内部使用 + namespace Interact { - #region 定义数据交互用到的数据结构(可能作为事件或回调函数的参数) - //包头基本结构 - public class BaseHead - { - //请求ID,由发送方定义的随机字符串。如果接受方有返回数据应包含相同的Token - //此特性暂时未启用,传空字符串即可 - public string Token; - //操作类型,决定数据的其他内容 - public string Operation; - } - // 接受方返回的执行结果反馈包头,包含错误文本 - public class ResultHead : BaseHead - { - //错误文本,为空则执行成功 - public string Error; - } - //获取单一用户信息结果包头 - public class GetUserInfoResultHead : ResultHead - { - - } - #endregion - - #region 定义Json序列化与反序列化时使用的数据结构,仅内部使用 //登录信息包头 internal class JsonLogInfoHead : BaseHead { @@ -57,6 +35,4 @@ namespace Interact { public string User; //要获取头像的用户名 } - - #endregion } diff --git a/Interact/Message.cs b/Interact/Message.cs index dce3d1e..d4033ee 100644 --- a/Interact/Message.cs +++ b/Interact/Message.cs @@ -17,15 +17,15 @@ namespace Interact To = Receiver; Text = text; } - + /*保留 public const UInt32 MaxHeadLength = 4096; //最大包头长度 public const UInt32 MaxMessageLength = 0x6400000; //最大数据长度 */ - public DateTime When { get; } //服务器接收时间 + public DateTime When; //服务器接收时间 public User From; //消息发送者 public User To; //消息接收者 - public string Text; //消息内容 + public string Text; //消息内容 //将消息保存到数据库(保留) protected bool Save() { return false; } @@ -36,7 +36,7 @@ namespace Interact { public BaseHead Head; //包头 public byte[] Data; //包内容 - public Action CallBack; //数据发送完成的回调函数。注意,此时仅保证数据已发送, + public Action CallBack; //数据发送完成的回调函数。注意,此时仅保证数据已发送, //服务器可能并没有处理完成请求 } //允许请求的操作列表。具体定义见设计文档 @@ -51,4 +51,22 @@ namespace Interact 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/Send.cs b/Interact/Send.cs index 483de17..6079300 100644 --- a/Interact/Send.cs +++ b/Interact/Send.cs @@ -14,11 +14,13 @@ namespace Interact // 消息写入线程,向tcp连接写入数据 private static void SendLoop() { + Packet packet = null; NetworkStream stream = tcpClient.GetStream(); try { - foreach (Packet packet in sendQueue.GetConsumingEnumerable()) + foreach (Packet i in sendQueue.GetConsumingEnumerable()) { + packet = i; //为了在代码块外引用i //结束循环 if (StormClient.Status != ClientStatus.Running) break; @@ -37,8 +39,14 @@ namespace Interact stream.Write(lenBuffer, 0, 4); if (length > 0) stream.Write(packet.Data, 0, packet.Data.Length); - //发送完成回调 - packet.CallBack?.Invoke(packet.Head); + //发送成功回调 + ResultHead result = new ResultHead + { + Token = packet.Head.Token, + Operation = packet.Head.Operation, + Error = "" + }; + packet.CallBack?.Invoke(result); } } catch (System.IO.IOException ex) @@ -49,6 +57,28 @@ namespace Interact { 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) diff --git a/Interact/User.cs b/Interact/User.cs index 36980f3..5976cc1 100644 --- a/Interact/User.cs +++ b/Interact/User.cs @@ -10,8 +10,31 @@ namespace Interact //用户 public class User { - public static User Me; //当前用户 - public static Image DefaultPhoto; //默认头像 + //根据用户名匹配用户。如果用户不存在,则返回一个仅用户名有效的实例 + 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; //昵称 diff --git a/StormChat/LogForm.cs b/StormChat/LogForm.cs index abaed82..185df21 100644 --- a/StormChat/LogForm.cs +++ b/StormChat/LogForm.cs @@ -23,6 +23,7 @@ namespace StormChat private void LogForm_Load(object sender, EventArgs e) { StormClient.OnLoginDone += this.LoginDone; + StormClient.OnMessage += this.ShowMessage; } private void btnLogin_Click(object sender, EventArgs e) { @@ -57,5 +58,10 @@ namespace StormChat else f.Invoke(); } + + private void ShowMessage(Interact.Message m) + { + MessageBox.Show(m.Text); + } } }