C#不用union,而是有更好的方式实现
用过C/C++的人都知道有个union,特别好用,似乎char数组到short,int,float等的转换无所不能,也确实是能,并且用起来十分方便。
那C#为什么没有这个关键字呢?怎么实现这个功能?其实C#只是没有了这个关键字,但是功能是能实现的,而且也是非常方便,并且是安全的。
网上有人用StructLayout特性来实现union,也确实是实现了一些功能。
比如:
C/C++:
union {
unsigned char ch
short ;
int i;
};
C#:
[StructLayout(LayoutKind.Explicit)]
public struct Class1
{
[FieldOffset(0)]
public byte b;
[FieldOffset(0)]
public short s;
[FieldOffset(0)]
public int i;
}
就可以实现。
但是我要是写个:
union {
unsigned char ch[4];
int i;
float f;
} temp;
硬是用C#没有模拟出来,估计我还没有找着合适的方法。因为我写
[StructLayout(LayoutKind.Explicit)]
public struct Class1
{
[FieldOffset(0)]
public byte[4] b;
[FieldOffset(0)]
public short s;
[FieldOffset(0)]
public int i;
}
这玩意是编译不通过的。然后折腾了半天,没有折腾出来。后来又回到C/C++想了一番,似乎有些认识。
C/C++用union其实就是使用同一块内存存储不同类型的数据,说白了,就是一块公用的内存,你用啥读取出来就是啥内容。其实计算机中的内存本身也就是这样,你定义一个int i;然后计算机会在内存栈上开辟一块空间,并且这块内存指明了是int类型,但是我们经常看到(int)data,(int*)pt等操作,说明可以强制转换。强制转换不是说把这几块内存的值改变了,只是临时改变了读取方式,然后用这种方式读取这块内存。那这样说来是不是也可以不用union来实现char数组与其他类型之间的转换,答案是必须可以。
比如:
unsigned char chArr[4] = "";
float f1 = 45.56f;
memcpy(chArr, &f1, sizeof(float));
// 运行结果:113 61 54 66
printf("%d\t%d\t%d\t%d\n", chArr[0], chArr[1], chArr[2], chArr[3]);
float f2 = 0.00f;
memcpy(&f2, chArr, sizeof(float));
printf("%0.2f\n", f2);
float f3 = *(float *)chArr;
printf("%0.2f\n", f3);
char *pch = (char *)&f3;
// 运行结果:113 61 54 66
printf("%d\t%d\t%d\t%d\n", pch[0], pch[1], pch[2], pch[3]);
那好问题来了,C#怎么实现?
那好,答案也来了。当然是用BitConvert。
比如:
float f = 45.56f;
byte[] b = BitConverter.GetBytes(f);
Console.WriteLine("bArr\t: {0}\t{1}\t{2}\t{3}", b[0], b[1], b[2], b[3]);
float f2 = BitConverter.ToSingle(b, 0);
Console.WriteLine("f2\t: {0}", f2);
完全木有问题啊,而且还安全。
最后呢,咱们看看微软是怎么给咱实现的。
// Converts a float into an array of bytes with length
// four.
[System.Security.SecuritySafeCritical] // auto-generated
public unsafe static byte[] GetBytes(float value)
{
Contract.Ensures(Contract.Result<byte[]>() != null);
Contract.Ensures(Contract.Result<byte[]>().Length == 4);
return GetBytes(*(int*)&value);
}
...
// Converts an int into an array of bytes with length
// four.
[System.Security.SecuritySafeCritical] // auto-generated
public unsafe static byte[] GetBytes(int value)
{
Contract.Ensures(Contract.Result<byte[]>() != null);
Contract.Ensures(Contract.Result<byte[]>().Length == 4);
byte[] bytes = new byte[4];
fixed(byte* b = bytes)
*((int*)b) = value;
return bytes;
}
看见了吗?是不是跟上面的C/C++代码很像。其实就是C/C++代码。如果你看不到这段代码,也许你还真不知道,原来以前自己的C/C++代码被搬到了这里。但是微软的公司的代码可不是我写的C/C++那么简单的转换,微软程序员是做了安全检查的。你如果将3个byte的数组转换到float,那对不起,玩不了,你得补一个字节。
好了,给大家附上微软C#开源的源代码地址:
https://referencesource.microsoft.com/#mscorlib/system/bitconverter.cs,9108fa2d0b37805b
.net自定义错误页面实现
前言:
在实际的web开发中,经常会遇到以下情况,导致给用不好的体验:
a、程序未处理的异常,直接输出显示到用户页面
b、用户访问的资源不存在,直接显示系统默认的404页面
c、其它以下请求错误状态的系统默认页面(403等)
为了给用户友好的体验,在实际项目开发中,
需要对系统会不同的异常定制相应的友好提示页面
.net中自定义异常页面的重定向都是通过web.config配置页面配置实现,
其具体的实现方式有两种方式:
其一、通过节点system.web新增customErrors配置节点实现
IIS环境需求:IIS7、IIS7+、IIS7以前版本
作用对象:作用于Asp.Net级别的错误处理程序
也就是说,对于(.html/.js)等静态资源不起作用
实现方式:
<system.web> <customErrors mode="On" defaultRedirect="ApplicationErroy.aspx"> <error statusCode="403" redirect="/ErrorPage/403.html"/> <error statusCode="404" redirect="/ErrorPage/404.html"/> <error statusCode="500" redirect="/ErrorPage/500.html"/> </customErrors> </system.web>
其二、通过节点system.webServer新增httpErrors配置节点实现
IIS环境需求:IIS7、IIS7+
作用对象:作用于IIS级别的错误信息处理程序
也就是说,asp.net程序异常和静态资源异常都处理
实现方式:
<system.webServer> <httpErrors errorMode="Custom" existingResponse="Replace"> <clear /> <error statusCode="404" responseMode="ExecuteURL" path="/ErrorPage/404.html" /> <error statusCode="403" responseMode="ExecuteURL" path="/ErrorPage/403.html" /> <error statusCode="500" responseMode="Redirect" path="/ErrorPage/500.html" /> </httpErrors> </system.webServer>
应用总结:
通过上面的描述,在实际开发中,只要环境时IIS7+,那么完全采用第二种方式即可
.net自定义错误页面实现升级篇
问题描述:
在上一篇博文 “.net自定义错误页面实现” 中已经介绍了在.net中如何实现自定义错误页面实现(有需要者可以去上一篇博文了解),单纯按照上一篇博文那样设置,能够实现所有请求的异常自定义跳转,但是这样又会产生一个问题:当通过ajax提交请求获取接口提交请求,如果出现未处理的异常也会被重定向到自定义错误页面。
针对ajax请求或者接口请求,这样返回一个重定向页面,用户体验显然不是太友好,针对这个问题,下面简单总结一下我自己的想法和解决方案,当然不一定科学和合理,所以也希望有大牛多多指点。
解决思路,我想到的有二:
解决方案一:
从物理结构上分割,将web项目严格分割成两个项目(当然可根据需要继续细分):网站(只有网站页面资源等内容)、接口(包括网站的所有数据逻辑处理,页面的数据请求交互都是直接同接口交互(js技术)),
只是网站项目按照上一篇博文方式设置自定义错误页面方式,这样是能够解决问题,项目也会更加的清晰,也有很多公司的项目就是按照这种方式(尤其是webApp),
但是在实际项目中,很多项目是没有达到这种严格区分的,所以下面的解决方案二,将介绍一个更通用的方式
解决方法二:
解决思路是:将上一篇博文 .net自定义错误页面实现 与 上上一篇博文 .net捕捉全局未处理异常的3种方式 结合使用,并在实际开发中严格约定(出了url地址请求以外的其他请求都通过post请求实现交互),在扑捉到异常时,如果是post请求,处理异常,并清除异常。具体以步骤如下:
第一步:定义一个请求处理结果数据MODEL,代码如下:
/// <summary> /// 请求结果MRequestResult /// </summary> public class MRequestResult { /// <summary> /// 请求结果编码(是一个枚举值) /// </summary> private RequestResultCodeEnum requestResultCode; /// <summary> /// 处理结果具体的返回值 /// </summary> private object resultValue; /// <summary> /// 请求结果编码(是一个枚举值) /// </summary> public RequestResultCodeEnum RequestResultCode { get { return this.requestResultCode; } set { this.requestResultCode = value; } } /// <summary> /// 处理结果具体的返回值 /// </summary> public object ResultValue { get { return this.resultValue; } set { this.resultValue = value; } } } /// <summary> /// 请求结果编码枚举值() /// </summary> public enum RequestResultCodeEnum { /// <summary> /// 请求成功 /// </summary> Success = 1, /// <summary> /// 请求失败 /// </summary> Fail = -1, }
第二步:按照 上一篇博文: .net自定义错误页面实现的步骤,配置好自定义错误页面相关配置操作
第三步:按照 上上一篇博文:.net捕捉全局未处理异常的3种方式 的步骤实现全局异常为处理相关操作设置
第四步:在扑捉全局未处理的异常中,添加上针对post请求的异常处理过滤(直接输入封装后的),具体代码如下:
/// <summary> /// 异常处理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public void context_Error(object sender, EventArgs e) { //此处处理异常 HttpContext ctx = HttpContext.Current; HttpResponse response = ctx.Response; HttpRequest request = ctx.Request; //获取到HttpUnhandledException异常,这个异常包含一个实际出现的异常 Exception ex = ctx.Server.GetLastError(); //实际发生的异常 Exception iex = ex.InnerException; //// 异常日志落地 //// 异常日志落地包括:记录异常文本文件日志、或者记录异常日志数据库等等(根据实际项目需要做记录) //// 获取请求方法 string httpMethod = request.HttpMethod; //// 如果是POST请求,那么是以下请求之一 //// ajax接口请求 //// form表单提交 //// 这种情况的异常,不用跳转至自已的异常错误页面,直接返回对应系统异常 if (httpMethod.ToUpper() == "POST") { //// 构建返回对象值 MRequestResult requestResultM = new MRequestResult(); requestResultM.RequestResultCode = RequestResultCodeEnum.Fail; requestResultM.ResultValue = "系统异常,请联系管理员!"; response.Write(JsonConvert.SerializeObject(requestResultM, Formatting.Indented)); ctx.Server.ClearError();//处理完及时清理异常 } }
代码实例:
ajax请求方法及其逻辑处理实例代码:
$(function () { $.ajax({ async: true, type: "post", url: "../ActionTestResult/ContentResultTest", data: "name=xu", success: function (resultValue) { if (resultValue) { //// 解析处理结果 var resultObj = $.parseJSON(resultValue); //// 当RequestResultCode==1 说明该请求成功 ////(备注:并不代表处理成功,具体是否处理成功需要通过ResultValue的值根据接口约定解析做相应的逻辑处理) if (resultObj["RequestResultCode"] == 1) { //// 自定义请求成功逻辑处理 //// 通过解析具体的ResultValue的值做相应的逻辑处理..... if (resultObj["ResultValue"]) { var doResult = resultObj["ResultValue"].split('^'); if (doResult && doResult.length > 1) { if (doResult[0] == 1) { //// 说明处理成功,做相应的逻辑处理 alert("处理成功!"); } else { //// 处理失败 alert(doResult[1]); } } else { alert("操作失败!"); } } else { alert("未知结果"); } } else { //// 说明请求异常 //// 自定义逻辑处理 alert(resultObj["ResultValue"]); } } else { //// 自定义逻辑处理 alert("操作失败!"); } console.log(resultValue); }, error: function (data) { //// 自定义逻辑处理 alert("系统异常,请联系管理员。电话:8888888"); console.log(data); } }); });
ajax对应的后台请求接受实例代码:
/// <summary> /// 测试 /// </summary> /// <returns></returns> public ContentResult ContentResultTest(string name) { //// 构建请求处理结果Model MRequestResult requestResultM = new MRequestResult() { RequestResultCode = RequestResultCodeEnum.Success }; //// 业务处理结果 string doResult = string.Empty; //// 本次自作简单的参数非空判断,只一个示例 //// 处理结果本例中也只是通过^链接表示,在实际处理过程中,可以将结果通过一个Json字符串 if (string.IsNullOrEmpty(name)) { doResult = "-1^操作失败:名称不能为空!"; } else { ///// 其他自定义业务逻辑处理,此处省略..... doResult = "1^操作成功"; } requestResultM.ResultValue = doResult; //// 返回结果 return Content(JsonConvert.SerializeObject(requestResultM, Formatting.Indented)); }
是不是觉得说的有点绕,本人表述能力有限,不懂的评论交流。
最后:个人能力有限也许该解决方式并不友好,有更好的解决方法,也欢迎留言交流,多多指点,多多指教
.net捕捉全局未处理异常的3种方式
前言:
我们在实际项目开发中,经常会遇到一些不可预见的异常产生,有的异常在程序运行时就对其进行处理(try)
但是,有的程序不需要每一个地方都用try进行处理,那么针对这种情况,可以参照下面的方式,实现对异常的统一抓取处理
方式一、Page_Error处理页面级未处理异常
作用域:当前的.aspx页面
描述:在需要处理的aspx页面的cs文件中,实现Page_Error方法,达到侦听当前页面未处理的异常
1
2
3
4
5
6
7
8
9
10
11
|
protected void Page_Error( object sender, EventArgs e)
{
string errorMsg = String.Empty;
Exception currentError = Server.GetLastError();
errorMsg += "来自页面的异常处理<br />" ;
errorMsg += "系统发生错误:<br />" ;
errorMsg += "错误地址:" + Request.Url + "<br />" ;
errorMsg += "错误信息:" + currentError.Message + "<br />" ;
Response.Write(errorMsg);
Server.ClearError(); //清除异常(否则将引发全局的Application_Error事件)
}
|
方式二、通过HttpModule来捕获未处理的异常
作用域:全局的request请求
描述:通过一个类实现IHttpModule接口,并侦听未经处理的异常
实现步骤:
1、首先需要新建一个类(MyHttpModule),该类需实现IHttpModule接口,具体代码实例如下:
/// <summary> /// MyHttpModule /// </summary> public class MyHttpModules : IHttpModule { public void Init(HttpApplication context) { context.Error += new EventHandler(context_Error); } public void context_Error(object sender, EventArgs e) { //此处处理异常 HttpContext ctx = HttpContext.Current; HttpResponse response = ctx.Response; HttpRequest request = ctx.Request; //获取到HttpUnhandledException异常,这个异常包含一个实际出现的异常 Exception ex = ctx.Server.GetLastError(); //实际发生的异常 Exception iex = ex.InnerException; response.Write("来自ErrorModule的错误处理<br />"); response.Write(iex.Message); ctx.Server.ClearError(); } }
2、配置文件配置相应的HttpModule节点
配置文件配置HttpModule节点时,有以下两种方式(根据IIS版本而异)
方法1、当IIS版本为7.0以下时,在<system.web>中新增如下配置节点
<httpModules> <add name="MyHttpModule" type="MyHttpModule.MyHttpModules,MyHttpModule" /> </httpModules>
方法2、当IIS版本为7.0及其以上版本时,在<system.webServer>中新增如下配置节点
<modules> <add name="MyHttpModule" type="MyHttpModule.MyHttpModules,MyHttpModule"/> </modules>
方式三、通过Global中捕获未处理的异常
作用域:全局的request请求
描述:通过在Global中实现Application_Error方法,来达到侦听未经处理的异常
具体代码如下:
void Application_Error(object sender, EventArgs e) { //获取到HttpUnhandledException异常,这个异常包含一个实际出现的异常 Exception ex = Server.GetLastError(); //实际发生的异常 Exception iex = ex.InnerException; string errorMsg = String.Empty; string particular = String.Empty; if (iex != null) { errorMsg = iex.Message; particular = iex.StackTrace; } else { errorMsg = ex.Message; particular = ex.StackTrace; } HttpContext.Current.Response.Write("来自Global的错误处理<br />"); HttpContext.Current.Response.Write(errorMsg); Server.ClearError();//处理完及时清理异常 }
有关三种异常抓取的总结分析:
以上三种方法根据IIS处理请求的顺序,可以知道,其三者触发的先后顺序是:
方式一、Page_Error处理页面级未处理异常--抓取后未清理异常(ClearError)-->
方式二、通过HttpModule来捕获未处理的异常--抓取后未清理异常(ClearError)-->
方式三、通过Global中捕获未处理的异常
三种方式的作用范围是:方式一中作用于当前的aspx页面,方法二和方式三都作用于全局
通过上面的两点,所以在实际使用中,如果是抓取全局的未出来的异常建议采用方式二
如果是抓取某一页面的未处理的异常采用方式一
异常抓取后的处理逻辑总结:
按照上面的三种方式抓取到的程序中未处理的异常后,那么在实际的项目中,具体处理方式如何呢?根据自己实际的项目应用总结如下:
{
一般处理逻辑分三步:
第一步:解析具体的异常信息
第二步:解析后的异常信息落地入库(文本日志(方便后续查问题)
、数据库日志(方便后续查问题,统计提醒))
第三步:页面重定向(将错误重定向到定制的具体自定义错误页面)
重定向时,不需要在异常抓取的时候处理,可通过配置文件实现灵活处理(具体实现方式,见下一篇)
在异常抓取后,不要调用ClearError()来清除异常,否则配置文件设置的错误重定向将不起作用
}
一款很不错的FLASH时种插件
直接贴一段代码上来,大家看看效果:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<div style="width: 300px; height: 100px; margin-top: 300px; margin-left: 200px;">
<object onmousedown="fun1" id="objID" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0"
width="160" height="70" id="honehoneclock" align="middle">
<param name="allowScriptAccess" value="always">
<param name="movie" value="http://chabudai.sakura.ne.jp/blogparts/honehoneclock/honehone_clock_wh.swf">
<param name="quality" value="high">
<param name="bgcolor" value="#ffffff">
<param name="wmode" value="transparent">
<embed wmode="transparent" src="http://chabudai.sakura.ne.jp/blogparts/honehoneclock/honehone_clock_wh.swf"
quality="high" bgcolor="#000000" width="640" height="280" name="honehoneclock"
align="middle" allowscriptaccess="always" type="application/x-shockwave-flash"
pluginspage="http://www.macromedia.com/go/getflashplayer">
</object>
</div>
</body>
</html>
大家看以后,知道这个插件是哪儿来的吗?在博客园发现了什么呢?嘿嘿
关于c#中委托使用小结
一、简述:
委托对与我们编程人员来说,一点都不陌生,在实际工作过程中,或多或少都应该是接触过
但是对与编程新手来说,对与委托的理解和使用应该还是一个坎,但是只要理解清楚了,这个坎也就过去了。
最近也经常有人在问关于委托的使用的相关问题,在此我花点时间,对与委托的个人理解进行一个总结,希望能够给大家有所帮助作用。
二、定义:
委托,其实在我们日常生活中,也经常用到委托的,比如:律师其实就是充当了一个委托的角色,当事人委托律师在法庭上对其诉讼辩护。
在c#中,委托简单的理解就是将一个方法以参数的形式传递到另外一个方法中去,有点类似于c++中的指针的概念。
三、实现委托的步骤:
在委托定义的时候,我们只需要定义一个结构体,至于具体的实现,需要在具体的委托方法中来实现。
实现委托的步骤,我总结如下:
其一、定义:要声明一个委托的结构体:delegate void Mydelegate(type1 para1,type2 para2);
其二、声明:声明就是要声明一个委托变量:Mydelegate myDel;
其三、实例化:就是要对声明的委托对象实例化具体的委托方法:myDel=new Mydelegate(obj.InstanceMethod);
其四、参数传递:简单的理解,就是将委托以一个参数的形式传递给一个方法:MyMethod(myDel);
其五、委托方法执行:这个和普通方法实现调用一样,就是在方法MyMethod里面调用执行委托的方法:myDel(obj.InstanceMethod)
四、使用
委托的使用,根据平时的开经验,委托其实在实际的使用中,有两种场景:
其一、同步使用:同步执行委托,阻塞主流程(和平时的方法执行效果一样)
namespace MyCalculat { /// <summary> /// 定义一个委托 /// </summary> /// <param name="num1">阐述1</param> /// <param name="num2">参数2</param> /// <returns>处理结果</returns> public delegate int MyDelegate(int num1, int num2); /// <summary> /// Calculat类 /// </summary> public class Calculat { public int Calcul(int num1,int num2,int type ) { MyDelegate myDelegate; switch (type) { case 1: myDelegate = new MyDelegate(this.Add); break; case 2: myDelegate = new MyDelegate(this.Reduce); break; //// //// 其他逻辑 //// default: myDelegate = new MyDelegate(this.Add); break; } return 0; } /// <summary> /// 计算具体逻辑处理 /// </summary> /// <param name="calDel">MyDelegate计算委托变量</param> /// <param name="num1">参数1</param> /// <param name="num2">参数2</param> /// <returns>计算处理结果</returns> public int CalculOpert(MyDelegate calDel, int num1, int num2) { //// //// 其他业务逻辑处理 //// //// 委托调用处理--同步调用 return calDel.Invoke(num1,num2); } /// <summary> /// 相加 /// </summary> /// <param name="num1">参数1</param> /// <param name="num2">参数2</param> /// <returns>结果</returns> public int Add(int num1, int num2) { return num1 + num2; } /// <summary> /// 相法 /// </summary> /// <param name="num1">参数1</param> /// <param name="num2">参数2</param> /// <returns>结果</returns> public int Reduce(int num1, int num2) { return num1 - num2; } } }
其二、异步使用:不阻塞主流程的执行,异步线程去执行委托方法里面的逻辑
namespace MyCalculat { /// <summary> /// 定义一个委托 /// </summary> /// <param name="num1">阐述1</param> /// <param name="num2">参数2</param> /// <returns>处理结果</returns> public delegate int MyDelegate(int num1, int num2); /// <summary> /// Calculat类 /// </summary> public class AcyCalculat { public int Calcul(int num1,int num2,int type ) { MyDelegate myDelegate; switch (type) { case 1: myDelegate = new MyDelegate(this.Add); break; case 2: myDelegate = new MyDelegate(this.Reduce); break; //// //// 其他逻辑 //// default: myDelegate = new MyDelegate(this.Add); break; } return 0; } /// <summary> /// 计算具体逻辑处理 /// </summary> /// <param name="calDel">MyDelegate计算委托变量</param> /// <param name="num1">参数1</param> /// <param name="num2">参数2</param> /// <returns>计算处理结果</returns> public int CalculOpert(MyDelegate calDel, int num1, int num2) { int resout = 0; //// //// 其他业务逻辑处理 //// //// 委托调用处理--异步调用 //// 异步执行完毕后,无需回调方法 calDel.BeginInvoke(num1, num2, null, null); //// 异步执行完毕后,有回调方法 calDel.BeginInvoke(num1, num2, new AsyncCallback(this.AsyncOpert), new int[] { num1, num2 }); return resout; } /// <summary> /// 处理异步执行结果逻辑 /// </summary> /// <param name="resout">异步处理结果</param> public void AsyncOpert(IAsyncResult resout) { MyDelegate myDel = (MyDelegate)((AsyncResult)resout).AsyncDelegate; //// 获取异步处理结果 int res = myDel.EndInvoke(resout); //// 委托方法参数 int[] lisNum = (int[])resout.AsyncState; if (res>0) { ///// ///// 业务逻辑处理 ///// } else { ///// ///// 业务逻辑处理 ///// } } /// <summary> /// 相加 /// </summary> /// <param name="num1">参数1</param> /// <param name="num2">参数2</param> /// <returns>结果</returns> public int Add(int num1, int num2) { //// 其它业务逻辑处理 return num1 + num2; } /// <summary> /// 相法 /// </summary> /// <param name="num1">参数1</param> /// <param name="num2">参数2</param> /// <returns>结果</returns> public int Reduce(int num1, int num2) { //// 其它业务逻辑处理 return num1 - num2; } } }
委托结合反射的使用:这中情况的使用场景是:当不同的委托其对于的参数个数或者参数类型不尽相同,那么这时候就在调用委托方法时,就不能简简单单的调用执行
这种情况在实际工作中场景举例:比如我们在使用缓存机制时候,都要使用到委托通反射相结合使用。
/// <summary> /// Calculat类 /// </summary> public class Calculat { /// <summary> /// 获取产品详情 /// </summary> /// <param name="proKeyID">产品ID</param> /// <returns>获取结果</returns> public string GetProInfor(string proKeyID) { string proInfor = string.Empty; proInfor = ChachHelp.GetBusData<string>(new Func<string, string>(new ProBuss().GetProInfor), "proDetile" + proKeyID, 60000, proKeyID); return proInfor; } /// <summary> /// 获取用户常用产品 /// </summary> /// <param name="userID">用户ID</param> /// <param name="cout">获取条数</param> /// <returns>获取结果</returns> public string GetCommonProInto(string userID, int cout) { string commonProInto = string.Empty; commonProInto = ChachHelp.GetBusData<string>(new Func<string, int, string>(new ProBuss().GetCommonProInto), "commonProInto" + userID, 60000, userID, cout); return commonProInto; } } /// <summary> /// 产品相关操作的业务逻辑处理 /// </summary> public class ProBuss { /// <summary> /// 获取产品详情 /// </summary> /// <param name="proKeyID">产品ID</param> /// <returns>获取结果</returns> public string GetProInfor(string proKeyID) { string proInfor = string.Empty; ////--- //// 具体产品详情的逻辑处理 ///--- return proInfor; } /// <summary> /// 获取用户常用产品 /// </summary> /// <param name="userID">用户ID</param> /// <param name="cout">获取条数</param> /// <returns>获取结果</returns> public string GetCommonProInto(string userID, int cout) { string commonProInto = string.Empty; ////--- //// 获取用户常用产品的逻辑处理 ///--- return commonProInto; } } /// <summary> /// 缓存操作类 /// </summary> public class ChachHelp { /// <summary> /// 获取缓存方法 /// </summary> /// <typeparam name="T">返回数据类型</typeparam> /// <param name="dele">数据获取方法</param> /// <param name="cacheKey">缓存建</param> /// <param name="cacheDuration">缓存时间</param> /// <param name="objs">具体的参数集合</param> /// <returns>获取结果</returns> public static T GetBusData<T>(Delegate dele, string cacheKey, int cacheDuration, params object[] objs) { object obj = null; try { ///// obj =//// 具体调用缓存获取数据方法; } catch (Exception ex) { //// 吃掉异常 } //// 如果缓存中没有获取到数据,那么就直接通过方法获取数据 if (obj == null) { string assemblyName = dele.Target.GetType().Assembly.FullName; string typeName = dele.Target.GetType().FullName; object instance = Assembly.Load(assemblyName).CreateInstance(typeName); MethodInfo methodInfo = dele.Method; obj = methodInfo.Invoke(instance, objs); if (obj != null) { //// 在获取到数据后,应当将获取到数据,存储记得到缓存中去 //// 缓存存储具体方法逻辑 } } //// 类型转换,将obj转换为对应的数据类型 //// 此处先简略的直接用强制转换一波 T tobj = (T)obj; return tobj; } }
五、小结:
从上面的委托代码中可以发现,引入委托后,编程人员可以把方法的引用封装在委托对象中
(把过程的调用转化为对象的调用,充分体现了委托加强了面向对象编程的思想。),
然后把委托对象传递给需要引用方法的代码,这样在编译的过程中我们并不知道调用了哪个方法,
这样一来,C#引入委托机制后,使得方法声明和方法实现的分离,充分体现了面向对象的编程思想。
WEB网站常见受攻击方式及解决办法
一个网站建立以后,如果不注意安全方面的问题,很容易被人攻击,下面就讨论一下几种漏洞情况和防止攻击的办法.
一.跨站脚本攻击(XSS)
跨站脚本攻击(XSS,Cross-site scripting)是最常见和基本的攻击WEB网站的方法。攻击者在网页上发布包含攻击性代码的数据。当浏览者看到此网页时,特定的脚本就会以浏览者用户的身份和权限来执行。通过XSS可以比较容易地修改用户数据、窃取用户信息,以及造成其它类型的攻击,例如CSRF攻击
常见解决办法:确保输出到HTML页面的数据以HTML的方式被转义
出错的页面的漏洞也可能造成XSS攻击.比如页面/gift/giftList.htm?page=2找不到,出错页面直接把该url原样输出,如果攻击者在url后面加上攻击代码发给受害者,就有可能出现XSS攻击
二. 跨站请求伪造攻击(CSRF)
跨站请求伪造(CSRF,Cross-site request forgery)是另一种常见的攻击。攻击者通过各种方法伪造一个请求,模仿用户提交表单的行为,从而达到修改用户的数据,或者执行特定任务的目的。为了假冒用户的身份,CSRF攻击常常和XSS攻击配合起来做,但也可以通过其它手段,例如诱使用户点击一个包含攻击的链接
解决的思路有:
1.采用POST请求,增加攻击的难度.用户点击一个链接就可以发起GET类型的请求。而POST请求相对比较难,攻击者往往需要借助javascript才能实现
2.对请求进行认证,确保该请求确实是用户本人填写表单并提交的,而不是第三者伪造的.具体可以在会话中增加token,确保看到信息和提交信息的是同一个人
三.Http Heads攻击
凡是用浏览器查看任何WEB网站,无论你的WEB网站采用何种技术和框架,都用到了HTTP协议.HTTP协议在Response header和content之间,有一个空行,即两组CRLF(0x0D 0A)字符。这个空行标志着headers的结束和content的开始。“聪明”的攻击者可以利用这一点。只要攻击者有办法将任意字符“注入”到headers中,这种攻击就可以发生
以登陆为例:有这样一个url:
http://localhost/login?page=http%3A%2F%2Flocalhost%2Findex
当登录成功以后,需要重定向回page参数所指定的页面。下面是重定向发生时的response headers.
HTTP/1.1 302 Moved Temporarily
Date: Tue, 17 Aug 2010 20:00:29 GMT
Server: Apache mod_fcgid/2.3.5 mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635
Location: http://localhost/index
假如把URL修改一下,变成这个样子:
http://localhost/login?page=http%3A%2F%2Flocalhost%2Fcheckout%0D%0A%0D%0A%3Cscript%3Ealert%28%27hello%27%29%3C%2Fscript%3E
那么重定向发生时的reponse会变成下面的样子:
HTTP/1.1 302 Moved Temporarily
Date: Tue, 17 Aug 2010 20:00:29 GMT
Server: Apache mod_fcgid/2.3.5 mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635
Location: http://localhost/checkout<CRLF>
<CRLF>
<script>alert('hello')</script>
这个页面可能会意外地执行隐藏在URL中的javascript。类似的情况不仅发生在重定向(Location header)上,也有可能发生在其它headers中,如Set-Cookie header。这种攻击如果成功的话,可以做很多事,例如:执行脚本、设置额外的cookie(<CRLF>Set-Cookie: evil=value)等。
避免这种攻击的方法,就是过滤所有的response headers,除去header中出现的非法字符,尤其是CRLF。
服务器一般会限制request headers的大小。例如Apache server默认限制request header为8K。如果超过8K,Aapche Server将会返回400 Bad Request响应:
对于大多数情况,8K是足够大的。假设应用程序把用户输入的某内容保存在cookie中,就有可能超过8K.攻击者把超过8k的header链接发给受害者,就会被服务器拒绝访问.解决办法就是检查cookie的大小,限制新cookie的总大写,减少因header过大而产生的拒绝访问攻击
四.Cookie攻击
通过Java Script非常容易访问到当前网站的cookie。你可以打开任何网站,然后在浏览器地址栏中输入:javascript:alert(doucment.cookie),立刻就可以看到当前站点的cookie(如果有的话)。攻击者可以利用这个特性来取得你的关键信息。例如,和XSS攻击相配合,攻击者在你的浏览器上执行特定的Java Script脚本,取得你的cookie。假设这个网站仅依赖cookie来验证用户身份,那么攻击者就可以假冒你的身份来做一些事情。
现在多数浏览器都支持在cookie上打上HttpOnly的标记,凡有这个标志的cookie就无法通过Java Script来取得,如果能在关键cookie上打上这个标记,就会大大增强cookie的安全性
五.重定向攻击
一种常用的攻击手段是“钓鱼”。钓鱼攻击者,通常会发送给受害者一个合法链接,当链接被点击时,用户被导向一个似是而非的非法网站,从而达到骗取用户信任、窃取用户资料的目的。为防止这种行为,我们必须对所有的重定向操作进行审核,以避免重定向到一个危险的地方.常见解决方案是白名单,将合法的要重定向的url加到白名单中,非白名单上的域名重定向时拒之,第二种解决方案是重定向token,在合法的url上加上token,重定向时进行验证.
六.上传文件攻击
1.文件名攻击,上传的文件采用上传之前的文件名,可能造成:客户端和服务端字符码不兼容,导致文件名乱码问题;文件名包含脚本,从而造成攻击.
2.文件后缀攻击.上传的文件的后缀可能是exe可执行程序,js脚本等文件,这些程序可能被执行于受害者的客户端,甚至可能执行于服务器上.因此我们必须过滤文件名后缀,排除那些不被许可的文件名后缀.
3.文件内容攻击.IE6有一个很严重的问题 , 它不信任服务器所发送的content type,而是自动根据文件内容来识别文件的类型,并根据所识别的类型来显示或执行文件.如果上传一个gif文件,在文件末尾放一段js攻击脚本,就有可能被执行.这种攻击,它的文件名和content type看起来都是合法的gif图片,然而其内容却包含脚本,这样的攻击无法用文件名过滤来排除,而是必须扫描其文件内容,才能识别。
判断URL是否存在
判断URL是否存在
判断网址是否存在,可以使用下面方面处理。如果HttpStatusCode为OK,说明网址存在。
提升高并发量服务器性能解决思路
刚刚在网上淘了一个提升高并发量服务器性能解决思路,个人感觉非常不错,给大家分享出来,希望给您有所帮助。
提升高并发量服务器性能解决思路
一个小型的网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构、性能的要求都很简单。随着互联网业务的不断丰富,网站相关的技术经过这些年的发展,已经细分到很细的方方面面,尤其对于大型网站来说,所采用的技术更是涉及面非常广,从硬件到软件、编程语言、数据库、WebServer、防火墙等各个领域都有了很高的要求,已经不是原来简单的html静态网站所能比拟的。
大型网站,比如门户网站,在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。这几个解决思路在一定程度上意味着更大的投入。
1、HTML静态化
其实大家都知道,效率最高、消耗最小的就是纯静态化的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统CMS,像我们常访问的各个门户站点的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。
除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化、有更新的时候再重新静态化也是大量使用的策略,像Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。
同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现。比如论坛中论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储在数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这部分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求。
2、图片服务器分离
大家知道,对于Web服务器来说,不管是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他们都有独立的、甚至很多台的图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃。
在应用服务器和图片服务器上,可以进行不同的配置优化,比如apache在配置ContentType的时候可以尽量少支持、尽可能少的LoadModule,保证更高的系统消耗和执行效率。
3、数据库集群、库表散列
大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。
在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是类似的方案,您使用了什么样的DB,就参考相应的解决方案来实施即可。
上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用DB类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并且最有效的解决方案。
我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。
sohu的论坛就是采用了这样的架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系统随时增加一台低成本的数据库进来补充系统性能。
4、缓存
缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。
架构方面的缓存,对Apache比较熟悉的人都能知道Apache提供了自己的缓存模块,也可以使用外加的Squid模块进行缓存,这两种方式均可以有效的提高Apache的访问响应能力。
网站程序开发方面的缓存,Linux上提供的Memory Cache是常用的缓存接口,可以在web开发中使用,比如用Java开发的时候就可以调用MemoryCache对一些数据进行缓存和通讯共享,一些大型社区使用了这样的架构。另外,在使用web语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP有Pear的Cache模块,Java就更多了,.net不是很熟悉,相信也肯定有。
5、镜像
镜像是大型网站常采用的提高性能和数据安全性的方式,镜像的技术可以解决不同网络接入商和地域带来的用户访问速度差异,比如ChinaNet和EduNet之间的差异就促使了很多网站在教育网内搭建镜像站点,数据进行定时更新或者实时更新。在镜像的细节技术方面,这里不阐述太深,有很多专业的现成的解决架构和产品可选。也有廉价的通过软件实现的思路,比如Linux上的rsync等工具。
6、负载均衡
负载均衡将是大型网站解决高负荷访问和大量并发请求采用的高端解决办法。
负载均衡技术发展了多年,有很多专业的服务提供商和产品可以选择,我个人接触过一些解决方法,其中有两个架构可以给大家做参考。
(1)、硬件四层交换
第四层交换使用第三层和第四层信息包的报头信息,根据应用区间识别业务流,将整个区间段的业务流分配到合适的应用服务器进行处理。
第四层交换功能就像是虚IP,指向物理服务器。它传输的业务服从的协议多种多样,有HTTP、FTP、NFS、Telnet或其他协议。这些业务在物理服务器基础上,需要复杂的载量平衡算法。在IP世界,业务类型由终端TCP或UDP端口地址来决定,在第四层交换中的应用区间则由源端和终端IP地址、TCP和UDP端口共同决定。
在硬件四层交换产品领域,有一些知名的产品可以选择,比如Alteon、F5等,这些产品很昂贵,但是物有所值,能够提供非常优秀的性能和很灵活的管理能力。“Yahoo中国”当初接近2000台服务器,只使用了三、四台Alteon就搞定了。
(2)、软件四层交换
大家知道了硬件四层交换机的原理后,基于OSI模型来实现的软件四层交换也就应运而生,这样的解决方案实现的原理一致,不过性能稍差。但是满足一定量的压力还是游刃有余的,有人说软件实现方式其实更灵活,处理能力完全看你配置的熟悉能力。
软件四层交换我们可以使用Linux上常用的LVS来解决,LVS就是Linux Virtual Server,他提供了基于心跳线heartbeat的实时灾难应对解决方案,提高系统的强壮性,同时可供了灵活的虚拟VIP配置和管理功能,可以同时满足多种应用需求,这对于分布式的系统来说必不可少。
一个典型的使用负载均衡的策略就是,在软件或者硬件四层交换的基础上搭建squid集群,这种思路在很多大型网站包括搜索引擎上被采用,这样的架构低成本、高性能还有很强的扩张性,随时往架构里面增减节点都非常容易。
对于大型网站来说,前面提到的每个方法可能都会被同时使用到,这里介绍得比较浅显,具体实现过程中很多细节还需要大家慢慢熟悉和体会。有时一个很小的squid参数或者apache参数设置,对于系统性能的影响就会很大。
7、最新:CDN加速技术
什么是CDN?
CDN的全称是内容分发网络。其目的是通过在现有的Internet中增加一层新的网络架构,将网站的内容发布到最接近用户的网络“边缘”,使用户可以就近取得所需的内容,提高用户访问网站的响应速度。
CDN有别于镜像,因为它比镜像更智能,或者可以做这样一个比喻:CDN=更智能的镜像+缓存+流量导流。因而,CDN可以明显提高Internet网络中信息流动的效率。从技术上全面解决由于网络带宽小、用户访问量大、网点分布不均等问题,提高用户访问网站的响应速度。
CDN的类型特点
CDN的实现分为三类:镜像、高速缓存、专线。
镜像站点(Mirror Site),是最常见的,它让内容直接发布,适用于静态和准动态的数据同步。但是购买和维护新服务器的费用较高,还必须在各个地区设置镜像服务器,配备专业技术人员进行管理与维护。对于大型网站来说,更新所用的带宽成本也大大提高了。
高速缓存,成本较低,适用于静态内容。Internet的统计表明,超过80%的用户经常访问的是20%的网站的内容,在这个规律下,缓存服务器可以处理大部分客户的静态请求,而原始的服务器只需处理约20%左右的非缓存请求和动态请求,于是大大加快了客户请求的响应时间,并降低了原始服务器的负载。
CDN服务一般会在全国范围内的关键节点上放置缓存服务器。
专线,让用户直接访问数据源,可以实现数据的动态同步。
CDN的实例
举个例子来说,当某用户访问网站时,网站会利用全球负载均衡技术,将用户的访问指向到距离用户最近的正常工作的缓存服务器上,直接响应用户的请求。
当用户访问已经使用了CDN服务的网站时,其解析过程与传统解析方式的最大区别就在于网站的授权域名服务器不是以传统的轮询方式来响应本地DNS的解析请求,而是充分考虑用户发起请求的地点和当时网络的情况,来决定把用户的请求定向到离用户最近同时负载相对较轻的节点缓存服务器上。
通过用户定位算法和服务器健康检测算法综合后的数据,可以将用户的请求就近定向到分布在网络“边缘”的缓存服务器上,保证用户的访问能得到更及时可靠的响应。
由于大量的用户访问都由分布在网络边缘的CDN节点缓存服务器直接响应了,这就不仅提高了用户的访问质量,同时有效地降低了源服务器的负载压力。
附:某CDN服务商的服务说明
采用GCDN加速方式
采用了GCDN加速方式以后,系统会在浏览用户和您的服务器之间增加一台GCDN服务器。浏览用户访问您的服务器时,一般静态数据,如图片、多媒体资料等数据将直接从GCDN服务器读取,使得从主服务器上读取静态数据的交换量大大减少。
为VIP型虚拟主机而特加的VPN高速压缩通道,使用高速压缩的电信<==>网通、电信<==>国际(HK)、网通<==>国际(HK)等跨网专线通道,智能多线,自动获取最快路径,极速的动态实时并发响应速度,实现了网站的动态脚本实时同步,对动态网站有一个更加明显的加速效果。
每个网络运营商(电信、网通、铁通、教育网)均有您服务器的GCDN服务器,无论浏览用户是来自何处,GCDN都能让您的服务器展现最快的速度!另外,我们将对您的数据进行实时备份,让您的数据更安全!