问:ESFramework是什么?
答:.NET通信框架.
问:能做什么?
答:分布式通信的系统如:即时通讯,多人在线游戏、在线网页游戏、文件传送、数据采集、分布式OA系统、远程教育系统等。
问:平台要求?
答:直接构建在.NET Framework 2.0上.
问:具体怎么写?
答:引擎初始化(服务端)
[ESBasic.dll版本2.0.0.0,ESFramework.dll版本6.6.0.0,ESFramework.Boost.dll版本1.0.0.0,均来自官方Demo]
using ESFramework;
using ESFramework.Server.UserManagement;
using ESPlus.Application.Basic.Server;
using ESPlus.Application.Contacts.Server;
using ESPlus.Application.CustomizeInfo;
using ESPlus.Rapid;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ConsoleServer
{class Program{static IRapidServerEngine RapidServerEngine = RapidEngineFactory.CreateServerEngine();static void Main(string[] args){try{InitEngine();//等待用户退出服务应用while (true){Console.WriteLine("输入'exit'退出服务器:");string cmd = Console.ReadLine();if (cmd.ToLower() == "exit"){break;}else{Console.WriteLine("输入不正确!");}}}catch (Exception ex){Console.WriteLine("发生异常:");Console.WriteLine(ex.ToString());Console.WriteLine("按任意键结束");Console.ReadKey();}finally{//关闭服务RapidServerEngine.Close();}}private static void InitEngine(){//此处为免费用户.如果是其它类型的授权用户,请使用下面的语句设定正确的授权用户ID和密码。ESPlus.GlobalUtil.SetAuthorizedUser("FreeUser", "");//使用简单的好友管理器,假设所有在线用户都是好友。(仅仅用于demo)DefaultContactsManager contactsManager = new DefaultContactsManager();RapidServerEngine.ContactsManager = contactsManager;//初始化服务端引擎端口(CustomizeHandler为客户端的消息,同步调用请求.BasicHandler为登陆验证)RapidServerEngine.Initialize(4530, new CustomizeHandler(), new BasicHandler());//服务端引擎初始化成功后,其UserManager属性才可用。contactsManager.UserManager = RapidServerEngine.UserManager;//上线时通知相关联系人RapidServerEngine.ContactsController.ContactsConnectedNotifyEnabled = true;//下线时通知相关联系人RapidServerEngine.ContactsController.ContactsDisconnectedNotifyEnabled = true;//设置重登陆模式:新连接取代旧链接RapidServerEngine.UserManager.RelogonMode = RelogonMode.ReplaceOld;//用户操作事件订阅RapidServerEngine.UserManager.UserDisplayer = new UserDisplayer();}}public class UserDisplayer : IUserDisplayer{public void AddUser(string userID, ClientType clientType, string userAddress){Console.WriteLine("");Console.WriteLine("AddUser:");Console.WriteLine($"增加用户[{userID}],设备类型[{clientType.ToString("G")}],地址[{userAddress}]");}public void ClearAll(){Console.WriteLine("");Console.WriteLine("ClearAll:");Console.WriteLine("清空列表");}public void OnMessageReceived(string userID, int messageType){//用户收到信息事件,用于统计下载次数//Console.WriteLine("");//Console.WriteLine("OnMessageReceived");//Console.WriteLine($"收到用户[{userID}]消息:[{messageType}]");}public void OnMessageSent(string userID, int messageType){//用户收到信息事件,用于统计上传次数//Console.WriteLine("");//Console.WriteLine("OnMessageSent");//Console.WriteLine($"发给用户[{userID}]消息:[{messageType}]");}public void RemoveUser(string userID, string cause){Console.WriteLine("");Console.WriteLine("RemoveUser:");Console.WriteLine($"用户ID[{userID}]已下线,原因[{cause}]") ;}}public class CustomizeHandler : ICustomizeHandler{public void HandleInformation(string sourceUserID, int informationType, byte[] info){//throw new NotImplementedException();}/// <summary>/// 处理客户端对服务端的同步信息/// </summary>public byte[] HandleQuery(string sourceUserID, int informationType, byte[] info){if (informationType == InformationTypes.ClientCallServer){Console.WriteLine("");Console.WriteLine($"收到客户端[{sourceUserID}]同步信息[{Encoding.UTF8.GetString(info)}]");string requestMessage = System.Text.Encoding.UTF8.GetString(info);string responseMessage =$"服务端已收到您的同步调用请求[{requestMessage}]!";return Encoding.UTF8.GetBytes(responseMessage);}return null;}}public class BasicHandler : IBasicHandler{public bool VerifyUser(string systemToken, string userID, string password, out string failureCause){failureCause = "始终认为登陆成功";return true;}}/// <summary>/// 自定义信息的类型/// </summary>public static class InformationTypes{/// <summary>/// 聊天信息/// </summary>public const int Chat = 0;/// <summary>/// 振动提醒/// </summary>public const int Viberation = 1;/// <summary>/// 客户端同步调用服务端/// </summary>public const int ClientCallServer = 100;}
}
初始化引擎成功后,若要在服务端观察用户接入情况,可将引擎传入继承接口IUserDisplayer的Form.接口中含有引擎用户上线下线消息收发时的事件.但是由于Form实时刷新用户,对性能开销较大,正式产品不推荐用.
问:怎么写客户端?
答:引擎初始化(客户端)
using ESBasic;
using ESPlus.Application.Basic;
using ESPlus.Application.CustomizeInfo;
using ESPlus.Application.FileTransfering;
using ESPlus.Application.P2PSession.Passive;
using ESPlus.FileTransceiver;
using ESPlus.Rapid;
using ESPlus.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ConsoleClient
{class Program{static IRapidPassiveEngine rapidPassiveEngine = RapidEngineFactory.CreatePassiveEngine();static void Main(string[] args){try{string userID = "";while (true){Console.WriteLine("请输入自己的用户ID:");userID = Console.ReadLine().Trim();if (userID.Equals("")){Console.WriteLine("自己的用户名不能为空或纯空格!");}else{break;}}string pwd = "";Console.WriteLine($"客户端[{userID}]尝试登陆服务...");if (InitEngine(userID, pwd, out string FailCause) == true){//登陆成功Console.WriteLine("登陆成功");}else{//登陆失败throw new Exception($"登陆失败,原因:[{FailCause}]");}//打印在线用户列表:PrintOnlineUserList();//while (true){Console.WriteLine("请输入要对话的用户ID:");string user = Console.ReadLine();if (user.Equals(userID)){Console.WriteLine("不能与自己对话!");continue;}if (IsUserOnline(user) == false){Console.WriteLine("该用户不在线,不能对话!");continue;}Console.WriteLine($"可以与用户[{user}]对话!请输入文本内容:");string content = Console.ReadLine();if (content.Trim().Equals("")){Console.WriteLine("不能发送空或空格!");continue;}//开始发送:SendViberationToFriend(user);SendMessageToFriend(user, content);SendFileToFriend(user, "123.txt");SendQurryToServer($"我[{userID}]与用户[{user}]对话一次");}}catch (Exception ex){Console.WriteLine("发生异常:");Console.WriteLine(ex.ToString());Console.WriteLine("按任意键结束");Console.ReadKey();}finally{rapidPassiveEngine.Close();}}private static bool InitEngine(string userID, string pwd, out string FailCause){//初始化引擎//赋值系统身份标志TokenrapidPassiveEngine.SystemToken = "";//初始化引擎(id,psw,ip,port,继承ICustomizeHandler接口的对象)并登录,返回登录结果。如果登陆成功,引擎将与当前用户绑定。LogonResponse logonResponse = rapidPassiveEngine.Initialize(userID, pwd,"127.0.0.1", 4530,new CustomizeHandler());//判断登陆详情if (logonResponse.LogonResult != LogonResult.Succeed){FailCause = logonResponse.FailureCause;return false;}else{FailCause = "";}//续订事件//预订断线事件rapidPassiveEngine.ConnectionInterrupted +=new CbGeneric(rapidPassiveEngine_ConnectionInterrupted);//预订重连开始事件rapidPassiveEngine.ConnectionRebuildStart +=new CbGeneric(rapidPassiveEngine_ConnectionRebuildStart);//预订重连成功事件rapidPassiveEngine.RelogonCompleted +=new CbGeneric<LogonResponse>(rapidPassiveEngine_RelogonCompleted);//预订联系人上线的事件rapidPassiveEngine.ContactsOutter.ContactsConnected +=new CbGeneric<string>(ContactsOutter_ContactsConnected);//预订联系人下线的事件rapidPassiveEngine.ContactsOutter.ContactsOffline +=new CbGeneric<string>(ContactsOutter_ContactsOffline);//预订自己被踢出掉线的事件rapidPassiveEngine.BasicOutter.BeingKickedOut += new CbGeneric(BasicOutter_BeingKickedOut);//预订自己被挤掉线的事件rapidPassiveEngine.BasicOutter.BeingPushedOut += new CbGeneric(BasicOutter_BeingPushedOut);//预定收到了来自发送方发送文件(夹)的请求的事件rapidPassiveEngine.FileOutter.FileRequestReceived +=new CbFileRequestReceived(fileOutter_FileRequestReceived);//预定接收方回复了同意/拒绝接收文件(夹)时的事件rapidPassiveEngine.FileOutter.FileResponseReceived +=new CbGeneric<TransferingProject, bool>(fileOutter_FileResponseReceived);//预定P2P Channel创建成功的事件rapidPassiveEngine.P2PController.P2PChannelOpened +=new CbGeneric<P2PChannelState>(P2PController_P2PChannelOpened);//预定P2P Channel关闭事件rapidPassiveEngine.P2PController.P2PChannelClosed +=new CbGeneric<P2PChannelState>(P2PController_P2PChannelClosed);return true;}private static void P2PController_P2PChannelClosed(P2PChannelState obj){Console.WriteLine("");Console.WriteLine("P2PController_P2PChannelClosed");Console.WriteLine("P2P Channel关闭事件通知");Console.WriteLine($"通道状态:对方IP信息[{ obj.DestIPE}]");}private static void P2PController_P2PChannelOpened(P2PChannelState obj){Console.WriteLine("");Console.WriteLine("P2PController_P2PChannelOpened");Console.WriteLine("P2P Channel创建成功的事件通知");Console.WriteLine($"通道状态:对方IP信息[{ obj.DestIPE}]");}private static void fileOutter_FileResponseReceived(TransferingProject obj1, bool obj2){Console.WriteLine("");Console.WriteLine("fileOutter_FileResponseReceived");Console.WriteLine($"收到来自对方同意或拒绝[{obj2}]接收文件[{obj1.ProjectName}]的事件通知");Console.WriteLine("");}private static void fileOutter_FileRequestReceived(string fileID, string senderID, string fileName, ulong totalSize, ResumedProjectItem resumedFileItem, string comment){Console.WriteLine("");Console.WriteLine("fileOutter_FileRequestReceived");Console.WriteLine($"收到来自对方[{senderID}]的文件[{fileID}]的事件通知");Console.WriteLine($"文件名<{fileName}>大小<{totalSize / 1024 / 1024}M>备注<{comment}>");//测试时始终接受文件string savePath = "456.txt";//若要测试拒收,填写一个无效路径//正式应用时应让用户选择是否接收和选择接收路径if (string.IsNullOrEmpty(savePath)==false){//收文件rapidPassiveEngine.FileOutter.BeginReceiveFile(fileID, savePath);}else{//拒收文件TransferingProject fileInfo = rapidPassiveEngine.FileOutter.GetTransferingProject(fileID);if (fileInfo != null){rapidPassiveEngine.FileOutter.RejectFile(fileID);}}}private static void BasicOutter_BeingPushedOut(){Console.WriteLine("");Console.WriteLine("BasicOutter_BeingPushedOut");Console.WriteLine("自身被其他端登陆挤出下线通知");Console.WriteLine("");}private static void BasicOutter_BeingKickedOut(){Console.WriteLine("");Console.WriteLine("BasicOutter_BeingKickedOut");Console.WriteLine("自身被踢出下线通知");Console.WriteLine("");}private static void ContactsOutter_ContactsOffline(string contactID){Console.WriteLine("");Console.WriteLine("ContactsOutter_ContactsOffline");Console.WriteLine($"联系人[{contactID}]下线通知");Console.WriteLine("");}private static void ContactsOutter_ContactsConnected(string contactID){Console.WriteLine("");Console.WriteLine("ContactsOutter_ContactsConnected");Console.WriteLine($"联系人[{contactID}]上线通知");Console.WriteLine("");}private static void rapidPassiveEngine_RelogonCompleted(LogonResponse obj){Console.WriteLine("");Console.WriteLine("rapidPassiveEngine_RelogonCompleted");Console.WriteLine("重连开始连接成功事件");Console.WriteLine("与服务器重新连接成功");}private static void rapidPassiveEngine_ConnectionRebuildStart(){Console.WriteLine("");Console.WriteLine("rapidPassiveEngine_ConnectionRebuildStart");Console.WriteLine("重连开始连接事件");Console.WriteLine("与服务器重新开始连接");}private static void rapidPassiveEngine_ConnectionInterrupted(){Console.WriteLine("");Console.WriteLine("rapidPassiveEngine_ConnectionInterrupted");Console.WriteLine("断线事件");Console.WriteLine("与服务器断开");}private static void PrintOnlineUserList(){//获取好友列表(假定所有用户都是好友)List<string> list = rapidPassiveEngine.BasicOutter.GetAllOnlineUsers();foreach (string id in list){Console.WriteLine($"用户[{id}]当前在线");}}private static bool IsUserOnline(string userID){List<string> list = rapidPassiveEngine.BasicOutter.GetAllOnlineUsers();if (list.Contains(userID)){return true;}else{return false;}}private static void SendMessageToFriend(string destUserID, string message){//发送文本消息TextChatContract contract = new TextChatContract(message);byte[] info = CompactPropertySerializer.Default.Serialize(contract);rapidPassiveEngine.CustomizeOutter.Send(destUserID, InformationTypes.Chat, info);Console.WriteLine($"");Console.WriteLine($"向用户[{destUserID}]的发送文本消息:");Console.WriteLine($"{contract.GetSentTime().ToString()}");Console.WriteLine($"{contract.Text}");Console.WriteLine($"");}private static void SendFileToFriend(string destUserID, string filePath){if (File.Exists(filePath)==false){return;}SendingFileParas sendingFileParas = new SendingFileParas(20480, 0);//文件数据包大小,可以根据网络状况设定,//局网内可以设为204800,传输速度可以达到30M/s以上;//公网建议设定为2048或4096或8192rapidPassiveEngine.FileOutter.BeginSendFile(destUserID, filePath, null, sendingFileParas, out string fileID);}private static void SendViberationToFriend(string destUserID){//发送震动消息rapidPassiveEngine.CustomizeOutter.Send(destUserID, InformationTypes.Viberation,null);Console.WriteLine($"");Console.WriteLine($"向用户[{destUserID}]的发送震动消息:");Console.WriteLine($"");}private static void SendQurryToServer(string qurry){Console.WriteLine($"");Console.WriteLine($"向服务器查询[{qurry}]的信息:");try{byte[] requestInfo = Encoding.UTF8.GetBytes(qurry);byte[] responseInfo = rapidPassiveEngine.CustomizeOutter.Query(InformationTypes.ClientCallServer, requestInfo);string responseMessage = System.Text.Encoding.UTF8.GetString(responseInfo);Console.WriteLine($"服务器返回信息:");Console.WriteLine(responseMessage);}catch (Exception ee){Console.WriteLine($"发生异常:");Console.WriteLine(ee.ToString());}}}public class CustomizeHandler : ICustomizeHandler{/// <summary>/// 处理消息,如果sourceUserID为null, 则表示是服务端发送过来的消息;如果sourceUserID不为null, 则表示是其他客户端发送过来的消息/// </summary> public void HandleInformation(string sourceUserID, int informationType, byte[] info){if (informationType == InformationTypes.Chat){TextChatContract contract = CompactPropertySerializer.Default.Deserialize<TextChatContract>(info, 0);Console.WriteLine($"");Console.WriteLine($"收到来自用户[{sourceUserID}]的消息:");Console.WriteLine($"{contract.GetSentTime().ToString()}");Console.WriteLine($"{contract.Text}");Console.WriteLine($"");return;}if (informationType == InformationTypes.Viberation){//计算机蜂鸣器提醒Console.Beep();Console.WriteLine($"");Console.WriteLine($"收到来自用户[{sourceUserID}]的震动提醒:");Console.WriteLine($"");return;}}public byte[] HandleQuery(string sourceUserID, int informationType, byte[] info){return null;}}/// <summary>/// TextChatContract 文字交谈协议。/// </summary> public class TextChatContract{private static DateTime DT2016 = DateTime.Parse("2016.01.01 00:00:00");#region Ctorpublic TextChatContract(){}public TextChatContract(string _text){this.text = _text;TimeSpan span = DateTime.Now - DT2016;this.timeSent = (int)span.TotalSeconds;}#endregion#region TimeSentprivate int timeSent = 0;/// <summary>/// 距离2016.01.01 00:00:00的总秒数。/// </summary>public int TimeSent{get{return this.timeSent;}set{this.timeSent = value;}}public DateTime GetSentTime(){return DT2016.AddSeconds(this.timeSent);}#endregion #region Textprivate string text = null;public string Text{get{return this.text;}set{this.text = value;}}#endregion }/// <summary>/// 自定义信息的类型/// </summary>public static class InformationTypes{/// <summary>/// 聊天信息/// </summary>public const int Chat = 0;/// <summary>/// 振动提醒/// </summary>public const int Viberation = 1;/// <summary>/// 客户端同步调用服务端/// </summary>public const int ClientCallServer = 100;}
}
以上代码实现了服务端和客户端的简单文本对话及振铃提醒.调用相应的方法即可在系统的客户端之间之发送文本消息.