diff --git a/Interact/Client.cs b/Interact/Client.cs index c2de87a..79a10f0 100644 --- a/Interact/Client.cs +++ b/Interact/Client.cs @@ -1,18 +1,14 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Text; using System.Net.Sockets; -using System.Net; using System.Threading; using System.Collections.Concurrent; -using System.Drawing; using System.IO; namespace Interact { //连接中断 - public delegate void DisconnectHandler(Exception ex); + public delegate void ConnectionBrokenHandler(Exception ex); //通用请求结果处理 public delegate void ResultHandler(ResultHead head); //消息到达事件处理 @@ -21,9 +17,6 @@ namespace Interact public delegate void LoginDoneHandler(ResultHead head, User user); //获取用户列表结果处理 public delegate void GetUserListDoneHandler(ResultHead head, User[] users); - //获取用户头像结果处理 - public delegate void GetUserPhotoDoneHandler(ResultHead head, Image image); - /// /// 与服务器进行数据通信,并向外部提供一系列通信接口 @@ -31,7 +24,7 @@ namespace Interact public static partial class StormClient { //连接中断事件 - public static event DisconnectHandler OnDisconnect; + public static event ConnectionBrokenHandler OnDisconnect; //服务器异常消息处理事件 public static event ResultHandler OnPanic; //服务器强制注销登陆事件 @@ -45,37 +38,27 @@ namespace Interact //登录结果处理事件 public static event LoginDoneHandler OnLoginDone; //获取用户列表结果处理事件 - public static event GetUserListDoneHandler OnGetUserListDone; - //获取用户头像结果处理事件 - public static event GetUserPhotoDoneHandler OnGetUserPhotoDone; + private static event GetUserListDoneHandler OnGetUserListDone; + + //当前连接状态 + public static ClientStatus Status + { + get { lock (statusLocker) { return clientStatus; } } + set { lock (statusLocker) { clientStatus = value; } } + } //发送消息时是否要求服务器返回处理结果 public static bool DoesSendMessageReturn { - get - { - return doesSendMessageReturn != 0; - } - set - { - Interlocked.Exchange(ref StormClient.doesSendMessageReturn, value ? 1 : 0); - } + get { lock (doesSendMessageReturnLocker) { return doesSendMessageReturnResult; } } + set { lock (doesSendMessageReturnLocker) { doesSendMessageReturnResult = value; } } } - - //TCP客户端 - private static TcpClient tcpClient; - //消息发送队列 - private static BlockingCollection sendQueue; - //数据读取线程 - private static Thread readLoopThread; - //数据发送线程 - private static Thread sendLoopThread; - //发送消息时是否要求服务器返回处理结果 - private static volatile int doesSendMessageReturn = 0; + static StormClient() { tcpClient = new TcpClient(); sendQueue = new BlockingCollection(); + Status = ClientStatus.Uninitialized; } /// @@ -87,7 +70,14 @@ namespace Interact { if (StormClient.tcpClient.Connected) return true; - StormClient.tcpClient.Connect(Interact.Properties.Resources.RemoteServerAddr, int.Parse(Interact.Properties.Resources.RemoteServerPort)); + try + { + StormClient.tcpClient.Connect(Interact.Properties.Resources.RemoteServerAddr, int.Parse(Interact.Properties.Resources.RemoteServerPort)); + } + catch (Exception) + { + return false; + } if (!StormClient.tcpClient.Connected) return false; if (sendLoopThread != null) @@ -97,8 +87,8 @@ namespace Interact //启动数据收发线程 sendLoopThread = new Thread(new ThreadStart(SendLoop)); readLoopThread = new Thread(new ThreadStart(ReadLoop)); - //设置为后台线程 sendLoopThread.IsBackground = readLoopThread.IsBackground = true; + StormClient.Status = ClientStatus.Running; sendLoopThread.Start(); readLoopThread.Start(); return true; @@ -116,7 +106,7 @@ namespace Interact JsonLogInfoHead logInfo = new JsonLogInfoHead() { Token = "", - Operation = Operations.Login.ToString(), + Operation = Operations.Login, User = user, Pwd = password }; @@ -128,7 +118,7 @@ namespace Interact }; return Send(packet); } - + /// /// 请求发送一条消息。异步,此方法将请求放入请求队列后返回。 /// @@ -141,7 +131,7 @@ namespace Interact JsonSendMessageHead jsonObj = new JsonSendMessageHead() { Token = "", - Operation = Operations.SendMessage.ToString(), + Operation = Operations.SendMessage, To = to.Name, NeedResult = DoesSendMessageReturn ? "1" : "0" }; @@ -153,7 +143,7 @@ namespace Interact }; return Send(packet); } - + /// /// 请求登出。异步,此方法将请求放入请求队列后返回。 /// @@ -164,7 +154,7 @@ namespace Interact BaseHead jsonObj = new BaseHead() { Token = "", - Operation = Operations.Logout.ToString(), + Operation = Operations.Logout, }; Packet packet = new Packet { @@ -174,7 +164,7 @@ namespace Interact }; return Send(packet); } - + /// /// 请求用户列表。异步,此方法将请求放入请求队列后返回。 /// @@ -185,7 +175,7 @@ namespace Interact BaseHead jsonObj = new BaseHead() { Token = "", - Operation = Operations.GetUserList.ToString(), + Operation = Operations.GetUsers, }; Packet packet = new Packet { @@ -195,7 +185,7 @@ namespace Interact }; return Send(packet); } - + /// /// 请求更新当前登录用户信息。异步,此方法将请求放入请求队列后返回。 /// @@ -203,8 +193,8 @@ namespace Interact /// 成功将请求加入发送队列返回true,否则返回false。 public static bool QueueUpdateUserInfo(UserInfo userInfo, Action callback = null) { - byte[] photoData = null; //头像数据 - //将头像数据转换到字符数组 + byte[] photoData = null; //头像数据 + //将头像数据转换到字符数组 if (userInfo.Photo != null) { MemoryStream memStream = new MemoryStream(); @@ -217,7 +207,7 @@ namespace Interact JsonUserInfoHead jsonObj = new JsonUserInfoHead() { Token = "", - Operation = Operations.UpdateUserInfo.ToString(), + Operation = Operations.UpdateUserInfo, NickName = userInfo.NickName, Password = userInfo.Password, Motto = userInfo.Motto, @@ -231,28 +221,13 @@ namespace Interact }; return Send(packet); } - - /// - /// 请求获取指定用户的头像。异步,此方法将请求放入请求队列后返回。 - /// - /// 要获取头像的用户 - /// 数据发送完毕时回调函数 - /// 成功将请求加入发送队列返回true,否则返回false。 - public static bool QueueGetUserPhoto(User user, Action callback = null) + + //客户端连接状态 + public enum ClientStatus { - JsonGetUserPhotoHead jsonObj = new JsonGetUserPhotoHead() - { - Token = "", - Operation = Operations.GetUserPhoto.ToString(), - User = user.Name - }; - Packet packet = new Packet - { - Head = jsonObj, - Data = null, - CallBack = callback - }; - return Send(packet); + Uninitialized, //未启动 + Running, //运行中 + Stopped //已停止 } } } diff --git a/Interact/Exception.cs b/Interact/Exception.cs new file mode 100644 index 0000000..9e5d777 --- /dev/null +++ b/Interact/Exception.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Interact +{ + class ConnectionBrokenException : Exception + { + public ConnectionBrokenException(string message) : base(message) { } + } +} diff --git a/Interact/Fields.cs b/Interact/Fields.cs new file mode 100644 index 0000000..9f95fd0 --- /dev/null +++ b/Interact/Fields.cs @@ -0,0 +1,37 @@ +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/Handle.cs b/Interact/Handle.cs index 7b29bcc..c739383 100644 --- a/Interact/Handle.cs +++ b/Interact/Handle.cs @@ -4,35 +4,50 @@ using System.Linq; using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Linq; + +//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[] headData, byte[] data) { string head = Encoding.UTF8.GetString(headData); JObject jsonObj = (JObject)JsonConvert.DeserializeObject(head); - switch ((Operations)Enum.Parse(typeof(Operations), jsonObj[AttrNames.Operation].ToString())) + switch (jsonObj[AttrNames.Operation].ToString()) { case Operations.Login: HandleLoginResult(GetResultHead(jsonObj), data); break; case Operations.Panic: + HandlePanic(GetResultHead(jsonObj)); + break; case Operations.TransMessage: + case Operations.SendMessage: - case Operations.Ping: case Operations.Logout: case Operations.Offline: - case Operations.GetUserList: case Operations.UpdateUserInfo: - case Operations.GetUserPhoto: default: break; } } - //提取基本结果包头结构 private static ResultHead GetResultHead(JObject head) { @@ -45,6 +60,13 @@ namespace Interact return resultHead; } + //处理服务器异常断开连接消息 + private static void HandlePanic(ResultHead head) + { + ConnectionBrokenException ex = new ConnectionBrokenException(head.Error); + HandleConnectionBroken(ex); + } + //处理登录反馈消息 private static void HandleLoginResult(ResultHead head, byte[] data) { @@ -64,7 +86,7 @@ namespace Interact }; OnLoginDone?.Invoke(head, user); } - + #endregion } diff --git a/Interact/Interact.csproj b/Interact/Interact.csproj index 331e988..bdb6e97 100644 --- a/Interact/Interact.csproj +++ b/Interact/Interact.csproj @@ -45,6 +45,8 @@ + + diff --git a/Interact/JsonObject.cs b/Interact/JsonObject.cs index c8602a5..50af897 100644 --- a/Interact/JsonObject.cs +++ b/Interact/JsonObject.cs @@ -20,6 +20,11 @@ namespace Interact { //错误文本,为空则执行成功 public string Error; + } + //获取单一用户信息结果包头 + public class GetUserInfoResultHead : ResultHead + { + } #endregion @@ -52,5 +57,6 @@ namespace Interact { public string User; //要获取头像的用户名 } + #endregion } diff --git a/Interact/Message.cs b/Interact/Message.cs index 1ea5db5..5afcecc 100644 --- a/Interact/Message.cs +++ b/Interact/Message.cs @@ -30,17 +30,15 @@ namespace Interact //服务器可能并没有处理完成请求 } //允许请求的操作列表。具体定义见设计文档 - public enum Operations + public static class Operations { - Panic, - TransMessage, - SendMessage, - Ping, - Login, - Logout, - Offline, - GetUserList, - UpdateUserInfo, - GetUserPhoto + 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"; } } diff --git a/Interact/Read.cs b/Interact/Read.cs index 019416e..79c2cf8 100644 --- a/Interact/Read.cs +++ b/Interact/Read.cs @@ -5,9 +5,9 @@ using System.Text; using System.Net; using System.Net.Sockets; +//StormClient读取数据相关内容 namespace Interact { - //读取数据相关内容 public partial class StormClient { // 数据读取线程,从tcp连接读取数据 @@ -16,7 +16,7 @@ namespace Interact NetworkStream stream = StormClient.tcpClient.GetStream(); try { - while (tcpClient.Connected) + while (StormClient.Status == ClientStatus.Running) { byte[] head = ReadDataWithLength(stream); byte[] data = ReadDataWithLength(stream); @@ -25,13 +25,11 @@ namespace Interact } catch (System.IO.IOException ex) { - tcpClient.Close(); - OnDisconnect(ex); + HandleConnectionBroken(ex); } catch (ObjectDisposedException ex) { - tcpClient.Close(); - OnDisconnect(ex); + HandleConnectionBroken(ex); } } // 从网络流读取数据 diff --git a/Interact/Send.cs b/Interact/Send.cs index 7515499..483de17 100644 --- a/Interact/Send.cs +++ b/Interact/Send.cs @@ -6,9 +6,9 @@ using Newtonsoft.Json; using System.Net; using System.Net.Sockets; +//StormClient发送数据相关内容 namespace Interact { - //发送数据相关内容 public partial class StormClient { // 消息写入线程,向tcp连接写入数据 @@ -20,7 +20,7 @@ namespace Interact foreach (Packet packet in sendQueue.GetConsumingEnumerable()) { //结束循环 - if (!tcpClient.Connected) + if (StormClient.Status != ClientStatus.Running) break; //向tcp连接写数据 byte[] lenBuffer; //数据长度缓存 @@ -43,13 +43,11 @@ namespace Interact } catch (System.IO.IOException ex) { - tcpClient.Close(); - OnDisconnect?.Invoke(ex); + HandleConnectionBroken(ex); } catch (ObjectDisposedException ex) { - tcpClient.Close(); - OnDisconnect?.Invoke(ex); + HandleConnectionBroken(ex); } } //发送数据 diff --git a/Interact/User.cs b/Interact/User.cs index 053ad61..0a94d81 100644 --- a/Interact/User.cs +++ b/Interact/User.cs @@ -15,7 +15,8 @@ namespace Interact public string Name; //用户名 public string NickName; //昵称 public string Motto; //个性签名 - public UserGroup Group; //用户组 + public UserGroup Group; //用户组 + public Image Photo; //头像 } //更新用户信息时新的用户信息。如果项值为null则不更改相应的信息