完善发送消息和接收消息接口

This commit is contained in:
Mattuy
2018-12-18 23:35:11 +08:00
parent 308b9494d7
commit 4d28316f67
8 changed files with 170 additions and 57 deletions
+8 -1
View File
@@ -15,11 +15,18 @@ namespace Interact
//处理结果信息 //处理结果信息
public const string Error = "Error"; public const string Error = "Error";
//用户信息 //获取用户信息
public const string User = "User"; public const string User = "User";
public const string NickName = "NickName"; public const string NickName = "NickName";
public const string Motto = "Motto"; public const string Motto = "Motto";
public const string UGroup = "UGroup"; public const string UGroup = "UGroup";
public const string Photo = "Photo"; 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";
} }
} }
+10 -10
View File
@@ -59,6 +59,7 @@ namespace Interact
{ {
tcpClient = new TcpClient(); tcpClient = new TcpClient();
sendQueue = new BlockingCollection<Packet>(); sendQueue = new BlockingCollection<Packet>();
workingDictionary = new Dictionary<string, object>();
Status = ClientStatus.Uninitialized; Status = ClientStatus.Uninitialized;
} }
@@ -79,8 +80,7 @@ namespace Interact
{ {
return false; return false;
} }
workingDictionary?.Clear(); //清空临时记录
workingDictionary.Clear(); //清空临时记录
if (sendLoopThread != null) if (sendLoopThread != null)
sendLoopThread.Abort(); sendLoopThread.Abort();
if (readLoopThread != null) if (readLoopThread != null)
@@ -102,11 +102,11 @@ namespace Interact
/// <param name="password">密码</param> /// <param name="password">密码</param>
/// <param name="callback">数据发送完毕回调函数。注意,结果处理回调请设置OnLoginDone事件</param> /// <param name="callback">数据发送完毕回调函数。注意,结果处理回调请设置OnLoginDone事件</param>
/// <returns>成功将请求加入发送队列返回true,否则返回false。</returns> /// <returns>成功将请求加入发送队列返回true,否则返回false。</returns>
public static bool QueueLogin(string user, string password, Action<BaseHead> callback = null) public static bool QueueLogin(string user, string password, Action<ResultHead> callback = null)
{ {
JsonLogInfoHead logInfo = new JsonLogInfoHead() JsonLogInfoHead logInfo = new JsonLogInfoHead()
{ {
Token = "", Token = Guid.NewGuid().ToString(),
Operation = Operations.Login, Operation = Operations.Login,
User = user, User = user,
Pwd = password Pwd = password
@@ -127,19 +127,19 @@ namespace Interact
/// <param name="to">消息接收者</param> /// <param name="to">消息接收者</param>
/// <param name="callback">数据发送完毕时回调函数</param> /// <param name="callback">数据发送完毕时回调函数</param>
/// <returns>成功将请求加入发送队列返回true,否则返回false。</returns> /// <returns>成功将请求加入发送队列返回true,否则返回false。</returns>
public static bool QueueSendMessage(string text, User to, Action<BaseHead> callback = null) public static bool QueueSendMessage(Message message, Action<ResultHead> callback = null)
{ {
JsonSendMessageHead jsonObj = new JsonSendMessageHead() JsonSendMessageHead jsonObj = new JsonSendMessageHead()
{ {
Token = "", Token = "",
Operation = Operations.SendMessage, Operation = Operations.SendMessage,
To = to.Name, To = message.To.Name,
NeedResult = DoesSendMessageReturn ? "1" : "0" NeedResult = DoesSendMessageReturn ? "1" : "0"
}; };
Packet packet = new Packet Packet packet = new Packet
{ {
Head = jsonObj, Head = jsonObj,
Data = Encoding.UTF8.GetBytes(text), Data = Encoding.UTF8.GetBytes(message.Text),
CallBack = callback CallBack = callback
}; };
return Send(packet); return Send(packet);
@@ -150,7 +150,7 @@ namespace Interact
/// </summary> /// </summary>
/// <param name="callback">数据发送完毕时回调函数</param> /// <param name="callback">数据发送完毕时回调函数</param>
/// <returns>成功将请求加入发送队列返回true,否则返回false。</returns> /// <returns>成功将请求加入发送队列返回true,否则返回false。</returns>
public static bool QueueLogout(Action<BaseHead> callback = null) public static bool QueueLogout(Action<ResultHead> callback = null)
{ {
BaseHead jsonObj = new BaseHead() BaseHead jsonObj = new BaseHead()
{ {
@@ -171,7 +171,7 @@ namespace Interact
/// </summary> /// </summary>
/// <param name="callback">数据发送完毕时回调函数</param> /// <param name="callback">数据发送完毕时回调函数</param>
/// <returns>成功将请求加入发送队列返回true,否则返回false。</returns> /// <returns>成功将请求加入发送队列返回true,否则返回false。</returns>
public static bool QueueGetUserList(Action<BaseHead> callback = null) public static bool QueueGetUserList(Action<ResultHead> callback = null)
{ {
BaseHead jsonObj = new BaseHead() BaseHead jsonObj = new BaseHead()
{ {
@@ -192,7 +192,7 @@ namespace Interact
/// </summary> /// </summary>
/// <param name="callback">数据发送完毕时回调函数</param> /// <param name="callback">数据发送完毕时回调函数</param>
/// <returns>成功将请求加入发送队列返回true,否则返回false。</returns> /// <returns>成功将请求加入发送队列返回true,否则返回false。</returns>
public static bool QueueUpdateUserInfo(UserInfo userInfo, Action<BaseHead> callback = null) public static bool QueueUpdateUserInfo(UserInfo userInfo, Action<ResultHead> callback = null)
{ {
byte[] photoData = null; //头像数据 byte[] photoData = null; //头像数据
//将头像数据转换到字符数组 //将头像数据转换到字符数组
+64 -11
View File
@@ -28,29 +28,29 @@ namespace Interact
#region #region
//根据包头信息处理数据 //根据包头信息处理数据
private static void HandleAll(byte[] headData, byte[] data) private static void HandleAll(byte[] head, byte[] data)
{ {
string head = Encoding.UTF8.GetString(headData); string headStr = Encoding.UTF8.GetString(head);
JObject jsonObj = (JObject)JsonConvert.DeserializeObject(head); JObject jsonObj = (JObject)JsonConvert.DeserializeObject(headStr);
switch (jsonObj[AttrNames.Operation].ToString()) switch (jsonObj[AttrNames.Operation].ToString())
{ {
case Operations.Login:
HandleLoginResult(jsonObj, data);
break;
case Operations.Panic: case Operations.Panic:
HandlePanic(GetResultHead(jsonObj)); HandlePanic(GetResultHead(jsonObj));
break; break;
case Operations.Login:
HandleLoginResult(jsonObj, data);
break;
case Operations.TransMessage: case Operations.TransMessage:
HandleMessage(jsonObj, data);
break; break;
case Operations.SendMessage: case Operations.SendMessage:
break; break;
case Operations.Logout:
break;
case Operations.Offline: case Operations.Offline:
break; break;
case Operations.UpdateUserInfo: case Operations.UpdateUserInfo:
break; break;
case Operations.GetUsers: case Operations.GetUsers:
HandleGetUsersPack(jsonObj, data);
break; break;
default: default:
break; break;
@@ -63,8 +63,9 @@ namespace Interact
{ {
Token = head[AttrNames.Token].ToString(), Token = head[AttrNames.Token].ToString(),
Operation = head[AttrNames.Operation].ToString(), Operation = head[AttrNames.Operation].ToString(),
Error = head[AttrNames.Error].ToString()
}; };
if (head[AttrNames.Error] != null)
resultHead.Error = head[AttrNames.Error].ToString();
return resultHead; return resultHead;
} }
@@ -102,15 +103,67 @@ namespace Interact
OnLoginDone?.Invoke(resultHead, user); 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) private static void HandleGetUsersPack(JObject head, byte[] data)
{ {
ResultHead result = GetResultHead(head);
if (head[AttrNames.Error].ToString() != string.Empty) if (head[AttrNames.Error].ToString() != string.Empty)
{ {
OnGetUserListDone?.Invoke(GetResultHead(head), null); OnGetUserListDone?.Invoke(result, new User[0]);
return; 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 #endregion
+2 -26
View File
@@ -3,32 +3,10 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
//定义Json序列化与反序列化时使用的数据结构,仅内部使用
namespace Interact 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 internal class JsonLogInfoHead : BaseHead
{ {
@@ -57,6 +35,4 @@ namespace Interact
{ {
public string User; //要获取头像的用户名 public string User; //要获取头像的用户名
} }
#endregion
} }
+20 -2
View File
@@ -22,7 +22,7 @@ namespace Interact
public const UInt32 MaxHeadLength = 4096; //最大包头长度 public const UInt32 MaxHeadLength = 4096; //最大包头长度
public const UInt32 MaxMessageLength = 0x6400000; //最大数据长度 public const UInt32 MaxMessageLength = 0x6400000; //最大数据长度
*/ */
public DateTime When { get; } //服务器接收时间 public DateTime When; //服务器接收时间
public User From; //消息发送者 public User From; //消息发送者
public User To; //消息接收者 public User To; //消息接收者
public string Text; //消息内容 public string Text; //消息内容
@@ -36,7 +36,7 @@ namespace Interact
{ {
public BaseHead Head; //包头 public BaseHead Head; //包头
public byte[] Data; //包内容 public byte[] Data; //包内容
public Action<BaseHead> CallBack; //数据发送完成的回调函数。注意,此时仅保证数据已发送, public Action<ResultHead> CallBack; //数据发送完成的回调函数。注意,此时仅保证数据已发送,
//服务器可能并没有处理完成请求 //服务器可能并没有处理完成请求
} }
//允许请求的操作列表。具体定义见设计文档 //允许请求的操作列表。具体定义见设计文档
@@ -51,4 +51,22 @@ namespace Interact
public const string UpdateUserInfo = "UpdateUserInfo"; public const string UpdateUserInfo = "UpdateUserInfo";
public const string GetUsers = "GetUsers"; 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
} }
+33 -3
View File
@@ -14,11 +14,13 @@ namespace Interact
// 消息写入线程,向tcp连接写入数据 // 消息写入线程,向tcp连接写入数据
private static void SendLoop() private static void SendLoop()
{ {
Packet packet = null;
NetworkStream stream = tcpClient.GetStream(); NetworkStream stream = tcpClient.GetStream();
try try
{ {
foreach (Packet packet in sendQueue.GetConsumingEnumerable()) foreach (Packet i in sendQueue.GetConsumingEnumerable())
{ {
packet = i; //为了在代码块外引用i
//结束循环 //结束循环
if (StormClient.Status != ClientStatus.Running) if (StormClient.Status != ClientStatus.Running)
break; break;
@@ -37,8 +39,14 @@ namespace Interact
stream.Write(lenBuffer, 0, 4); stream.Write(lenBuffer, 0, 4);
if (length > 0) if (length > 0)
stream.Write(packet.Data, 0, packet.Data.Length); 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) catch (System.IO.IOException ex)
@@ -49,6 +57,28 @@ namespace Interact
{ {
HandleConnectionBroken(ex); HandleConnectionBroken(ex);
} }
finally
{
List<Packet> packets = new List<Packet>(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) private static bool Send(Packet packet)
+23
View File
@@ -10,6 +10,29 @@ namespace Interact
//用户 //用户
public class User 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 User Me; //当前用户
public static Image DefaultPhoto; //默认头像 public static Image DefaultPhoto; //默认头像
+6
View File
@@ -23,6 +23,7 @@ namespace StormChat
private void LogForm_Load(object sender, EventArgs e) private void LogForm_Load(object sender, EventArgs e)
{ {
StormClient.OnLoginDone += this.LoginDone; StormClient.OnLoginDone += this.LoginDone;
StormClient.OnMessage += this.ShowMessage;
} }
private void btnLogin_Click(object sender, EventArgs e) private void btnLogin_Click(object sender, EventArgs e)
{ {
@@ -57,5 +58,10 @@ namespace StormChat
else else
f.Invoke(); f.Invoke();
} }
private void ShowMessage(Interact.Message m)
{
MessageBox.Show(m.Text);
}
} }
} }