权限管理系统系列之WCF通信
目录
首先说下题外话,有些园友看了前一篇【权限管理系统系列之序言】博客加了QQ群(186841119),看了我写的权限管理系统的相关文档(主要是介绍已经开发的功能),给出了一些建议,感觉非常好,希望后续有更多的园友能再接再厉给出更多的指导意见,在平常的开发中个人会结合你们的建议做出适当修改和完善,促进共同学习和进步。关于源码共享的问题,可能会过段时间公布,不会现在公开源码,个人还在不断完善中,等完成差不多后会公开源码。
客户端与服务器的通信在一个程序中会占住关键的作用,处理起来可能会有很多方式,比如说Remoting、Socket、WebServices、WCF等等都可以实现。本人这几种基本上都用过,Socket可能比较少些,一些聊天室的程序就会使用Socket,通过字节的形式接收数据;WebServices会WinCE开发中使用到,数据传输进行压缩,这样操作数据就比较方便,实时操作数据库;Remoting主要用在MIS系统的客户端与服务端通信,个人也说不出那种好;WCF也是我最近一两年才接触到的,公司现在使用的就是WCF通信的,个人感觉用WCF比较方便和简单,实用起来使用三个函数(一个函数是检测客户端与服务器端的心跳,一个是用于登录的、一个是公共的接口,基本上所有的客户端和服务端的通信都是用這个函数),這个函数可以搞定所有的客户端访问服务端的方法,所有的SQL在服务端执行,便于维护和日常的分工,不过在平常的开发中也不会分客户端和服务端的开发,基本上也是一个一个模块进行分工的。
WCF的配置(包括客户端和服务端)
客户端的配置文件:
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="TcpBinding_AppService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="10485760" maxBufferSize="10485760" maxConnections="10" maxReceivedMessageSize="10485760">
<readerQuotas maxDepth="32" maxStringContentLength="10485760" maxArrayLength="10485760" maxBytesPerRead="10485760" maxNameTableCharCount="10485760"/>
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false"/>
<security mode="None">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign"/>
<message clientCredentialType="Windows"/>
</security>
</binding> <binding name="TcpBinding_MessageService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="10485760" maxBufferSize="10485760" maxConnections="10" maxReceivedMessageSize="10485760">
<readerQuotas maxDepth="32" maxStringContentLength="10485760" maxArrayLength="10485760" maxBytesPerRead="10485760" maxNameTableCharCount="10485760"/>
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false"/>
<security mode="None">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign"/>
<message clientCredentialType="Windows"/>
</security>
</binding> </netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://localhost:9090/AppService" binding="netTcpBinding" bindingConfiguration="TcpBinding_AppService" contract="IAppService" name="TcpBinding_AppService"/> <endpoint address="net.tcp://localhost:7070/MessageService" binding="netTcpBinding" bindingConfiguration="TcpBinding_MessageService" contract="IMessageService" name="TcpBinding_MessageService"/>
</client>
</system.serviceModel>
</configuration>
服务端的配置文件:
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service behaviorConfiguration="Service.Behavior" name="Server.AppService">
<endpoint address="AppService" binding="netTcpBinding" bindingConfiguration="AppServiceBinding" name="TcpBinding_AppService" contract="Server.IAppService" />
<endpoint address="AppService/mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:9090" />
</baseAddresses>
</host>
</service>
<service behaviorConfiguration="Service.Behavior" name="Server.MessageService">
<endpoint address="MessageService" binding="netTcpBinding" bindingConfiguration="MessageServiceBinding" name="TcpBinding_MessageService" contract="Server.IMessageService" />
<endpoint address="MessageService/mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:7070" />
</baseAddresses>
</host>
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="AppServiceBinding" maxBufferSize="10485760" maxReceivedMessageSize="10485760">
<readerQuotas maxDepth="32" maxStringContentLength="10485760"
maxArrayLength="10485760" maxBytesPerRead="10485760" maxNameTableCharCount="10485760" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="None" />
</binding>
<binding name="MessageServiceBinding" maxBufferSize="10485760" maxReceivedMessageSize="10485760">
<readerQuotas maxDepth="32" maxStringContentLength="10485760"
maxArrayLength="10485760" maxBytesPerRead="10485760" maxNameTableCharCount="10485760" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="Service.Behavior">
<serviceMetadata />
<serviceDebug includeExceptionDetailInFaults="true" />
<!--会话最大数量(并发会话)-->
<serviceThrottling maxConcurrentSessions="100" />
<!--数据序列最大量-->
<dataContractSerializer maxItemsInObjectGraph="10485760" />
</behavior>
<behavior name="mexConfig">
<serviceDebug includeExceptionDetailInFaults="True" />
<serviceMetadata />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
以上为客户端与服务端的配置文件,有两个配置,一个为基本通信所用,一个为双工通信所用。
介绍完配置文件后再介绍实现函数:
[ServiceContract(Name = "IAppService", SessionMode = SessionMode.Allowed, Namespace = "http://tempuri.org/")]
public interface IAppService
{
//心跳
[OperationContract]
string HeartBeat(string echo); //登录
[OperationContract]
bool Login(string UserName, string Password); //统一的应用业务请求调用,用于会话控制和调用转发
[OperationContract]
Result AppCall(Request request);
} #region * 推送消息
[ServiceContract(CallbackContract = typeof(IPushClient))]
public interface IMessageService
{
[OperationContract]
void RegisterClient();
} public interface IPushClient
{
[OperationContract(IsOneWay = true)]
void SendMessage(string message);
}
#endregion
分别有三个函数,一个是维持服务端与客户端的心跳,一个为登陆所用,一个为所有函数的接口,基本上所有的通信都是通过这个函数进行调用。最下面的为双工通信的定义,定义成回调函数的形式。
以上函数实现逻辑:
//每个会话一个实例,同一个会话下的多线程并发
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class AppService : IAppService
{
//记录是否登录
bool IsLogined = false;
Context context = new Context(); private static readonly string _username = "Server";
private static readonly string _password = ""; //心跳请求,维持链路正常
public string HeartBeat(string echo)
{
Log.Log.Debug("收到请求:" + echo);
return "Re:" + echo;
} //用于登录
public bool Login(string UserName, string Password)
{
Log.Log.Debug("收到请求:" + UserName + ";" + Password);
if (_username.Equals(Encrypt.DecryptDES(UserName, Const.EncryptKey)) && _password.Equals(Encrypt.DecryptDES(Password, Const.EncryptKey)))
{
IsLogined = true;
context.UserName = UserName;
return IsLogined;
}
else
{
IsLogined = false;
context.UserName = "";
return IsLogined;
}
} //统一的业务请求调用代理
delegate Result ActionDelegate(string reqdata);
//统一的应用业务请求调用,用于会话控制和调用转发
public Result AppCall(Request request)
{
if (!string.IsNullOrEmpty(request.data))
{
if (request.data.Contains(JSON.CompressionFlag))
{
Log.Log.Debug("收到请求:" + Compression.DecompressString(request.data.Replace(JSON.CompressionFlag, JSON.ReplaceFlag)));
}
else
{
Log.Log.Debug("收到请求:" + request.data);
}
}
if (!IsLogined)
{
Result result = new Result() { success = false, errors = "用户未登录,请登录后再提交请求!" };
Log.Log.Info("Server.AppService.AppCall(Request request):" + JSON.Object2Json(result, false));
return result;
} if (request.action == null || request.method == null)
{
Result result = new Result() { success = false, errors = "请求数据格式不正确{request.action==null || request.method==null},请检查!" };
Log.Log.Error("Server.AppService.AppCall(Request request)出错:" + JSON.Object2Json(result, false));
return result; }
try
{
Type type = Type.GetType("Server.Action." + request.action);
object action = Activator.CreateInstance(type, new object[] { this.context });
ActionDelegate doAction = (ActionDelegate)Delegate.CreateDelegate(typeof(ActionDelegate), action, request.method);
Result result = doAction(request.data);
return result;
}
catch (Exception e)
{
Log.Log.Error("Server.AppService.AppCall(Request request)异常:" + e.ToString());
return new Result() { success = false, errors = "执行业务请求错误!" };
}
}
} #region * 推送消息
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class MessageService : IMessageService, IDisposable
{
public static List<IPushClient> ClientCallbackList { get; set; }
public MessageService()
{
ClientCallbackList = new List<IPushClient>();
} public void RegisterClient()
{
var client = OperationContext.Current.GetCallbackChannel<IPushClient>();
var id = OperationContext.Current.SessionId;
//Console.WriteLine("{0}registered.", id);
Log.Log.Info(string.Format("{0}registered.", id));
OperationContext.Current.Channel.Closing+=new EventHandler(Channel_Closing);
ClientCallbackList.Add(client);
} private void Channel_Closing(object sender, EventArgs e)
{
lock (ClientCallbackList)
{
ClientCallbackList.Remove((IPushClient)sender);
}
} public void Dispose()
{
ClientCallbackList.Clear();
}
}
#endregion
}
通信接口通过以上实体,success为函数执行状态,msg为函数返回的信息,data为返回的数据(格式为json格式的),errors为返回的错误信息,sql为函数执行的sql,返回给前台界面。
WCF服务生成客户端的配置文件步骤:
a.打开vs命令行,用cd进入到exe文件目录。
b.svcutil .exe
c.svcutil *.wsdl *.xsd
完成以上即可。
客户调用CS文件:
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.18444
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------ namespace Server.Domain
{
using System.Runtime.Serialization; [System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name = "Request", Namespace = "http://schemas.datacontract.org/2004/07/Server.Domain")]
public partial class Request : object, System.Runtime.Serialization.IExtensibleDataObject
{ private System.Runtime.Serialization.ExtensionDataObject extensionDataField; private string actionField; private string dataField; private string methodField; public System.Runtime.Serialization.ExtensionDataObject ExtensionData
{
get
{
return this.extensionDataField;
}
set
{
this.extensionDataField = value;
}
} [System.Runtime.Serialization.DataMemberAttribute()]
public string action
{
get
{
return this.actionField;
}
set
{
this.actionField = value;
}
} [System.Runtime.Serialization.DataMemberAttribute()]
public string data
{
get
{
return this.dataField;
}
set
{
this.dataField = value;
}
} [System.Runtime.Serialization.DataMemberAttribute()]
public string method
{
get
{
return this.methodField;
}
set
{
this.methodField = value;
}
}
} [System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name = "Result", Namespace = "http://schemas.datacontract.org/2004/07/Server.Domain")]
public partial class Result : object, System.Runtime.Serialization.IExtensibleDataObject
{ private System.Runtime.Serialization.ExtensionDataObject extensionDataField; private string dataField; private string errorsField; private string msgField; private string sqlField; private bool successField; public System.Runtime.Serialization.ExtensionDataObject ExtensionData
{
get
{
return this.extensionDataField;
}
set
{
this.extensionDataField = value;
}
} [System.Runtime.Serialization.DataMemberAttribute()]
public string data
{
get
{
return this.dataField;
}
set
{
this.dataField = value;
}
} [System.Runtime.Serialization.DataMemberAttribute()]
public string errors
{
get
{
return this.errorsField;
}
set
{
this.errorsField = value;
}
} [System.Runtime.Serialization.DataMemberAttribute()]
public string msg
{
get
{
return this.msgField;
}
set
{
this.msgField = value;
}
} [System.Runtime.Serialization.DataMemberAttribute()]
public string sql
{
get
{
return this.sqlField;
}
set
{
this.sqlField = value;
}
} [System.Runtime.Serialization.DataMemberAttribute()]
public bool success
{
get
{
return this.successField;
}
set
{
this.successField = value;
}
}
}
} [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName = "IAppService")]
public interface IAppService
{ [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IAppService/HeartBeat", ReplyAction = "http://tempuri.org/IAppService/HeartBeatResponse")]
string HeartBeat(string echo); [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IAppService/Login", ReplyAction = "http://tempuri.org/IAppService/LoginResponse")]
bool Login(string UserName, string Password); [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IAppService/AppCall", ReplyAction = "http://tempuri.org/IAppService/AppCallResponse")]
Server.Domain.Result AppCall(Server.Domain.Request request);
} [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public interface IAppServiceChannel : IAppService, System.ServiceModel.IClientChannel
{
} [System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public partial class AppServiceClient : System.ServiceModel.ClientBase<IAppService>, IAppService
{ public AppServiceClient()
{
} public AppServiceClient(string endpointConfigurationName) :
base(endpointConfigurationName)
{
} public AppServiceClient(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
} public AppServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
} public AppServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{
} public string HeartBeat(string echo)
{
return base.Channel.HeartBeat(echo);
} public bool Login(string UserName, string Password)
{
return base.Channel.Login(UserName, Password);
} public Server.Domain.Result AppCall(Server.Domain.Request request)
{
return base.Channel.AppCall(request);
}
} [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName = "IMessageService", CallbackContract = typeof(IMessageServiceCallback))]
public interface IMessageService
{ [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IMessageService/RegisterClient", ReplyAction = "http://tempuri.org/IMessageService/RegisterClientResponse")]
void RegisterClient();
} [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public interface IMessageServiceCallback
{ [System.ServiceModel.OperationContractAttribute(IsOneWay = true, Action = "http://tempuri.org/IMessageService/SendMessage")]
void SendMessage(string message);
} [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public interface IMessageServiceChannel : IMessageService, System.ServiceModel.IClientChannel
{
} [System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public partial class MessageServiceClient : System.ServiceModel.DuplexClientBase<IMessageService>, IMessageService
{ public MessageServiceClient(System.ServiceModel.InstanceContext callbackInstance) :
base(callbackInstance)
{
} public MessageServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName) :
base(callbackInstance, endpointConfigurationName)
{
} public MessageServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, string remoteAddress) :
base(callbackInstance, endpointConfigurationName, remoteAddress)
{
} public MessageServiceClient(System.ServiceModel.InstanceContext callbackInstance, string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(callbackInstance, endpointConfigurationName, remoteAddress)
{
} public MessageServiceClient(System.ServiceModel.InstanceContext callbackInstance, System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(callbackInstance, binding, remoteAddress)
{
} public void RegisterClient()
{
base.Channel.RegisterClient();
}
}
public static AppServiceClient AppService;
public static ServiceHearBeat HeartBeat;
/// <summary>
/// 连接是否正常
/// </summary>
public static bool IsAlive
{
get { return HeartBeat.IsAlive; }
}
/// <summary>
/// 是否已经弹出过提示
/// </summary>
public static bool HasNotice
{
get { return HeartBeat.HasNotice; }
set { HeartBeat.HasNotice = value; }
} //连接到应用服务
if (!ConnectToAppServer())
{
Comm.MessageBox.Info("连接服务端失败,请检查服务端是否已经启动。");
return;
} //连接到应用服务
public static bool ConnectToAppServer()
{
try
{
//如果服务存在,则尝试关闭链接
if (AppService != null)
{
try
{
AppService.Close();
}
catch (Exception)
{
}
}
//创建新的服务对象,并进行连接和登录
AppService = new AppServiceClient();
AppService.Open();
if (!AppClient.AppService.Login(Encrypt.EncryptDES("Server", Const.EncryptKey), Encrypt.EncryptDES("", Const.EncryptKey)))
{
return false;
}
return true;
}
catch (EndpointNotFoundException)
{
Log.Error("连接服务端失败,服务端IP端口配置错误或者是服务端尚未启动");
}
catch (SocketException)
{
Log.Error("连接服务端失败,服务端IP端口配置错误或者是服务端尚未启动");
}
catch (Exception e)
{
Log.Error("连接服务端出错:" + e.ToString());
}
return false; }
以上即可完成对客户端连接服务端了,基本上完成以后步骤可以说完成了WCF的通信,实现了客户端连接服务端。
服务端打开的效果:
客户端打开的效果:
如对权限管理系统有兴趣可加QQ群:186841119,可参与相关话题讨论。相互学习交流,共同进步。
权限管理系统系列之WCF通信的更多相关文章
- 以WCF安全认证方式调用通用权限管理系统获取基础信息资料
在B/S开发中,涉及到获取系统基础资料的问题,因为是在不同平台下的开发,采用了WCF方式获取. 下面是一个调用通用权限管理系统(吉日嘎拉)基础信息资料的一个demo供参考 调用原理图: web.con ...
- 系列文章--Silverlight与WCF通信
Silverlight与WCF通信(一) :Silverlight通过httpBinding访问IIS宿主WCF 摘要: 首语本人在学习Silverlight 和 WCF的时候,各种问题层出不穷,在园 ...
- Asp.Net Core 项目实战之权限管理系统(4) 依赖注入、仓储、服务的多项目分层实现
0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...
- [Django]用户权限学习系列之设计自有权限管理系统设计思路
若在阅读本片文章遇到权限操作问题,请查看本系列的前两章! http://www.cnblogs.com/CQ-LQJ/p/5609690.html和http://www.cnblogs.com/CQ- ...
- 分享一个html+js+ashx+easyui+ado.net权限管理系统
EasyUI.权限管理 这是个都快被搞烂了的组合,但是easyui的确好用,权限管理在项目中的确实用.一直以来博客园里也不少朋友分享过,但是感觉好的要不没源码,要不就是过度设计写的太复杂看不懂,也懒得 ...
- MVC + EF + Bootstrap 2 权限管理系统入门级(附源码)
MVC .EF 学习有大半年了,用的还不是很熟练,正好以做这样一个简单的权限管理系统作为学习的切入点,还是非常合适的. 开发环境: VS 2013 + Git + MVC 5 + EF 6 Code ...
- Angularjs,WebAPI 搭建一个简易权限管理系统
Angularjs,WebAPI 搭建一个简易权限管理系统 Angularjs名词与概念(一) 1. 目录 前言 Angularjs名词与概念 权限系统原型 权限系统业务 数据库设计和实现 Web ...
- ASP.NET MVC+EF框架+EasyUI实现权限管理系列(14)-主框架搭建
原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(14)-主框架搭建 ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇) (1):框架搭建 (2 ...
- 开篇ASP.NET MVC 权限管理系列
开篇 [快乐编程系列之ASP.NET MVC 权限管理系列]一.开篇 用了好长一段时间的ASP.NET MVC,感觉MVC真的是很好用,最近一年左右做了两个中小型项目,觉得还是很多地方不是很熟悉的 ...
随机推荐
- rabbitMQ 常用命令
启动监控管理器:rabbitmq-plugins enable rabbitmq_management 关闭监控管理器:rabbitmq-plugins disable rabbitmq_manage ...
- Gradle with Android
[Gradle with Android] The Android Studio build system is based on Gradle, and the Android plugin for ...
- jQuery源码解读三选择器
直接上jQuery源码截取代码 // Map over jQuery in case of overwrite _jQuery = window.jQuery, // Map over the $ i ...
- float数据类型研究,发现其能显示的有效数字极为有限
1. 范围 float和double的范围是由指数的位数来决定的. float的指数位有8位,而double的指数位有11位,分布如下: float: 1bit(符号位) 8bits(指数位) ...
- leetcdoe 175. Combine Two Tables
给定两个表,一个是人,一个是地址,要求查询所有人,可以没有地址. select a.FirstName, a.LastName, b.City, b.State from Person as a le ...
- ios - 设置一个VC的navigationController的显示图案或文字,其他navigationController依旧不变
1. override func viewDidLoad() { super.viewDidLoad() self.navigationController?.delegate = self } 2. ...
- [leetcode]117. Populating Next Right Pointers in Each NodeII用next填充同层相邻节点
Given a binary tree struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode *nex ...
- 9-最短路径(dijkstra)
参考博客:http://www.wutianqi.com/?p=1890 #include <iostream>using namespace std;#define max 1< ...
- struts工作原理(图解)
Struts2框架的工作原理: 1.服务器启动,会加载我们的xml配置文件中的内容. 2.服务器启动之后,过来一个servlet请求,如user类中的save方法.请求过来先过过滤器(strutsPr ...
- 3D文件压缩库——Draco简析
3D文件压缩库——Draco简析 今年1月份时,google发布了名为“Draco”的3D图形开源压缩库,下载了其代码来看了下,感觉虽然暂时用不到,但还是有前途的,故简单做下分析. 注:Draco 代 ...