Hprose官网:http://www.hprose.com/
下载C#版本Hprose.dll
地址:https://github.com/hprose/hprose-dotnet
文档地址没有C#版本的文档,我是通看java文档来尝试使用C#文档,有相同的地方,网上还有1.3版本的文档,可以进行参考
文档地址:https://github.com/hprose/hprose-doc
先看简单案例:
服务器
创建一个http服务器
HproseHttpListenerServer server = new HproseHttpListenerServer("http://localhost:20108/");
注册方法
server.Add("SayHello", hello);
启动服务器
server.Start();
客户端:
连接服务器
Hprose.Client.HproseClient client = Hprose.Client.HproseClient.Create("http://localhost:20108/");
两种调用方法的方式
Console.WriteLine(hello.SayHello("World")); //接口方式
Console.WriteLine(client.Invoke("SayHello", new object[] { "World" })); //方法名方式
运行结果
在客户单就可以调用服务器的方法,并且在客户端实现输出
下面看看我在项目中的应用
首先我定义了一个xml配置表用来控制我所需要的数据
<RpcConfig>
<RegisterClass>
<class namespace="NetTableUser" classname ="protos.GameProto.NetTableUser|Proto.dll" />
<class namespace="NetServerConfig" classname ="protos.GameProto.NetServerConfig|Proto.dll" />
<class namespace="NetTableRole" classname ="protos.GameProto.NetTableRole|Proto.dll" />
<class namespace="_Game_Match_RoleInfo" classname="protos.GameProto._Game_Match_RoleInfo|Proto.dll" />
<class namespace="NetProps" classname ="protos.GameProto.NetProps|Proto.dll" />
<class namespace="NetTankStruct" classname ="protos.GameProto.NetTankStruct|Proto.dll" />
</RegisterClass>
<RegisterChannel>
<channel id ="GameSuit0" type="tcp" url="127.0.0.1:10003">
<data type="class" connect="GameSuit.Rpc.GameSuit" />
</channel>
</RegisterChannel>
<ConnectChannel>
<class name="GameHall0" type="tcp" url="127.0.0.1:10001" />
<class name="GameData" type="tcp" url="127.0.0.1:4321" />
</ConnectChannel>
</RpcConfig>
对这个配置表解释
<!-- 配置表拥有参数
RegisterClass 注册类
namespace:类的别名
classname:类完整命名空间,如果不是当前程序集需要执行程序集 格式:类完整命名空间|程序集
RegisterChannel 注册信道
type:信道类型 有两种:tcp|http
data:信道注册的数据
type:数据类型 有三种class(类)|method(方法)|static(静态方法)
connect: class类的完整命名空间 method:方法名|类的完整命名空间 static:方法名|类完整命名空间
ConnectChannel 连接信道
type:信道类型
url:地址
实例:
< RegisterClass>
<class namespace="NetTableUser" classname ="protos.NetStruct.NetTableUser|Proto.dll" />
</RegisterClass>
<RegisterChannel>
<channel id ="1" type="tcp" url="127.0.0.1:4321">
<data type="class" connect="GameData.HproseRpc.DataOperation" />
<data type="method" connect = "add|GameData.HproseRpc.DataOperation" />
</channel>
</RegisterChannel>
<ConnectChannel>
<class name="GameData" type="tcp" url="127.0.0.1:4321" />
</ConnectChannel>
-->
然后定义一个配置表解析脚本:
using System.Collections.Generic;
using System.Xml;
using IVServer.Xml;
public class RpcConfig:XMLCache<RpcConfig>
{
//注册类
public class RegisterClass
{
public string NameSpace { get; set; }
public string ClassName { get; set; }
}
//注册信道
public class RegisterChannel
{
public class ChannelData
{
public string DataType { get; set; }
public string DataConnect { get; set; }
}
public string ChannelID { get; set; }
public string ChannelType { get; set; }
public string ChannelUrl { get; set; }
public List<ChannelData> ChannelDatas = new List<ChannelData>();
public string GetUrl
{
get {
return string.Format("{0}://{1}/",ChannelType,ChannelUrl);
}
}
}
//连接信道
public class ConnectChannel
{
public string ConnectName { get; set; }
public string ConnectType { get; set; }
public string ConnectUrl { get; set; }
public string GetUrl
{
get {
return string.Format("{0}://{1}/", ConnectType, ConnectUrl);
}
}
}
public List<RegisterClass> RegisterClassList = new List<RpcConfig.RegisterClass>();
public Dictionary<string, RegisterChannel> RegisterChannelDic = new Dictionary<string, RpcConfig.RegisterChannel>();
public Dictionary<string, ConnectChannel> ConnectChannelDic = new Dictionary<string, ConnectChannel>();
//解析配置表
public override void ParseXml(XmlNode xmlNode)
{
XmlNodeList child = null;
switch (xmlNode.Name)
{
case "RegisterClass":
child = xmlNode.ChildNodes;
for (int i = 0; i < child.Count; i++)
{
RegisterClass rc = new RegisterClass();
rc.NameSpace = child[i].Attributes["namespace"].Value;
rc.ClassName = child[i].Attributes["classname"].Value;
RegisterClassList.Add(rc);
}
break;
case "RegisterChannel":
child = xmlNode.ChildNodes;
for (int i = 0; i < child.Count; i++)
{
RegisterChannel rc = new RegisterChannel();
rc.ChannelID = child[i].Attributes["id"].Value;
rc.ChannelType = child[i].Attributes["type"].Value;
rc.ChannelUrl = child[i].Attributes["url"].Value;
XmlNodeList datas = child[i].ChildNodes;
for (int j = 0; j < datas.Count; j++)
{
RegisterChannel.ChannelData cd = new RegisterChannel.ChannelData();
cd.DataType = datas[j].Attributes["type"].Value;
cd.DataConnect = datas[j].Attributes["connect"].Value;
rc.ChannelDatas.Add(cd);
}
RegisterChannelDic.Add(rc.ChannelID,rc);
}
break;
case "ConnectChannel":
child = xmlNode.ChildNodes;
for (int i = 0; i < child.Count; i++)
{
ConnectChannel cc = new ConnectChannel();
cc.ConnectName = child[i].Attributes["name"].Value;
cc.ConnectType = child[i].Attributes["type"].Value;
cc.ConnectUrl = child[i].Attributes["url"].Value;
ConnectChannelDic.Add(cc.ConnectName,cc);
}
break;
}
OnParseFinsh(xmlNode.Name, this);
}
}
xml配置表解析父类
using System;
using System.Collections.Generic;
using System.Xml;
namespace IVServer.Xml
{
public abstract class XMLCache<T> where T:XMLCache<T>
{
/// <summary>
/// 数据字段
/// </summary>
private static Dictionary<string, T> dataDic = new Dictionary<string, T>();
/// <summary>
/// xml数据信息
/// </summary>
private static XmlInfo<T> xmlInfo = new XmlInfo<T>();
static XMLCache()
{
string name = typeof(T).Name;
xmlInfo.InitXmlInfo(name);
//ResterConfig();
}
/// <summary>
/// 重置配置表
/// </summary>
public static void ResterConfig()
{
dataDic.Clear();
string name = typeof(T).Name;
xmlInfo.InitXmlInfo(name);
}
/// <summary>
/// 是否存在
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static bool IsHave(string key)
{
return dataDic.ContainsKey(key);
}
/// <summary>
/// 是否存在
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsHave(string key, out T value)
{
if (!IsHave(key))
{
value = null;
return false;
}
value = dataDic[key];
return true;
}
/// <summary>
/// 尝试获取
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public static void TryGet(string key, out T value)
{
if (!IsHave(key, out value))
{
value = null;
}
}
/// <summary>
/// 尝试获取
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static T TryGet(string key)
{
T data = default(T);
if (IsHave(key, out data))
return data;
return default(T);
}
/// <summary>
/// 数据数量
/// </summary>
public static int Count {
get {
return dataDic.Count;
}
}
/// <summary>
/// 获取数据列表
/// </summary>
public static List<T> GetData
{
get {
List<T> dataList = new List<T>(dataDic.Values);
return dataList;
}
}
/// <summary>
/// 解析数据
/// </summary>
/// <param name="xmlNode"></param>
/// <param name="data"></param>
public abstract void ParseXml(XmlNode xmlNode);
/// <summary>
/// 数据解析完毕后
/// </summary>
/// <param name="key"></param>
/// <param name="data"></param>
protected static void OnParseFinsh(string key, T data)
{
if (dataDic.ContainsKey(key)) return;
dataDic.Add(key,data);
}
}
public class XmlInfo<T> where T: XMLCache<T>
{
public void InitXmlInfo(string xmlName)
{
try
{
//xml 路径
string filePath = string.Format(GameConfig.GetConfig("Config.XML.Data.Path"), xmlName);
XmlDocument xmlDoc = new XmlDocument();
XmlReader reader = XmlReader.Create(filePath);
if (reader != null)
{
xmlDoc.Load(reader);
}
reader.Close();
XmlNodeList nodeList = xmlDoc.SelectSingleNode(xmlName).ChildNodes;
for (int i = 0; i < nodeList.Count; i++)
{
if (nodeList[i].NodeType == XmlNodeType.Comment) continue;
Type type = typeof(T);
T obj = Activator.CreateInstance<T>();
obj.ParseXml(nodeList[i]);
}
}
catch (Exception e)
{
Log.LogError("XML解析错误,Exception:{0}",e.Message);
}
}
}
}
然后在创建一个类来控制所有的数据
using System;
using System.Collections.Generic;
using Hprose.IO;
using Hprose.Server;
using Hprose.Client;
using System.Reflection;
//RPC类型
public enum RpcType
{
tcp,
http
}
//服务器类型
public enum RpcSuitType
{
//匹配服
Match,
//战斗服
Fight,
//物理连接服
PhySuit,
//数据服
DBSuit,
}
/// <summary>
/// Rpc信息
/// </summary>
public class RpcInfo
{
//Rpc名称
public string RpcName;
//服务器类型
public RpcSuitType RpcSuitType;
//服务器优先级
public int RcpLevel;
//Rpc类型
public RpcType RpcType;
//rpc地址
public string RpcUrl;
public string GetUrl {
get {
return string.Format("{0}://{1}/",RpcType,RpcUrl);
}
}
}
/// <summary>
/// Rpc管理类
/// </summary>
public class RpcManager
{
private static Dictionary<string, HproseService> ServerDic = new Dictionary<string, HproseService>();
private static Dictionary<string, HproseClient> ClientDic = new Dictionary<string, HproseClient>();
/// <summary>
/// 注册的RPC列表
/// </summary>
private static Dictionary<string, HproseClient> rpcNameClientDic = new Dictionary<string, HproseClient>();
/// <summary>
/// 注册rpc列表
/// </summary>
private static Dictionary<string, RpcInfo> rpcNameDic = new Dictionary<string, RpcInfo>();
/// <summary>
/// 初始化Rpc
/// </summary>
public static void InitRpc()
{
//注册类
List<RpcConfig.RegisterClass> rcs = RpcConfig.TryGet("RegisterClass").RegisterClassList;
for (int i = 0; i < rcs.Count; i++)
{
Assembly assem = Assembly.GetEntryAssembly();
Type type = null;
if (rcs[i].ClassName.Contains("|"))
{
string[] ary = rcs[i].ClassName.Split('|');
assem = Assembly.LoadFrom(ary[1]);
type = assem.GetType(ary[0]);
}else
type = assem.GetType(rcs[i].ClassName);
HproseClassManager.Register(type, rcs[i].NameSpace);
}
//注册通道
Dictionary<string, RpcConfig.RegisterChannel> rcDic = RpcConfig.TryGet("RegisterChannel").RegisterChannelDic;
List<RpcConfig.RegisterChannel> rcList = new List<RpcConfig.RegisterChannel>(rcDic.Values);
for (int i = 0; i < rcList.Count; i++)
{
switch (rcList[i].ChannelType)
{
case "tcp":
try
{
HproseTcpListenerServer tcpServer = new HproseTcpListenerServer(rcList[i].GetUrl);
AddData(tcpServer, rcList[i].ChannelDatas);
tcpServer.Start();
if (tcpServer.IsStarted)
{
Log.LogInfo("信道 {0}-{1} 启动成功...", rcList[i].ChannelID, rcList[i].GetUrl);
}
else
Log.LogInfo("信道 {0}-{1} 启动失败...", rcList[i].ChannelID, rcList[i].GetUrl);
ServerDic.Add(rcList[i].ChannelID, tcpServer);
}catch(Exception e)
{
Log.LogError("信道 {0}-{1} 启动失败,Error:{2}", rcList[i].ChannelID, rcList[i].GetUrl,e.Message);
}
break;
case "http":
try
{
HproseHttpListenerServer httpServer = new HproseHttpListenerServer(rcList[i].GetUrl);
AddData(httpServer, rcList[i].ChannelDatas);
httpServer.Start();
if (httpServer.IsStarted)
{
Log.LogInfo("信道 {0}-{1} 启动成功...", rcList[i].ChannelID, rcList[i].GetUrl);
}
else
Log.LogInfo("信道 {0}-{1} 启动失败...", rcList[i].ChannelID, rcList[i].GetUrl);
ServerDic.Add(rcList[i].ChannelID, httpServer);
}
catch (Exception e)
{
Log.LogError("信道 {0}-{1} 启动失败,Error:{2}", rcList[i].ChannelID, rcList[i].GetUrl, e.Message);
}
break;
}
}
}
/// <summary>
/// 获取注册信道信息
/// </summary>
/// <param name="ChannelID"></param>
/// <returns></returns>
public static RpcConfig.RegisterChannel GetChannel(string ChannelID)
{
Dictionary<string, RpcConfig.RegisterChannel> rpcDic = RpcConfig.TryGet("RegisterChannel").RegisterChannelDic;
if (rpcDic.ContainsKey(ChannelID))
return rpcDic[ChannelID];
return null;
}
/// <summary>
/// 添加数据
/// </summary>
/// <param name="server"></param>
/// <param name="datas"></param>
private static void AddData(HproseService server,List<RpcConfig.RegisterChannel.ChannelData> datas)
{
for (int i = 0; i < datas.Count; i++)
{
string[] ary = null;
switch (datas[i].DataType)
{
case "class":
ary = datas[i].DataConnect.Split(',');
for (int j = 0; j < ary.Length; j++)
{
Assembly assem = Assembly.GetEntryAssembly();
server.Add(Activator.CreateInstance(assem.GetType(ary[j])));
}
break;
case "method":
ary = datas[i].DataConnect.Split(',');
for (int j = 0; j < ary.Length; j++)
{
if (ary[j].Equals("")) continue;
string[] dataAry = ary[j].Split('|');
Assembly assem = Assembly.GetEntryAssembly();
server.Add(dataAry[0],Activator.CreateInstance(assem.GetType(dataAry[1])));
}
break;
case "static":
ary = datas[i].DataConnect.Split(',');
for (int j = 0; j < ary.Length; j++)
{
if (ary[j].Equals("")) continue;
string[] dataAry = ary[j].Split('|');
Assembly assem = Assembly.GetEntryAssembly();
server.Add(dataAry[0], assem.GetType(dataAry[1]));
}
break;
}
}
}
/// <summary>
/// 连接服务器
/// </summary>
/// <param name="connectName"></param>
/// <returns></returns>
public static HproseClient Connect(string connectName)
{
if (ClientDic.ContainsKey(connectName))
return ClientDic[connectName];
Dictionary<string,RpcConfig.ConnectChannel> connDic = RpcConfig.TryGet("ConnectChannel").ConnectChannelDic;
if (connectName.Contains(":"))
{
HproseClient client = HproseClient.Create(connectName);
ClientDic.Add(connectName, client);
return client;
}
if (connDic.ContainsKey(connectName))
{
RpcConfig.ConnectChannel cc = connDic[connectName];
HproseClient client = HproseClient.Create(string.Format("{0}://{1}/", cc.ConnectType, cc.ConnectUrl));
ClientDic.Add(connectName, client);
return client;
}
return null;
}
/// <summary>
/// 连接服务器
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="connectName"></param>
/// <returns></returns>
public static T Connect<T>(string connectName) where T:class
{
HproseClient client =Connect(connectName);
return client.UseService<T>();
}
/// <summary>
/// 注册Rpc信息
/// </summary>
/// <param name="rpcInfo"></param>
public static void RegisterRpc(RpcInfo rpcInfo)
{
if (!rpcNameDic.ContainsKey(rpcInfo.RpcName))
{
rpcNameDic.Add(rpcInfo.RpcName, rpcInfo);
Log.LogInfo("服务器:{0}-{1} 注册本服务器...",rpcInfo.RpcName,rpcInfo.GetUrl);
}
}
/// <summary>
/// 移除Rpc信息
/// </summary>
/// <param name="rpcInfo"></param>
public static void RemoveRpc(RpcInfo rpcInfo)
{
RemoveRpc(rpcInfo.RpcName);
}
/// <summary>
/// 移除Rpc信息
/// </summary>
/// <param name="RpcName"></param>
public static void RemoveRpc(string RpcName)
{
if (rpcNameDic.ContainsKey(RpcName))
{
Log.LogInfo("服务器:{0}-{1} 移除本服务器注册...",RpcName, rpcNameDic[RpcName].GetUrl);
rpcNameDic.Remove(RpcName);
}
}
/// <summary>
/// 获取所有RpcInfo列表
/// </summary>
/// <returns></returns>
public static List<RpcInfo> GetRpcInfo()
{
return new List<RpcInfo>(rpcNameDic.Values);
}
///获取特定类型服务器
public static List<RpcInfo> GetRpcInfo(RpcSuitType suitType)
{
List<RpcInfo> all = new List<RpcInfo>(rpcNameDic.Values);
List<RpcInfo> suits = new List<RpcInfo>();
for(int i=0;i<all.Count;i++)
{
if(all[i].RpcSuitType==suitType)
suits.Add(all[i]);
}
return suits;
}
/// <summary>
/// 获取RpcInfo
/// </summary>
/// <param name="rpcName"></param>
/// <returns></returns>
public static RpcInfo GetRpcInfo(string rpcName)
{
if (rpcNameDic.ContainsKey(rpcName))
return rpcNameDic[rpcName];
return null;
}
/// <summary>
/// 连接Rpc
/// </summary>
/// <param name="rpcID"></param>
/// <returns></returns>
public static HproseClient RpcConnect(string rpcName)
{
if (!rpcNameDic.ContainsKey(rpcName))
return null;
if (rpcNameClientDic.ContainsKey(rpcName))
return rpcNameClientDic[rpcName];
//连接
RpcInfo info = rpcNameDic[rpcName];
HproseClient client = HproseClient.Create(info.GetUrl);
rpcNameClientDic.Add(rpcName,client);
return client;
}
/// <summary>
/// 连接Rpc
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="rpcName"></param>
/// <returns></returns>
public static T RpcConnect<T>(string rpcName) where T : class
{
HproseClient client = RpcConnect(rpcName);
return client.UseService<T>();
}
//连接Rpc
public static T RpcConnect<T>(RpcInfo rpcInfo)
{
HproseClient client = RpcConnect(rpcInfo.RpcName);
return client.UseService<T>();
}
}
然后定义接口文件,
public interface IGameMatch
{
int GetFree();
//注册连接服务器服务器
void ResgisterSuit(RpcInfo rpcInfo);
//移除连接服务器
void RemoveSuit(RpcInfo rpcInfo);
//移除连接服务器
void RemoveSuit(string rpcName);
//加入匹配
int Match_JoinMatch(int matchType, List<_Game_Match_RoleInfo> roleInfo);
//离开匹配
int Match_ExitMatch(List<_Game_Match_RoleInfo> roleInfo);
//匹配结果推送
void Match_MatchResultPush(List<_Game_Match_RoleInfo> roleInfo);
//是否加入战斗
void Match_IsJoin(string roleUuid,bool isJoin);
//移除匹配
void Match_RemoveRole(string roleUuid);
//英雄选择模块
//选择英雄
void SelHero_SelHero(_Game_Match_RoleInfo roleInfo);
//确认英雄
void SelHero_ConfirmHero(_Game_Match_RoleInfo roleInfo);
}
在服务器端创建借口实现类
using System;
using System.Collections.Generic;
using protos.GameProto;
using GameMatch.Server;
namespace GameMatch.Rpc
{
public class GameMatch : IGameMatch
{
public int GetFree()
{
return 0;
}
public void RemoveSuit(string rpcName)
{
RpcManager.RemoveRpc(rpcName);
}
public void RemoveSuit(RpcInfo rpcInfo)
{
RpcManager.RemoveRpc(rpcInfo);
}
public void ResgisterSuit(RpcInfo rpcInfo)
{
RpcManager.RegisterRpc(rpcInfo);
}
public int Match_ExitMatch(List<_Game_Match_RoleInfo> roleInfo)
{
return MathcCenter.ExitMatch(roleInfo);
}
public void Match_IsJoin(string roleUuid, bool isJoin)
{
MathcCenter.IsJoin(roleUuid,isJoin);
}
public int Match_JoinMatch(int matchType, List<_Game_Match_RoleInfo> roleInfo)
{
return MathcCenter.JoinMatch(matchType,roleInfo);
}
public void Match_MatchResultPush(List<_Game_Match_RoleInfo> roleInfo)
{
throw new NotImplementedException();
}
public void Match_RemoveRole(string roleUuid)
{
MathcCenter.RemoveRole(roleUuid);
}
/*********选择英雄******************/
public void SelHero_ConfirmHero(_Game_Match_RoleInfo roleInfo)
{
RoomCenter.ConfirmHero(roleInfo);
}
public void SelHero_SelHero(_Game_Match_RoleInfo roleInfo)
{
RoomCenter.SelHero(roleInfo);
}
}
}
在服务器启动的时候,开启服务器本身信道
以及连接其他服务器的信道
调用接口中的相应方法即可
运行结果:
Hprose中使用的方法
注册类,type是类型,alias是别名
public static void Register(Type type, string alias);
注册tcp服务器链接
HproseTcpListenerServer tcpServer = new HproseTcpListenerServer("url");
httpServer.Start(); //启动服务器
注册http服务器链接
HproseHttpListenerServer httpServer = new HproseHttpListenerServer(rcList[i].GetUrl);
httpServer.Start(); //启动服务器
添加实现类
HproseService.Add();
需要注意的是,Hprose获取List为List 格式,需要自己进行强转。
太复杂的功能我没有制作,但是在我自己写服务器中实现了通信(没测过外网)。
这是几个月前研究的,已经忘的差不多了。