Add a README
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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<Packet> sendQueue;
|
||||
//数据读取线程
|
||||
private static Thread readLoopThread;
|
||||
//数据发送线程
|
||||
private static Thread sendLoopThread;
|
||||
//某些请求需要持续的等待服务器返回数据包,然后将所有数据包处理后提供给用户,
|
||||
// 此字段用于暂存这些未接收完全的数据包。它的key是对应请求的Token字串。
|
||||
private static Dictionary<string, object> 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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<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)
|
||||
{
|
||||
if (sendLoopThread == null || !sendLoopThread.IsAlive)
|
||||
return false;
|
||||
//加入数据发送队列等待
|
||||
sendQueue.Add(packet);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
/// <summary>
|
||||
/// 与服务器进行数据通信,并向外部提供一系列通信接口
|
||||
/// </summary>
|
||||
public static partial class StormClient
|
||||
{
|
||||
/// <summary>
|
||||
/// 连接中断事件。注意,此事件并不保证连接中断时总是触发,例如主动关闭连接、登出操作
|
||||
/// </summary>
|
||||
public static event ConnectionBrokenHandler OnDisconnect;
|
||||
/// <summary>
|
||||
/// 服务器异常消息处理事件
|
||||
/// </summary>
|
||||
public static event ResultHandler OnPanic;
|
||||
/// <summary>
|
||||
/// 服务器强制注销登陆事件
|
||||
/// </summary>
|
||||
public static event ResultHandler OnOffline;
|
||||
/// <summary>
|
||||
/// 消息到达事件
|
||||
/// </summary>
|
||||
public static event MessageHandler OnMessage;
|
||||
/// <summary>
|
||||
/// 发送消息处理完成反馈事件(仅在DoesSendMessageReturn为true时有效)
|
||||
/// </summary>
|
||||
public static event ResultHandler OnSendMessageDone;
|
||||
/// <summary>
|
||||
/// 更新用户信息结果处理事件
|
||||
/// </summary>
|
||||
public static event ResultHandler OnUpdateUserInfoDone;
|
||||
/// <summary>
|
||||
/// 登录结果处理事件
|
||||
/// </summary>
|
||||
public static event LoginDoneHandler OnLoginDone;
|
||||
/// <summary>
|
||||
/// 获取用户列表结果处理事件
|
||||
/// </summary>
|
||||
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<Packet>();
|
||||
workingDictionary = new Dictionary<string, object>();
|
||||
SetStatus(ClientStatus.Uninitialized);
|
||||
}
|
||||
|
||||
//设置当前连接状态
|
||||
internal static void SetStatus(ClientStatus value)
|
||||
{
|
||||
lock (statusLocker) { clientStatus = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化客户端,连接服务器。如果成功则启动数据接收和发送线程。
|
||||
/// 注意,当网络不稳定时此方法可能会阻塞。
|
||||
/// </summary>
|
||||
/// <returns>如果已连接到服务器返回true,否则返回false。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 登录。此操作为异步操作,服务器返回结果后产生OnLoginDone事件。
|
||||
/// </summary>
|
||||
/// <param name="user">用户名</param>
|
||||
/// <param name="password">密码</param>
|
||||
/// <param name="callback">数据发送完毕回调函数。注意,结果处理回调请设置OnLoginDone事件</param>
|
||||
/// <returns>成功将请求加入发送队列返回true,否则返回false。</returns>
|
||||
public static bool QueueLogin(string user, string password, Action<ResultHead> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求发送一条消息。异步,此方法将请求放入请求队列后返回。
|
||||
/// </summary>
|
||||
/// <param name="text">要发送的消息文本</param>
|
||||
/// <param name="to">消息接收者</param>
|
||||
/// <param name="callback">数据发送完毕时回调函数。</param>
|
||||
/// <returns>成功将请求加入发送队列返回true,否则返回false。</returns>
|
||||
public static bool QueueSendMessage(Message message, Action<ResultHead> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求登出。异步,此方法将请求放入请求队列后返回。
|
||||
/// </summary>
|
||||
/// <param name="callback">数据发送完毕时回调函数。</param>
|
||||
/// <returns>成功将请求加入发送队列返回true,否则返回false。</returns>
|
||||
public static bool QueueLogout(Action<ResultHead> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求用户列表。异步,此方法将请求放入请求队列后返回。
|
||||
/// </summary>
|
||||
/// <param name="callback">数据发送完毕时回调函数。</param>
|
||||
/// <returns>成功将请求加入发送队列返回true,否则返回false。</returns>
|
||||
public static bool QueueGetUserList(Action<ResultHead> callback = null)
|
||||
{
|
||||
BaseHead jsonObj = new BaseHead()
|
||||
{
|
||||
Token = "",
|
||||
Operation = Operations.GetUsers,
|
||||
};
|
||||
Packet packet = new Packet
|
||||
{
|
||||
Head = jsonObj,
|
||||
Data = null,
|
||||
CallBack = callback
|
||||
};
|
||||
return Send(packet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求更新当前登录用户信息。异步,此方法将请求放入请求队列后返回。
|
||||
/// </summary>
|
||||
/// <param name="userInfo">新的用户信息,如果某项值为null,则不改变该项。</param>
|
||||
/// <param name="callback">数据发送完毕时回调函数。</param>
|
||||
/// <returns>成功将请求加入发送队列返回true,否则返回false。</returns>
|
||||
public static bool QueueUpdateUserInfo(UserInfo userInfo, Action<ResultHead> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 客户端连接状态
|
||||
/// </summary>
|
||||
public enum ClientStatus
|
||||
{
|
||||
Uninitialized, //未启动
|
||||
Running, //运行中
|
||||
Stopped //已停止
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) { }
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{CEF054AF-95B7-4B88-934E-02705FEEA554}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Interact</RootNamespace>
|
||||
<AssemblyName>Interact</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>.\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Client.cs" />
|
||||
<Compile Include="Exception.cs" />
|
||||
<Compile Include="Client-Fields.cs" />
|
||||
<Compile Include="Client-Handle.cs" />
|
||||
<Compile Include="jsonObject.cs" />
|
||||
<Compile Include="Message.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Client-Read.cs" />
|
||||
<Compile Include="Client-Send.cs" />
|
||||
<Compile Include="AttrNames.cs" />
|
||||
<Compile Include="User.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -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; //新头像大小
|
||||
}
|
||||
}
|
||||
@@ -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<ResultHead> 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
|
||||
}
|
||||
@@ -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")]
|
||||
@@ -1,81 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 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.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Interact.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// 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() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to mattuy.top.
|
||||
/// </summary>
|
||||
internal static string RemoteServerAddr {
|
||||
get {
|
||||
return ResourceManager.GetString("RemoteServerAddr", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to 3727.
|
||||
/// </summary>
|
||||
internal static string RemoteServerPort {
|
||||
get {
|
||||
return ResourceManager.GetString("RemoteServerPort", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="RemoteServerAddr" xml:space="preserve">
|
||||
<value>mattuy.top</value>
|
||||
</data>
|
||||
<data name="RemoteServerPort" xml:space="preserve">
|
||||
<value>3727</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -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 //群组
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
# StormChat
|
||||
|
||||
## 简介
|
||||
  前段时间心血来潮想学习最近的明星编程语言Golang。于是想做个聊天小程序实践一下。程序基于TCP协议通信,更详细的设计见<a href="#design">设计思路</a>
|
||||
|
||||
  用Golang实现了服务端程序,同时代码抄抄改改做了个Golang客户端,又由于输出问题,用C++写了个Windows控制台的简陋的[输出控制库](https://github.com/mattuylee/conctrl),于是客户端在Windows上能看了(不过朋友说很丑>_<)。
|
||||
|
||||
  在朋友([@Billows](https://github.com/KaniuBillows))的鼓励下,我们决定用C#写一个客户端程序,正好他在学习WPF编程,于是界面采用了WPF编写。我负责后台数据交互,他负责前台界面逻辑。
|
||||
|
||||
  **C#客户端还使用到了JSON格式化库<a href="https://github.com/JamesNK/Newtonsoft.Json">Newtonsoft.Json</a>,特此说明。**
|
||||
<a name="design"></a>
|
||||
|
||||
## 设计思路
|
||||
### 通信流程
|
||||
1. 发送方(客户端或者服务器)发送数据包;
|
||||
2. 接收方(服务器或者客户端)处理数据;
|
||||
3. 如果需要反馈,接收方发送反馈数据包;
|
||||
4. 发送方处理反馈数据(如果有)。
|
||||
|
||||
### 通信数据包结构
|
||||
数据通信基于TCP协议,每次通信的数据包应包含【包头域+数据域】。包头域总是JSON格式、UTF-8编码的字符串,数据域由包头决定,可以为空。通信数据包结构如图。
|
||||
|
||||
<img src="design/message.png" alt="数据包结构图"/>
|
||||
|
||||
HEAD(包头域)至少包含两个参数:
|
||||
* Toekn
|
||||
* Operation
|
||||
|
||||
Token参数是一次通信的标识文本,由请求方随机生成,处理方返回数据包的Token参数应和请求方的Token参数一致(如果有返回数据)。
|
||||
|
||||
Operation参数指定本次通信的请求。它决定了数据包的其他数据。Operation具体行为定义见<a href="design/operations.svg">这里</a>。
|
||||
|
||||
本来做了设计图表,不过第一次用starUML,画了一半才发现完全是鬼画桃符,没有掌握正确的作图方式,也没什么热情重新画了。因此这里不展示完整的设计文件了(本来也不完整),上面的链接是截取的关键部分(才知道starUML可以导出html文档)。
|
||||
|
||||
至于原文件...留着权当做个纪念,当作自己学习的见证吧(我也很无奈啊(* ̄︿ ̄))。实在是不嫌弃不怕被误导的话,也可以看看。文件是starUML3的设计文件(.mdj),需要<a href="http://staruml.io/">starUML</a>软件打开(付费的,不过可以免费使用)。这是<a href="design/stormchat.mdj" download="stormchat.mdj">设计文件</a>。
|
||||
|
||||
哦,还有服务器端的数据库结构,见文件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,这个坑懒得填了。
|
||||
* 服务器端的日志文件不会自动清除(反正也没什么日志要写)。
|
||||
* 其他的,想到再补充。
|
||||
|
||||
|
||||
## 目录结构(主要部分)
|
||||
<pre>
|
||||
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 //数据表现和用户交互模块*
|
||||
</pre>
|
||||
|
||||
## 使用截图
|
||||
### Golang客户端
|
||||
<img src="pic/client-login.png"/>
|
||||
<img src="pic/client-chat.png"/>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
|
||||
</startup>
|
||||
</configuration>
|
||||
@@ -1,9 +0,0 @@
|
||||
<Application x:Class="StormChatWPF.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:StormChatWPF"
|
||||
StartupUri="LogWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// App.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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<User> ContactsList;//联系人集合
|
||||
public static Chat chat=new Chat();
|
||||
private Chat()
|
||||
{
|
||||
StormClient.OnLoginDone += OnHaveLogin;
|
||||
StormClient.OnGetUserListDone += OnGetContactsList;
|
||||
StormClient.OnMessage +=OnMessage;
|
||||
}
|
||||
internal static User CurrentContact { get; set; }//设置当前联系人对象
|
||||
/// <summary>
|
||||
/// 获取联系人列表(完成后打开主窗体)
|
||||
/// </summary>
|
||||
private void OnGetContactsList(ResultHead head, User[] users)
|
||||
{
|
||||
if (head.Error != "")
|
||||
{
|
||||
MessageBox.Show("无法获取联系人列表");
|
||||
return;
|
||||
}
|
||||
App.Current.Dispatcher.Invoke(
|
||||
(Action)delegate ()
|
||||
{
|
||||
ContactsList = new List<User>(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<ResultHead> 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("连接服务器失败!");
|
||||
}
|
||||
}//登录
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<Window x:Class="StormChatWPF.LogWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:StormChatWPF"
|
||||
mc:Ignorable="d"
|
||||
Title="WelCome" Height="450" Width="800"
|
||||
Icon="UI/Resources/闪电.png"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<Grid Height="418" VerticalAlignment="Stretch" Margin="2,1,1,1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="200*"/>
|
||||
<ColumnDefinition Width="200*"/>
|
||||
<ColumnDefinition Width="200*"/>
|
||||
<ColumnDefinition Width="200*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="150*"/>
|
||||
<RowDefinition Height="150*"/>
|
||||
<RowDefinition Height="150*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Image x:Name="LogBackground" Grid.ColumnSpan="4" Height="268" Margin="10,10,10,0" Grid.RowSpan="2" VerticalAlignment="Top" Source="pack://application:,,,/UI/Resources/登录界面底.png"/>
|
||||
<Button x:Name="Login_button" Content="LogIn" Click="Login_button_Click" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="2" Margin="160.6,69.6,160.6,44.6" Width="70" Height="25" HorizontalAlignment="Center"/>
|
||||
<TextBox x:Name="AccountBox" TextWrapping="Wrap" Text="201701110203" Grid.Row="2" TextAlignment="Left" Margin="137.6,4.6,137.6,112.6" Grid.Column="1" Grid.ColumnSpan="2" Height="22" Width="122" HorizontalAlignment="Center" BorderThickness="0,0,0,1" VerticalAlignment="Top" />
|
||||
<PasswordBox x:Name="passwordBox" Password="1233211234567" Margin="135,35,135,0" Grid.Column="1" Grid.Row="2" VerticalAlignment="Top" Grid.ColumnSpan="2" Height="22" Width="122" HorizontalAlignment="Center" BorderThickness="0,0,0,1" HorizontalContentAlignment="Left"/>
|
||||
<Label x:Name="Account" Content="Account:" Grid.Column="1" Margin="0,138.8,70.2,0" Grid.Row="1" VerticalAlignment="Top" RenderTransformOrigin="0.415,0.379" Height="24" FontFamily="Comic Sans MS" FontStyle="Italic" Grid.RowSpan="2" HorizontalContentAlignment="Center" HorizontalAlignment="Right" Width="60"/>
|
||||
<Label x:Name="Password" Content="Password:" Grid.Column="1" Margin="0,31.6,65.2,0" Grid.Row="2" VerticalAlignment="Top" FontFamily="Comic Sans MS" FontStyle="Italic" Height="24" HorizontalContentAlignment="Center" HorizontalAlignment="Right" Width="70"/>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -1,37 +0,0 @@
|
||||
using Interact;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace StormChatWPF
|
||||
{
|
||||
/// <summary>
|
||||
/// MainWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class LogWindow : Window
|
||||
{
|
||||
public static LogWindow Instence;
|
||||
public LogWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
Instence = this;
|
||||
}
|
||||
private void Login_button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Chat.Log(AccountBox.Text,passwordBox.Password);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<Window x:Class="StormChatWPF.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:StormChatWPF"
|
||||
mc:Ignorable="d"
|
||||
Title="StormChat" Height="450" Width="800"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="60*"/>
|
||||
<RowDefinition Height="300*"/>
|
||||
<RowDefinition Height="90*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="200*"/>
|
||||
<ColumnDefinition Width="200*"/>
|
||||
<ColumnDefinition Width="200*"/>
|
||||
<ColumnDefinition Width="200*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ListView x:Name="UsersList" Margin="0,0,0,0" IsEnabled="True" SelectionChanged="UsersList_SelectionChanged" Grid.Row="1" Grid.RowSpan="2"/>
|
||||
<TextBox x:Name="InputBox" Margin="0,0,0,0" TextWrapping="Wrap" Text="" Grid.Row="2" Grid.ColumnSpan="3" Grid.Column="1" />
|
||||
<Button Content="Send" HorizontalAlignment="Right" Margin="0,0,0,0" VerticalAlignment="Bottom" Width="75" Click="Button_Click" Height="20" Grid.Row="2" Grid.Column="3"/>
|
||||
<ScrollViewer Grid.ColumnSpan="3" Grid.Column="1" Margin="0,0,0,0" Grid.Row="1" >
|
||||
<ScrollViewer.Content>
|
||||
<StackPanel x:Name="OutBox" Margin="0,0,0,0" Grid.ColumnSpan="3" Grid.Column="1" Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Hidden"/>
|
||||
</ScrollViewer.Content>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -1,83 +0,0 @@
|
||||
using Interact;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace StormChatWPF
|
||||
{
|
||||
/// <summary>
|
||||
/// UserWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public static MainWindow Instence;
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
LoadContactsList();
|
||||
Instence = this;
|
||||
}
|
||||
private void LoadContactsList()
|
||||
{
|
||||
if (Chat.ContactsList.Any())
|
||||
{
|
||||
foreach (var user in Chat.ContactsList)
|
||||
{
|
||||
UsersList.Items.Add(user.NickName);
|
||||
}
|
||||
}
|
||||
}//向联系人列表listview中添加元素
|
||||
|
||||
private void UsersList_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (UsersList.SelectedItem != null)
|
||||
{
|
||||
Chat.CurrentContact = Chat.ContactsList[UsersList.SelectedIndex];
|
||||
}
|
||||
}
|
||||
|
||||
private void Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Chat.CurrentContact == null)
|
||||
{
|
||||
MessageBox.Show("请选择联系人!");
|
||||
return;
|
||||
}
|
||||
if (InputBox.Text!="")
|
||||
{
|
||||
Chat.chat.SendMessage(InputBox.Text, Chat.CurrentContact);
|
||||
InputBox.Text = "";
|
||||
}
|
||||
}
|
||||
|
||||
internal void ShowMessage(Message message)
|
||||
{
|
||||
if (message.To == User.Me)
|
||||
{
|
||||
App.Current.Dispatcher.Invoke(
|
||||
delegate ()
|
||||
{
|
||||
OutBox.Children.Add(new ChatBubble(message,HorizontalAlignment.Left));
|
||||
});
|
||||
}//接受到的的消息
|
||||
else
|
||||
{
|
||||
App.Current.Dispatcher.Invoke(
|
||||
delegate ()
|
||||
{
|
||||
OutBox.Children.Add(new ChatBubble(message,HorizontalAlignment.Right));
|
||||
});
|
||||
}//发送的的消息
|
||||
}//将消息展现于UI界面
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
// 有关程序集的一般信息由以下
|
||||
// 控制。更改这些特性值可修改
|
||||
// 与程序集关联的信息。
|
||||
[assembly: AssemblyTitle("StormChatWPF")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("StormChatWPF")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// 将 ComVisible 设置为 false 会使此程序集中的类型
|
||||
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
|
||||
//请将此类型的 ComVisible 特性设置为 true。
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
//若要开始生成可本地化的应用程序,请设置
|
||||
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
|
||||
//例如,如果您在源文件中使用的是美国英语,
|
||||
//使用的是美国英语,请将 <UICulture> 设置为 en-US。 然后取消
|
||||
//对以下 NeutralResourceLanguage 特性的注释。 更新
|
||||
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
|
||||
|
||||
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
|
||||
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //主题特定资源词典所处位置
|
||||
//(未在页面中找到资源时使用,
|
||||
//或应用程序资源字典中找到时使用)
|
||||
ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
|
||||
//(未在页面中找到资源时使用,
|
||||
//、应用程序或任何主题专用资源字典中找到时使用)
|
||||
)]
|
||||
|
||||
|
||||
// 程序集的版本信息由下列四个值组成:
|
||||
//
|
||||
// 主版本
|
||||
// 次版本
|
||||
// 生成号
|
||||
// 修订号
|
||||
//
|
||||
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
|
||||
// 方法是按如下所示使用“*”: :
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -1,120 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -1,26 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 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.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace StormChatWPF.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
||||
|
Before Width: | Height: | Size: 71 KiB |
@@ -1,64 +0,0 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace StormChatWPF
|
||||
{
|
||||
public class ScrollViewerExtensions
|
||||
{
|
||||
public static readonly DependencyProperty AlwaysScrollToEndProperty = DependencyProperty.RegisterAttached("AlwaysScrollToEnd", typeof(bool), typeof(ScrollViewerExtensions), new PropertyMetadata(false, AlwaysScrollToEndChanged));
|
||||
private static bool _autoScroll;
|
||||
|
||||
|
||||
private static void AlwaysScrollToEndChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
ScrollViewer scroll = sender as ScrollViewer;
|
||||
if (scroll != null)
|
||||
{
|
||||
bool alwaysScrollToEnd = (e.NewValue != null) && (bool)e.NewValue;
|
||||
if (alwaysScrollToEnd)
|
||||
{
|
||||
scroll.ScrollToEnd();
|
||||
scroll.ScrollChanged += ScrollChanged;
|
||||
// scroll.SizeChanged += Scroll_SizeChanged;
|
||||
}
|
||||
else { scroll.ScrollChanged -= ScrollChanged; /*scroll.ScrollChanged -= ScrollChanged; */}
|
||||
}
|
||||
else { throw new InvalidOperationException("The attached AlwaysScrollToEnd property can only be applied to ScrollViewer instances."); }
|
||||
}
|
||||
|
||||
|
||||
//private static void Scroll_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
//{
|
||||
// ScrollViewer scroll = sender as ScrollViewer;
|
||||
// if (scroll == null) { throw new InvalidOperationException("The attached AlwaysScrollToEnd property can only be applied to ScrollViewer instances."); }
|
||||
// double d = scroll.ActualHeight + scroll.ViewportHeight + scroll.ExtentHeight;
|
||||
// scroll.ScrollToVerticalOffset(d);
|
||||
//}
|
||||
|
||||
|
||||
public static bool GetAlwaysScrollToEnd(ScrollViewer scroll)
|
||||
{
|
||||
if (scroll == null) { throw new ArgumentNullException("scroll"); }
|
||||
return (bool)scroll.GetValue(AlwaysScrollToEndProperty);
|
||||
}
|
||||
|
||||
|
||||
public static void SetAlwaysScrollToEnd(ScrollViewer scroll, bool alwaysScrollToEnd)
|
||||
{
|
||||
if (scroll == null) { throw new ArgumentNullException("scroll"); }
|
||||
scroll.SetValue(AlwaysScrollToEndProperty, alwaysScrollToEnd);
|
||||
}
|
||||
|
||||
|
||||
private static void ScrollChanged(object sender, ScrollChangedEventArgs e)
|
||||
{
|
||||
ScrollViewer scroll = sender as ScrollViewer;
|
||||
if (scroll == null) { throw new InvalidOperationException("The attached AlwaysScrollToEnd property can only be applied to ScrollViewer instances."); }
|
||||
|
||||
|
||||
if (e.ExtentHeightChange == 0) { _autoScroll = scroll.VerticalOffset == scroll.ScrollableHeight; }
|
||||
if (_autoScroll && e.ExtentHeightChange != 0) { scroll.ScrollToVerticalOffset(scroll.ExtentHeight); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{CF4D6BE0-A862-401F-A3A6-4FDAC32F5B70}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>StormChatWPF</RootNamespace>
|
||||
<AssemblyName>StormChatWPF</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject>StormChatWPF.App</StartupObject>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="Chat.cs" />
|
||||
<Compile Include="MainWindow.xaml.cs">
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ScrollViewer.cs" />
|
||||
<Compile Include="UI\ChatBubble.cs" />
|
||||
<Page Include="LogWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="LogWindow.xaml.cs">
|
||||
<DependentUpon>LogWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Page Include="MainWindow.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Interact\Interact.csproj">
|
||||
<Project>{cef054af-95b7-4b88-934e-02705feea554}</Project>
|
||||
<Name>Interact</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="UI\Resources\登录界面底.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="UI\Resources\闪电.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="UI\Resources\聊天气泡.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -1,41 +0,0 @@
|
||||
using Interact;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace StormChatWPF
|
||||
{
|
||||
/// <summary>
|
||||
/// 聊天气泡
|
||||
/// </summary>
|
||||
class ChatBubble:Label
|
||||
{
|
||||
TextBlock text = new TextBlock()
|
||||
{
|
||||
MaxWidth = 400,
|
||||
MinWidth = 10,
|
||||
MinHeight = 20,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
FontFamily = new FontFamily("Comic Sans MS"),
|
||||
HorizontalAlignment = HorizontalAlignment.Right,
|
||||
VerticalAlignment = VerticalAlignment.Bottom
|
||||
};
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="msg">传入消息体</param>
|
||||
/// <param name="alignment">设置水平对齐方式,参数为HorizontalAlignment枚举类型</param>
|
||||
public ChatBubble(Message msg,HorizontalAlignment alignment)
|
||||
{
|
||||
Background = new ImageBrush()
|
||||
{
|
||||
ImageSource=new BitmapImage(new Uri("pack://application:,,,/UI/Resources/聊天气泡.png"))
|
||||
};
|
||||
HorizontalAlignment = alignment;
|
||||
text.Text = msg.Text;
|
||||
Content = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
@@ -12,14 +12,6 @@ const (
|
||||
server_addr string = "localhost:3727"
|
||||
)
|
||||
|
||||
//用户组
|
||||
const (
|
||||
ugroup_user string = "User" //普通用户
|
||||
ugroup_vip string = "Vip" //会员
|
||||
ugroup_admin string = "Admin" //管理员
|
||||
ugroup_group string = "Group" //群聊
|
||||
)
|
||||
|
||||
//消息头的通用字段名称
|
||||
const (
|
||||
keyname_operation string = "Operation" //操作名
|
||||
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
//用户信息
|
||||
type UserInfo struct {
|
||||
User string //用户名
|
||||
NickName string //昵称
|
||||
Motto string //签名
|
||||
UGroup string //用户组。ugroup_*常量值
|
||||
}
|
||||
|
||||
//用户组
|
||||
const (
|
||||
ugroup_user string = "User" //普通用户
|
||||
ugroup_vip string = "Vip" //会员
|
||||
ugroup_admin string = "Admin" //管理员
|
||||
ugroup_group string = "Group" //群聊
|
||||
)
|
||||
@@ -1,9 +0,0 @@
|
||||
package main
|
||||
|
||||
//用户信息
|
||||
type UserInfo struct {
|
||||
User string //用户名
|
||||
NickName string //昵称
|
||||
Motto string //签名
|
||||
UGroup string //用户组。ugroup_*常量值
|
||||
}
|
||||
@@ -6,7 +6,7 @@ CREATE TABLE `message` (
|
||||
`To` varchar(12) NOT NULL,
|
||||
`Msg` text,
|
||||
PRIMARY KEY (`Id`)
|
||||
) AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
|
||||
) DEFAULT CHARSET=utf8;
|
||||
|
||||
LOCK TABLES `message` WRITE;
|
||||
UNLOCK TABLES;
|
||||
@@ -20,9 +20,9 @@ CREATE TABLE `user` (
|
||||
`UGroup` enum('User','Vip','Admin','Group') DEFAULT 'User',
|
||||
`Photo` mediumtext,
|
||||
PRIMARY KEY (`User`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
) DEFAULT CHARSET=utf8;
|
||||
|
||||
LOCK TABLES `user` WRITE;
|
||||
INSERT INTO `user` VALUES ('mattuy','mattuy','Mattuy','','Admin',NULL);
|
||||
INSERT INTO `user` VALUES ('test','test','Test','This is my Motto.','Admin',NULL);
|
||||
UNLOCK TABLES;
|
||||
|
||||
|
||||
@@ -14,35 +14,6 @@ const (
|
||||
timeout_message = 100 * 365 * 24 * 60 * 60
|
||||
)
|
||||
|
||||
//用户组
|
||||
const (
|
||||
ugroup_user string = "User" //普通用户
|
||||
ugroup_vip string = "Vip" //会员
|
||||
ugroup_admin string = "Admin" //管理员
|
||||
ugroup_group string = "Group" //群聊
|
||||
)
|
||||
|
||||
//消息头的通用字段名称
|
||||
const (
|
||||
keyname_operation string = "Operation" //操作名
|
||||
keyname_token string = "Token"
|
||||
)
|
||||
|
||||
//控制消息。详见设计文档
|
||||
const (
|
||||
operation_panic string = "Panic"
|
||||
operation_trans_message string = "TransMessage"
|
||||
operation_send_message string = "SendMessage"
|
||||
operation_ping string = "Ping"
|
||||
operation_login string = "Login2"
|
||||
operation_login_old1 string = "Login"
|
||||
operation_logout string = "Logout"
|
||||
operation_offline string = "Offline"
|
||||
operation_get_user_list string = "GetUserList"
|
||||
operation_update_userinfo string = "UpdateUserInfo"
|
||||
operation_get_users string = "GetUsers"
|
||||
)
|
||||
|
||||
//字符串常量
|
||||
const (
|
||||
//日志文件。交互模式时无效(输出到os.Stdout)
|
||||
|
||||
@@ -5,6 +5,27 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
//控制消息。详见设计文档
|
||||
const (
|
||||
operation_panic string = "Panic"
|
||||
operation_trans_message string = "TransMessage"
|
||||
operation_send_message string = "SendMessage"
|
||||
operation_ping string = "Ping"
|
||||
operation_login string = "Login2"
|
||||
operation_login_old1 string = "Login"
|
||||
operation_logout string = "Logout"
|
||||
operation_offline string = "Offline"
|
||||
operation_get_user_list string = "GetUserList"
|
||||
operation_update_userinfo string = "UpdateUserInfo"
|
||||
operation_get_users string = "GetUsers"
|
||||
)
|
||||
|
||||
//消息头的通用字段名称
|
||||
const (
|
||||
keyname_operation string = "Operation" //操作名
|
||||
keyname_token string = "Token"
|
||||
)
|
||||
|
||||
//消息结构
|
||||
type Message struct {
|
||||
When time.Time //接收时间
|
||||
|
||||
@@ -5,6 +5,14 @@ import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
//用户组
|
||||
const (
|
||||
ugroup_user string = "User" //普通用户
|
||||
ugroup_vip string = "Vip" //会员
|
||||
ugroup_admin string = "Admin" //管理员
|
||||
ugroup_group string = "Group" //群聊
|
||||
)
|
||||
|
||||
//用户信息
|
||||
type UserInfo struct {
|
||||
User string //用户名
|
||||
|
||||