(给DotNet加星标,提升.Net技能)
转自:Supper_littcnblogs.解析的类很简单,因为这里创建的数据结构很简单。
按照我们的约定,这里,对数据按照我定义的方式来进行解包即可。
服务器就完成了,是不是很简单。当然客户端也需要按照一样的方式处理打包即可
客户端
客户端就很简单了,只需要连接到服务器,通过我们自动生成的代码(这里没有写自动生成,就手动了),然后就直接可以返回结果了
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: