c#发展

首页 » 常识 » 诊断 » C之RPC很火么
TUhjnbcbe - 2021/3/16 17:22:00

(给DotNet加星标,提升.Net技能)

转自:Supper_littcnblogs.
  
  
  //0:void忽略1:int2:bool3:stringintarg_type=br.ReadByte();if(arg_type==1){byte[]values=br.ReadBytes(4);list.Add(bytes2int(values));}elseif(arg_type==2){boolvalue=br.ReadByte()==1;list.Add(value);}elseif(arg_type==3){intstr_len=bytes2int(br.ReadBytes(4));stringstr=Encoding.UTF8.GetString(br.ReadBytes(str_len));list.Add(str);}}Typeinter_type=null;vartypes=Assembly.GetExecutingAssembly().GetTypes();foreach(vartypeintypes){varts=type.GetInterfaces();foreach(vartints){if(t.Name==inter_name){inter_type=type;break;}}}MethodInfoinvokeMethod=null;if(inter_type!=null){varmethods=inter_type.GetMethods();foreach(varmethodinmethods){if(method.Name==method_name){invokeMethod=method;break;}}}if(invokeMethod!=null){ObjectthisObj=Activator.CreateInstance(inter_type);objectresult=invokeMethod.Invoke(thisObj,list.ToArray());if(return_type==1){intvalue=Convert.ToInt32(result);reurnint2bytes(value);}elseif(return_type==2){returnnewbyte[1]{Convert.ToBoolean(result)?(byte)1:(byte)0};}elseif(return_type==2){Listbyteresult_data=newListbyte();varstr=(result==null?"":result.ToString());vardata=Encoding.UTF8.GetBytes(str);result_data.AddRange(int2bytes(data.Length));result_data.AddRange(data);returnresult_data.ToArray();}}returnnewbyte[1]{0xFF};}publicstaticbyte[]int2bytes(intlen){byte[]data_len=newbyte[4];data_len[0]=(byte)((len8*3)0xFF);data_len[1]=(byte)((len8*2)0xFF);data_len[2]=(byte)((len8*1)0xFF);data_len[3]=(byte)(len0xFF);returndata_len;}publicstaticintbytes2int(byte[]buffer){intvalue=0;value+=(int)(buffer[0](8*3));value+=(int)(buffer[1](8*2));value+=(int)(buffer[2](8*1));value+=(int)(buffer[3]);returnvalue;}}

解析的类很简单,因为这里创建的数据结构很简单。

按照我们的约定,这里,对数据按照我定义的方式来进行解包即可。

服务器就完成了,是不是很简单。当然客户端也需要按照一样的方式处理打包即可

客户端

客户端就很简单了,只需要连接到服务器,通过我们自动生成的代码(这里没有写自动生成,就手动了),然后就直接可以返回结果了

classProgram{staticvoidMain(string[]args){IMyServiceservice=newMyServiceProxy();DateTimestartTime=DateTime.Now;intresult=service.add(,);intmin_seconds=(int)(DateTime.Now-startTime).TotalMilliseconds;Console.WriteLine(result+"耗时"+min_seconds);Console.ReadLine();}}

上面直接调用了,接口,至于接口的实现,这里的步骤就三个:1、构造需要请求的数据,2、连接服务器并发送数据,3、接收返回内容,并解析结果。

publicclassMyServiceProxy:IMyService{publicintadd(intx,inty){ListArgInfoargList=newListArgInfo();argList.Add(newArgInfo(TypeEnu.Int,x));argList.Add(newArgInfo(TypeEnu.Int,y));byte[]send_data=create_send_package("IMyService","add",2,TypeEnu.Int,argList);Socketclient=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);client.Connect(newIPEndPoint(IPAddress.Parse("..0."),));client.Send(send_data);byte[]buffer=newbyte[4];intcount=client.Receive(buffer);if(count0){returnbytes2int(buffer);}thrownewException("系统异常");}publicboollogin(stringname,stringpwd){ListArgInfoargList=newListArgInfo();argList.Add(newArgInfo(TypeEnu.String,name));argList.Add(newArgInfo(TypeEnu.String,pwd));byte[]send_data=create_send_package("IMyService","login",2,TypeEnu.Bool,argList);Socketclient=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);client.Connect(newIPEndPoint(IPAddress.Parse("..0."),));client.Send(send_data);byte[]buffer=newbyte[1];intcount=client.Receive(buffer);if(count0){returnbuffer[0]==1;}thrownewException("系统异常");}privatebyte[]create_send_package(stringinter_name,stringmethod_name,intarg_length,TypeEnureturn_type,ListArgInfoargList){Listbytelist=newListbyte();list.Add((byte)inter_name.Length);list.AddRange(Encoding.UTF8.GetBytes(inter_name));list.Add((byte)method_name.Length);list.AddRange(Encoding.UTF8.GetBytes(method_name));list.Add((byte)arg_length);list.Add((byte)return_type);foreach(vararginargList){list.Add((byte)arg.type);if(arg.type==TypeEnu.Int){list.AddRange(int2bytes(Convert.ToInt32(arg.value)));}elseif(arg.type==TypeEnu.Bool){boolvalue=Convert.ToBoolean(arg.value);list.Add(value?(byte)1:(byte)0);}elseif(arg.type==TypeEnu.String){stringvalue=arg.value.ToString();list.AddRange(int2bytes(value.Length));list.AddRange(Encoding.UTF8.GetBytes(value));}}returnlist.ToArray();}publicbyte[]int2bytes(intlen){byte[]data_len=newbyte[4];data_len[0]=(byte)((len8*3)0xFF);data_len[1]=(byte)((len8*2)0xFF);data_len[2]=(byte)((len8*1)0xFF);data_len[3]=(byte)(len0xFF);returndata_len;}publicintbytes2int(byte[]buffer){intvalue=0;value+=(int)(buffer[0](8*3));value+=(int)(buffer[1](8*2));value+=(int)(buffer[2](8*1));value+=(int)(buffer[3]);returnvalue;}}publicclassArgInfo{publicTypeEnutype{get;set;}publicobjectvalue{get;set;}publicArgInfo(TypeEnutype,objectvalue){this.type=type;this.value=value;}}publicenumTypeEnu{Void=0,Int=1,Bool=2,String=3}

接口的定义沿用服务端的即可。说明一点:MyServiceProxy这个类,这里我是手写的,真实的环境,这个类,应该是由我们定义的某种格式,然后写一个代码生成器,让他自动生成,然后就可以不用费力,兼容所有的调用了,

当然这里只支持了四种类型,我们还可以扩充更多类型,只需要找到传递数据的方式即可。譬如一种对象,我们不知道如何传递,可以直接把对象定义成一个json字符串,或者序列化成二进制,只要两端,都知道了这个类型,就可以了。

相当于设计模式里面的(约定大于配置了)

知识点梳理

这里有一些知识点,是不常用的,这里梳理出来了。

1、MemoryStreamms=newMemoryStream(buffer);BinaryReaderbr=newBinaryReader(ms);通过binaryReader的方式,可以像C/C++指针一样取数据

2、vartypes=Assembly.GetExecutingAssembly().GetTypes();通过Assembly可以得到当前exe或者dll的所有类型(类接口都是一种类型)

3、ObjectthisObj=Activator.CreateInstance(inter_type);通过Activator调用默认构造,实现对象的初始化

总结

这样一个rpc框架,本身并没有优化,还有很多地方是可以优化的,比如:缓存(不用每次遍历查询类型等),udp支持(这里仅仅只是对tcp进行了支持),

自动代码生成(定义一种规范和支持程序,进行支持),错误重试,数据唯一性,数据包的大小处理,等等,所以想要开发一个易用的框架,还需要不断演进,这里只是对他的原理进行了简单剖析。

代码git:

1
查看完整版本: C之RPC很火么