組成.net軟件技術的組件 Composed of. Net component software technology
組件之一,“智能”客戶端應用軟件和操作係統,包括PC、PDA、手機或其他移動設備通過互聯網、藉助Web Services技術,用戶能夠在任何時間、任何地點都可以得到需要的信息和服務。例如:可以在手機上閱讀新聞、定購機票、瀏覽在綫相册等等。現在我們假設一種場景,如公司內使用的CRM係統,應用了.net的解决方案後所有的業務人員便可以通過手機或PDA直接訪問客戶信息了。
如何創建“智能”終端
Web Services是智能終端軟件的基礎,微軟為用戶創建智能終端提供了一整套豐富的解决方案,包括:
.net Framework - 智能終端實現跨平臺(設備無關性)的執行環境
Visual Studio .net – 建立並集成Web Services和應用程序的快速開發工具
Microsoft Windows Server 2003 – 新一代的企業服務器,用於提供建立和發佈各種解决方案
Microsoft Office Professional Edition 2003 – 內建的工具集也能幫助開發智能終端
現在發展最快的終端非手機莫屬了,有兩大陣營在開發SmartPhone技術,一個是微軟為代表的Stinger應用於三星,另一個就是以NOKIA、愛立信組成的Symbian
Web Services是.net的核心技術。那什麽是Web Services呢?正如Web是新一代的用戶與應用交互的途徑,XML是新一代的程序之間通訊的途徑一樣,Web Services是新一代的計算機與計算機之間一種通用的數據傳輸格式,可讓不同運算係統更容易進行數據交換。Web Services有以下幾點特性:Web services允許應用之間共享數據;Web services分散了代碼單元;基於XML這種internet數據交換的通用語言,實現了跨平臺、跨操作係統、跨語言。那微軟的ASP和Web services究竟有什麽不同呢,ASP仍然是一個集中式計算模型的産物,衹不過是披着一層互聯網的外衣。但Web Services卻是一個迥然不同的精靈,它秉承“軟件就是服務”的真言,同時順應分佈式計算模式的潮流。而它的存在形式又與以往軟件不同。這種組件模式,小巧、單一,對於開發人員來講,開發成本較低。
在這裏指出Web services不是微軟發明的,同樣也不屬於微軟專有。Web services是一個開放的標準,和HTTP、 XML、SOAP一樣。他們是一個工業標準而非微軟標準,WS-I是為了促進Web Services互通性的聯盟組織,最初是由IBM和微軟所發起,其它的成員包括BEA System、惠普計算機(HP)、甲骨文(Oracle)、英特爾(Intel)和SUN 計算機(Sun Microsystem)。如今網絡上存在的大多Web services其實沒有使用.net構架,Web services具有互操作屬性,你同樣可以使用Windows開發客戶端來調用運行於Linux上面的Web services的方法。
先前提到的接口規範問題,在.net中,Web service接口通常使用Web Services Description Language (WSDL)描述。 WSDL 使用XML來定義這種接口操作標準及輸入輸出參數,看起來很像COM和CORBA的接口定義語言(IDLS)Interface Definition Languages。接口定義後就必須使用一些協議調用接口,如SOAP協議,SOAP源於一種叫做XML RPC(XML遠程進程調用remote procedure calling)的協議,而Java則根據XML-RPC發展了自己的JAX-RPC協議用來調用Web Services。發佈和訪問Web Services的接口就用到UDDI了,這裏我們衹需要知道WSDL使用XML定義Web Services接口,通過SOAP訪問Web Services,在internet上尋找Web Services使用UDDI就行了,更多的Web Services將在最後一課介紹。
Microsoft提供了最佳的服務器構架—Microsoft Windows Server System—便於發佈、配置、管理、編排Web Services。為了滿足分佈式計算的需要微軟構造了一係列的服務器係統,這些內建安全技術的係統全部支持XML,這樣加速了係統、應用程序以及同樣使用Web Services的夥伴應用之間的集成。
Microsoft Windows Server System包括:
Microsoft Application Center 2000 - 配置和管理Web應用程序
Microsoft BizTalk Server 2002 - 建立基於XML的跨應用和組織的商業邏輯
Microsoft Commerce Server 2002 – 能夠迅速建立大規模電子商務的解决方案
Microsoft Content Management Server 2002 – 管理動態電子商務網站的目錄
Microsoft Exchange Server 2000 – 用於進行隨時隨地的通訊協作
Microsoft Host Integration Server 2000 – 用於和主機係統之間傳輸數據
Microsoft Internet Security and Acceleration Server 2000 (ISA Server) – internet連接
Microsoft Mobile Information Server 2002 – 用於支持手持設備
Microsoft Operations Manager 2000 – 描述企業級解决方案的操作管理
Microsoft Project Server 2002 - 提供項目管理的最佳方案
Microsoft SharePoint Portal Server 2001 – 查詢、共享、發佈商業信息
Microsoft SQL Server 2000 – 企業級數據庫
Microsoft Visual Studio .net和Microsoft .net Framework對於建立,發佈並運行Web Services是一個完美的解决方案。
Microsoft .net 框架 SDK 快速入門教程:www.aspxweb.com/quickstart/
微軟官方的教程。
CLR 與 CLI
.net的初級組成是CLI和CLR。CLI是一套運作環境說明,包括一般係統、基礎類庫和與機器無關的中間代碼,全稱為通用中間語言(CLI)。CLR則是確認操作密碼符合CLI的平臺。在CLI執行前,CLR必須將指令及時編譯轉換成原始機械碼。
所有CLI都可經由.net自我表述。CLR檢查元資料以確保正確的方法被調用。元資料通常是由語言編譯器生成的,但開發人員也可以通過使用客戶屬性創建他們自己的元資料。
如果一種語言實現生成了CLI,它也可以通過使用CLR被調用,這樣它就可以與任何其他.net語言生成的資料相交互。CLR也被設計為作業係統無關性。
當一個匯編體被載入時,CLR執行各種各樣的測試。其中的兩個測試是確認與核查。在確認的時候,CLR檢查匯編體是否包含有效的元資料和CLI,並且檢查內部表的正確性。核查則不那麽精確。核查機製檢查代碼是否會執行一些“不安全”的操作。核查所使用的演算法非常保守,導致有時一些“安全”的代碼也通不過核查。不安全的代碼衹有在匯編體擁有“跳過核查”許可的情況下纔會被執行,通常這意味着代碼是安裝在本機上的。
通過.net,你可以用SOAP和不同的Web services進行交互。
第一個特性:隱式類型化本地變量
這個特性非常簡單,有些JavaScript的影子,我們可以統一使用使用"var"關鍵字來聲明局部變量,而不再需要指明變量的確切類型了,變量的確切類型可通過聲明變量時的初始值推斷出來。這樣一來,可以大大簡化我們聲明局部變量的工作量了,下面是一個例子:
class LocalVariables : AppRunner.AbstractApplication
{
public override void Run()
{
var intValue = 5;
var stringValue = "This is a string";
var customClass = new LocalVariables();
var intArray = new int { 1, 2, 3 };
foreach (var value in intArray)
Console.WriteLine(value);
}
}
上面的代碼將被解析成:
class LocalVariables : AppRunner.AbstractApplication
{
public override void Run()
{
int intValue = 5;
string stringValue = "This is a string";
LocalVariables customClass = new LocalVariables();
int[] intArray = new int;
foreach (int value in intArray)
Console.WriteLine(value);
}
}
要特別註意的是,由於變量的類型是通過變量初始值推斷而來的,所以在聲明變量的同時必需為變量指定初始值。並且,變量並不是沒有類型的,變量一旦初始化之後,類型就確定下來了,以後就衹能存儲某種類型的值了,比如上面的stringValue的類型經推斷為string,所以該變量就衹能保存string類型的值了。
第二個特性:匿名類型
有些時候我們需要臨時保存一些運算的中間結果,特別是當這些中間結果是由多個部份組成時,我們常常會去聲明一個新的類型,以方便保存這些中間結果。表面上看起來這很正常,而細想之後就會發現,這個新類型衹服務於這個函數,其它地方都不會再使用它了,就為這一個函數而去定義一個新的類型,確實有些麻煩。
現在,C#3.0中的匿名類型特性就可以很好的解决上面提到的問題,通過匿名類型,我們可以簡單使用new { 屬性名1=值1, 屬性名2=值2, ..... , 屬性名n=值n }的形式直接在函數中創建新的類型,看下面這個例子:
class AnonymousType : AppRunner.AbstractApplication
{
public override void Run()
{
var anonymousType1 = new {
CardNumber = "10001", Name = "van’s", Sex = true
};
Console.WriteLine(anonymousType1.CardNumber);
Console.WriteLine(anonymousType1.Name);
var anonymousType2 = new {
CardNumber = "10002", Name = "martin", Sex = true
};
anonymousType2 = anonymousType1;
}
}
在新類型中衹能有字段成員,而且這些字段的類型也是通過初值的類型推斷出來的。如果在聲明新的匿名類型時,新類型的字段名、順序以及初始值的類型是一致的,那麽將會産生相同的匿名類型,所以上例中anonymousType1和anonymousType2的類型是相同的,自然能進行anonymousType2=anonymousType1的賦值。
第三個特性:隱式類型化數組
這個特性是對隱式類型化本地變量的擴展,有了這個特性,將使我們創建數組的工作變得簡單。我們可以直接使用"new[]"關鍵字來聲明數組,後面跟上數組的初始值列表。在這裏,我們並沒有直接指定數組的類型,數組的類型是由初始化列表推斷出來的。
class AnonymousTypeArray : AppRunner.AbstractApplication
{
public override void Run()
{
var intArray = new[] { 1, 2, 3, 4, 5 };
var doubleArray = new[] { 3.14, 1.414 };
var anonymousTypeArray = new[] {
new { Name="van’s", Sex=false, Arg=22 },
new { Name="martin", Sex=true, Arg=23 }
};
Console.WriteLine(intArray);
Console.WriteLine(doubleArray);
Console.WriteLine(anonymousTypeArray[0].Name);
}
}
上面的代碼中,anonymousTypeArray變量的聲明同時運用了隱式類型化數組和匿名類型兩種特性,首先創建匿名類型,然後再初始值列表,推斷出數組的確切類型。
第四個特性:對象構造者
我們在聲明數組時,可以同時對其進行初始化,這樣就省去了很多麻煩,但是在創建類的對象時,這招可就不靈了,我們要麽調用該類的構造函數完成對象的初始化,要麽就手工進行初始化。這兩種方法都不太方便,使用構造函數來對對象進行初始化時,我們為了某種靈活性,可能需要編寫構造函數的多個重載版本,實在是麻煩。
C#3.0中加入的對象構造者特性,使得對象的初始化工作變得格外簡單,我們可以采用類似於數組初始化的方式來初始化類的對象,方法就是直接在創建類對象的表達式後面跟上類成員的初始化代碼。具體示例如下:
class Point
{
public int X { get; set; }
public int Y { get; set; }
public override string ToString()
{
return "(" + X.ToString() + ", " + Y.ToString() + ")";
}
}
class Rectangle
{
public Point P1 { get; set; }
public Point P2 { get; set; }
public Rectangle()
{
P1 = new Point();
P2 = new Point();
}
public override string ToString()
{
return "P1: " + P1 + ", P2: " + P2;
}
}
class ObjectBuilder : AppRunner.AbstractApplication
{
public override void Run()
{
Point thePoint = new Point() { X = 1, Y = 2 };
Console.WriteLine("Point(X, Y) = ", thePoint);
Rectangle theRectangle = new Rectangle() {
P1 = { X = 1, Y = 1 }, P2 = { X = 100, Y = 200 }
};
Console.WriteLine(theRectangle);
}
}
我們在定義Point類的X和Y屬性時,衹須寫上該屬性的get和set訪問器聲明,C#編譯器會自動為我們生成默認的get和set操作代碼,當我們需要定義簡單屬性時,這個特性非常有用。
我們以new Point() { X = 1, Y = 2 }語句,輕鬆的完成了對Point類的初始化工作。在創建類的對象時,我們可以按照需要去初始化類的對象,衹要在類的創建表達式後跟上要初始化屬性的列表即可,且可以衹對需要初始化的屬性賦初值,而無需把所有屬性的初始值都寫上去。
在theRectangle對象的初始化表達式中,我們首先對P1屬性進行初始化,然而P1屬性也是一個自定義的類型,所以P1屬性的初始化是另一個類型(Point)的初始化表達式,我們可以這樣的方式來對更加復雜的類型進行初始化。
上篇文章中介紹了C# 3.0中比較簡單的四個特性,分別是隱式類型化本地變量、匿名類型、隱式類型化數組,以及對象構造者,下面我將對C# 3.0中的較復雜,同時也是非常強大的幾個特性進行介紹,供大傢快速瀏覽。
第五個特性:集合構造者
我們可以在聲明數組的同時,為其指定初始值,方法是直接在數組聲明的後面跟上初始值列表。這樣就使數組的初始化工作變得簡單,而對於我們自己創建的集合類型,就無法享受到與普通數組一樣的待遇了,我們無法在創建自定義集合對象的同時,使用數組的初始化語法為其指定初始值。
C# 3.0中加入的集合構造者特性,可使我們享受到與普通數組一樣的待遇,從而在創建集合對象的同時為其指定初始值。為了做到這一點,我們需要讓我們的集合實現ICollection<T>接口,在這個接口中,完成初始化操作的關鍵在於Add函數,當我使用初始化語法為集合指定初始值時,C#編譯器將自動調用ICollection<T>中的Add函數將初始列表中的所有元素加入到集合中,以完成集合的初始化操作。使用示例如下:
class CollectionInitializer : AppRunner.AbstractApplication
{
class StringCollection : ICollection<string>
{
public void Add(string item)
{
Console.WriteLine(item);
}
// Other ICollection<T> Members
}
public override void Run()
{
StringCollection strings = new StringCollection() { "Van's", "Brog", "Vicky" };
}
}
在這個示例中,編譯器會自動為strings對象調用Add方法,以將初始值列表中的所有元素加入到集合中,這裏我們衹是簡單將初始值列表中的元素輸出到控製臺。
第六個特性:Lambda表達式
C# 2.0中加入的匿名代理,簡化了我們編寫事件處理函數的工作,使我們不再需要單獨聲明一個函數來與事件綁定,衹需要使用delegate關鍵字在綫編寫事件處理代碼。
而C# 3.0則更進一步,通過Lambda表達式,我們可以一種更為簡潔方式編寫事件處理代碼,新的Lambda事件處理代碼看上去就像一個計算表達式,它使用"=>"符號來連接事件參數和事件處理代碼。我可以這樣寫:SomeEvent += 事件參數 => 事件處理代碼;下面是完整的示例:
delegate T AddDelegate<T>(T a, T b);
class LambdaExpression : AppRunner.AbstractApplication
{
public static event EventHandler MyEvent;
public override void Run()
{
MyEvent += delegate(object s, EventArgs e)
{
Console.WriteLine(s);
};
MyEvent += (s, e) => { Console.WriteLine(s); };
MyEvent(this, null);
AddDelegate<string> add = (a, b) => a + b;
Console.WriteLine(add("Lambda", "Expression"));
}
}
在上面的例子中,分別使用了匿名代理和Lambda表達式來實現同樣的功能,可以明顯看出Lambda表達式的實現更為簡潔。我們在使用Lambda表達式編寫事件處理代碼時,無需指明事件參數的類型,且返回值就是最後一條語句的執行結果。
第七個特性:擴展方法
當我們需要對已有類的功能進行擴展時,我們通常會想到繼承,繼承已有類,然後為其加入新的行為。而C# 3.0中加入的擴展方法特性,則提供了另一種實現功能擴展的方式,我們可以在不使用繼承的前提下實現對已有類本身的擴展,這種方法並不會産生新的類型,而是采用嚮已有類中加入新方法的方式來完成功能擴展。
在對已有類進行擴展時,我們需將所有擴展方法都寫在一個靜態類中,這個靜態類就相當於存放擴展方法的容器,所有的擴展方法都可以寫在這裏面。而且擴展方法采用一種全新的聲明方式:public static 返回類型 擴展方法名(this 要擴展的類型 sourceObj [,擴展方法參數列表]),與普通方法聲明方式不同,擴展方法的第一個參數以this關鍵字開始,後跟被擴展的類型名,然後纔是真正的參數列表。下面是使用示例:
static class Extensions
{
public static int ToInt32(this string source)
{
return Int32.Parse(source);
}
public static T[] Slice<T>(this T[] source, int index, int count)
{
if (index < 0 || count < 0 || index + count > source.Length)
{
throw new ArgumentException();
}
T[] result = new T[count];
Array.Copy(source, index, result, 0, count);
return result;
}
}
class ExtensionMethods : AppRunner.AbstractApplication
{
public override void Run()
{
string number = "123";
Console.WriteLine(number.ToInt32());
int[] intArray = new int[] { 1, 2, 3 };
intArray = intArray.Slice(1, 2);
foreach (var i in intArray)
Console.WriteLine(i);
}
}
在上面的示例中,靜態的Extensions類中有兩個擴展方法,第一個方法是對string類的擴展,它為string類加入了名為ToInt32的方法,該方法沒有參數,並返回一個int類型的值,它將完成數字字符嚮整數的轉換。有了這個擴展方法之後,就可對任意string類的對象調用ToInt32方法了,該方法就像其本身定義的一樣。
第二個擴展方法是一個範型方法,它是對所有數組類型的擴展,該方法完成數組的切片操作。
C# 3.0中的Linq表達式,就是大量運用擴展方法來實現數據查詢的。
第八個特性:Linq查詢表達式
C# 3.0中加入的最為復雜的特性就是Linq查詢表達式了,這使我們可直接采用類似於SQL的語法對集合進行查詢,這就使我們可以享受到關係數據查詢的強大功能。
Linq查詢表達式是建立在多種C# 3.0的新特性之上的,這也是我為什麽最後纔介紹Linq的原因。下面看一個例子:
class LinqExpression : AppRunner.AbstractApplication
{
public override void Run()
{
// 定義匿名數組persons, 並為其賦初值
var persons = new[] {
new { Name="Van's", Sex=false, Age=22 },
new { Name="Martin", Sex=true, Age=30 },
new { Name="Jerry", Sex=false, Age=24 },
new { Name="Brog", Sex=false, Age=25 },
new { Name="Vicky", Sex=true, Age=20 }
};
/*
執行簡單Linq查詢
檢索所有年齡在24歲以內的人
查詢結果放在results變量中
results變量的類型與數組persons相同
*/
var results = from p in persons
where p.Age <= 24
select p;
foreach (var person in results)
{
Console.WriteLine(person.Name);
}
Console.WriteLine();
// 定義匿名數組customers, 並為其賦初值
// 該數組是匿名類型的
var customers = new[] {
new {
Name="Van's", City="China", Orders=new[] {
new {
OrderNo=0,
OrderName="C# Programming Language(Second Edition)",
OrderDate=new DateTime(2007,9, 5)
},
new {
OrderNo=1,
OrderName="Head First Design Patterns(Chinese Edition)",
OrderDate=new DateTime(2007,9,15)
},
new {
OrderNo=2,
OrderName="ASP.net Unleashed 2.0(Chinese Edition)",
OrderDate=new DateTime(2007,09,18)
},
new {
OrderNo=3,
OrderName="The C++ Programming Langauge(Special Edition)",
OrderDate=new DateTime(2002, 9, 20)
}
}
},
new {
Name="Brog", City="China", Orders=new[] {
new {
OrderNo=0,
OrderName="C# Programming Language(Second Edition)",
OrderDate=new DateTime(2007, 9, 15)
}
}
},
new {
Name="Vicky", City="London", Orders=new[] {
new { OrderNo=0,
OrderName="C++ Programming Language(Special Edition)",
OrderDate=new DateTime(2007, 9, 20)
}
}
}
};
/*
執行多重Linq查詢
檢索所在城市為中國, 且訂單日期為2007年以後的所有記錄
查詢結果是一個匿名類型的數組
其中包含客戶名, 訂單號, 訂單日期, 訂單名四個字段
*/
var someCustomers = from c in customers
where c.City == "China"
from o in c.Orders
where o.OrderDate.Year >= 2007
select new { c.Name, o.OrderNo, o.OrderDate, o.OrderName };
foreach (var customer in someCustomers)
{
Console.WriteLine(
customer.Name + ", " + customer.OrderName + ", " +
customer.OrderDate.ToString("D")
);
}
}
}
從上面的例子中,我們可以看到Linq查詢的強大特性,它允許我們進行簡單查詢,或者進行更為復雜的多重連接查詢。且查詢的結果還可以是自定義的匿名類型。
.net 3.5的新特性
一些Web開發新特性之快速指南
VS 2008的多定嚮支持
VS 2008允許你構建針對多個.net框架版本的應用。你可以從下面的博客貼子裏進一步瞭解其中的工作原理:
VS 2008 Multi-Targeting Su ort
VS 2008 Web設計器和C 支持
VS 2008包含一個顯著改進的HTML web設計器。該設計器提供了分割視圖編輯,嵌套母板頁,以及出色的C 集成。
A .net還提供了一個新的控件,該控件對數據UI場景提供了非常靈活的支持,允許對輸出的標識做完全的定製,與VS 2008中的新C 支持還有良好的協作。
A .net AJAX和JavaScript支持
.net 3.5 內置提供A .net AJAX,還添加了支持We art的UpdatePanel,支持JSON的WCF,以及N個缺陷修補和性能改進等方面的新特性。VS 2008還對集成JavaScript和AJAX進你的應用提供了極棒的支持:
VS 2008 JavaScript Intellise e
VS 2008 JavaScript Debugging
語言改進和LINQ
VS 2008中的新VB和C#編譯器對這些語言做了顯著的改進。兩者都添加了函數式編程概念的支持,允許你編寫更幹淨,更簡潔,更具有表達性的代碼。這些特性還促成了我們稱之為LINQ(語言級集成查詢)的新編程模型,使得查詢和操作數據成為.net中的一等編程概念。
下面是我撰寫的一些討論這些新語言特性的文章(用C#作為示例):
自動屬性,對象初始化器,和集合初始化器
擴展方法
Lambda表達式
查詢句法
匿名類型
LINQ to SQL中的數據訪問改進
LINQ to SQL是.net 3.5中內置的OR/M (對象關係映射器)。它允許你使用.net 對象模型對關係數據庫進行建模。然後你可以使用LINQ對數據庫進行查詢,以及更新、插入,刪除數據。LINQ to SQL完整支持事務,視圖和存儲過程。它還提供了一個把業務邏輯和驗證規則結合進你的數據模型的簡易方式。下面是一些我討論如何使用LINQ to SQL的文章:
Part 1: Introduction to LINQ to SQL
Part 2: Defining our Data Model Cla es
Part 3: Querying our Database
Part 4: Updating our Database
Part 5: Binding UI using the A :LinqDataSource Control
我會在以後的幾周內再往這個係列裏添加幾篇文章。我認為你會發現LINQ to SQL顯著地簡化了構建非常幹淨的數據模型以及編寫極其幹淨的數據代碼。
說不盡的其他改進
上面的列表衹是所做改進的一小部分。針對客戶端開發,VS 2008 包含了WPF設計器和項目支持。ClickOnce 和WPF XBA 現在在FireFox中也工作了。WinForms和WPF項目現在也能使用A .net 應用服務(成員,角色和用戶數據)來漫遊用戶數據了。辦公開發也更加豐富了,包括對Office 2007 Ri on的集成支持。WCF和Workflow項目和設計器也包括在VS 2008中了。單元測試的速度大為提高,而且單元測試的支持現在包括在VS Profe ional版本(而不僅僅是VSTS版了)中了。連續集成支持現在也內置於TFS中了。AJAX web測試(單元和壓力)現在也由VS Test産品支持了。還有許許多多多的改進,這裏無法一一提及了。
重要的安裝註意事項
在安裝VS 2008 和.net 3.5 Beta2之後,還有2件重要的事情你應該馬上做:
1) 你應該下載和運行這個批文件。這衹要幾秒鐘就可以運行完,它修補了這個星期早些時候我們發現的System.Web.Exte io .dll版本政策的問題,該程序集包含了 A .net AJAX。如果你不運行這個批文件,那麽用A .net AJAX 1.0 和 VS 2005構建的現有的A .net 2.0項目就會自動地運載隨 .net 3.5 Beta2發佈的新A .net AJAX 版本。這會工作而且運行良好,但會不小心導致你的VS2005應用依賴於.net 3.5。運行這個批文件會改變新的System.Web.Exte io .dll 程序集的版本綁定政策,確保你衹在你明確構建.net 3.5項目時纔使用新的.net 3.5 A .net AJAX版本。
2) 假如你曾經在你的機器上安裝過Orcas或VS 2008的早期版本(Beta1 或某個CTP 版本)的話,你需要在安裝Beta2後重新設定你的VS 2008設置。如果你不這麽做的話,有些設置會非常奇怪(一些窗口在出現在奇怪的地方),你也有可能看到一些IDE性能問題。你可以在命令行上對VS 2008的IDE版本鍵入“DevEnv /resetsettings”來重新設定你的配置:
加密
信息安全是計算機應用的首要問題之一,但目前關於.net加密功能的範例卻少之又少。有鑒於此,本文探討了在.net平臺下加密/解密文件的一般過程,並提供了一個加密/解密文件的工具。
Web服務以不容置疑的態勢迅速發展,促使許多單位開始考慮.net之類的開發平臺。但是,出於對安全問題的擔心,一些單位總是對采用新技術心存顧慮。好在有許多成熟的安全和網絡技術,例如虛擬私有網絡(VPN)和防火墻等,能夠極大地提高Web服務應用的安全和性能,讓開發者擁有選擇安全技術的自由,而不是非得使用尚在發展之中的XML安全技術不可。
雖然安全是信息係統的首要問題,但有關.net安全和加密工具的範例卻少之又少。看看大多數.net書籍的目錄,找不到任何有關安全的題目,更不用說關於密碼係統的探討了。
有鑒於此,本文將介紹如何在VB開發中運用.net的加密和密鑰生成類,提供一個可用來加密和解密文件的工具Cryption。有了這個工具,你就可以在硬盤上保存各種機密文件,例如所有的密碼/用戶名字信息、收支文件、以及其他想要保密的信息,還可以加密那些通過Internet發送的文件。加密技術的用途非常廣泛,你可以進一步定製本文提供的工具滿足某些特殊需要,例如增加批處理能力等。
一、兩類重要的安全威脅
攻擊和泄密是計算機面臨的兩大安全威脅。攻擊可能來自病毒,例如它會刪除文件、降低機器運行速度或引發其它安全問題。相比之下,泄密往往要隱蔽得多,它侵害的是你的隱私:未經授權訪問硬盤文件,截取通過Internet發送的郵件,等等。泄密還可能伴隨着攻擊,例如修改機密文件等。
針對泄密的最佳防範措施就是加密。有效的加密不僅杜絶了泄密,而且還防範了由泄密引發的攻擊。加密技術有時還用於通信過程中的身份驗證——如果某個用戶知道密碼,那麽他應該就是那個擁有這一身份的人。
然而必須說明的是,沒有一種防範泄密的安全技術是絶對堅固的,因為密碼有可能被未經授權的人獲得。
二、使用.net加密功能的前提
首先,要想使用.net的安全功能,就必須用Imports語句引入加密用的包。試驗本文涉及的任何代碼之前,請在VB代碼窗口的頂部加入下列Imports語句:
Imports System.IO
Imports System.Text
Imports System.Security.Cryptography
第二,美國政府過去限製某些加密技術出口。雖然這些限製不再有效,.net框架在Windows的出口版本中禁用了“高級”加密技術。如果你的 Windows不帶高級加密能力,可以從微軟網站下載更新包:對於Windows 2000,安裝Service Pack 2包含的High Encryption Pack;對於NT,安裝Service Pack 6a。對於Windows ME、95、98的用戶,IE 5.5也包含了High Encryption Pack。
三、加密/解密工具概況
本文提供的工具可用來加密和解密文件,如果你急着給一些文件加密,衹需直接啓動本文後面提供的工具即可。
這個工具提供了一個用來輸入文件名字的文本框和一個輸入密鑰的文本框,通過便捷的用戶界面提供加密、解密和密鑰生成功能。在圖一中,上方的文本框用來輸入待加密/解密文件的名字;下面的文本框用來輸入8個字符的密碼。執行加密操作之後將生成一個新的文件,這個經過加密的文件和原始文件在同一目錄下,文件名字也和原始文件的一樣,但加上了“xx”後綴,例如,假設原始文件是MyFile.txt,則加密得到的文件是MyFilexx.txt。
加密好之後,原始文件不一定非刪除不可,但一般來說最好刪除,因為加密的根本目的就是為了隱藏原始文件的數據。如果要從加密後的文件恢復出原始文件,在上面的文本框中輸入MyFilexx.txt,然後提供密碼,Cryption工具將創建一個與原始文件一樣的MyFile.txt文件。也就是說, Cryption把文件名字後面的“xx”看作是要求解密密文的標志。
註意:加密文件之後如果忘記了用來加密該文件的密碼,再想恢復出原始文件就不可能了。當然,這與密碼本身的復雜程度有關,要想保證文件的安全,最好采用較復雜的密碼,例如混合運用字母、數字和特殊字符(如“$”符號等)。
.net提供的加密技術不止一種,不過本文討論的主要是對稱加密。對稱加密也稱為私有密鑰加密,它的特點是加密和解密用的是同一個密鑰(實際上是同一種算法),解密方和加密方都有責任保障密碼的安全(對於公用密鑰、不對稱加密,密鑰一共有兩個,其中一個密鑰是公開的,這是當前公認最有效的加密技術,但就速度而言要比對稱加密算法慢不少)。
在正式利用.net加密類加密文件之前,首先必須從用戶提供的密碼生成一個密鑰。密鑰可以利用Hash函數生成,Hash函數把用戶的密碼字符串轉換成一組類似隨機數序列的、無意義的數據,這組數據可作為密鑰使用,在加密過程中對原始數據進行唯一性變形處理。
例如,用密鑰加密數據的一種辦法是把原始數據的ASCII碼加上密鑰的ASCII碼:
密鑰:ab = ASCII: 97, 98
數據:merry = ASCII: 109, 101, 114, 114, 121
把這組數據的ASCII碼加上密鑰的ASCII碼(必要時重複使用密鑰),得到的加密結果是:
97 98 97 98 97
+109 +101 +114 +114 +121
206 199 211 212 218
對於同樣的數據,Hash算法總是生成同樣的結果(這就是說,對於同一個密碼,同一Hash算法總是生成相同的bit序列)。實際上,在本文提供的代碼中,利用.net的SHA1CryptoServiceProvider類的ComputeHash方法可以驗證這一點,例如,對於同一個輸入參數 morph,任何時候該方法總是返回下面的結果:124,230,93,253,197,206,136,72。因此,如果有人知道密碼以及生成密鑰的算法,他也可以輕鬆地推算出密鑰。
四、執行加密/解密
.net加密技術要求密鑰有確定的長度,例如,DES(Data Encryption Standard)函數要求密鑰的長度是64位,Rijndael則要求128、192或256位長度的密鑰。密鑰越長,加密強度越高。對於DES之外的加密算法,查詢LegalKeySizes屬性即可得到它允許的密鑰長度,包括MinSize(支持的最小密鑰長度)、MaxSize(最大密鑰長度)、 SkipSize(增量)。SkipSize表示密鑰最大長度和最小長度之間可用長度的間隔,例如,Rijndael算法的SkipSize值是64位。
利用下面的代碼可以得到密鑰的長度信息:
' 創建DES加密對象
Dim des As New DESCryptoServiceProvider()
Dim fd() As KeySizes
fd = des.LegalKeySizes() 'tells us the size(s), in bits
MsgBox("加密類型=" & des.ToString() & Chr(13) & "minsize = " & fd(0).MinSize & Chr(13) & _
"maxsize = " & fd(0).MaxSize & Chr(13) & "skipsize = " & fd(0).SkipSize)
運行上面的代碼,得到的結果是64、64、0。如果把加密對象的聲明改成TripleDESCryptoServiceProvider(),得到的結果是128、192、64。
說明:DES算法要求輸入一個8字節的密碼,但實際使用的密鑰衹有56位(7個字節),每一個字節的最後一位不用(它作為校驗位使用,但不用於實際的加密過程)。
下面的代碼開始生成本文示例程序的密鑰:
Public Class Form1
Inherits System.Windows.Forms.Form
' 保存密鑰的8字節的數組
Private TheKey(7) As Byte
' 在嚮量中放入一些隨機數據
Private Vector() As Byte = {&H12, &H44, &H16, &HEE, &H88, &H15, &HDD, &H41}
首先,代碼定義了保存密鑰和初始嚮量(請參見稍後的詳細說明)的兩個變量。嚮量的初值這裏用隨機數據填充,當然,通過密碼和Hash算法也可以獲得嚮量的初值。下面的過程從用戶輸入的密碼創建出密鑰:
Sub CreateKey(ByVal strKey As String)
' 保存密鑰的字節數組
Dim arrByte(7) As Byte
Dim AscEncod As New ASCIIEncoding()
Dim i As Integer = 0
AscEncod.GetBytes(strKey, i, strKey.Length, arrByte, i)
' 獲得密碼的Hash值
Dim hashSha As New SHA1CryptoServiceProvider()
Dim arrHash() As Byte = hashSha.ComputeHash(arrByte)
' 將Hash值保存到密鑰
For i = 0 To 7
TheKey(i) = arrHash(i)
Next i
End Sub
用戶的密碼(strKey)傳入到CreateKey過程,分解成一組ASCII值保存到一個字節數組。把這個字節數組傳遞給 SHA1CryptoServiceProvider類的ComputeHash方法,返回一個Hash值。把這個Hash值保存到TheKey數組,供以後的加密/解密過程使用(註意SHA1CryptoServiceProvider實際能夠支持160位,但本文示例程序衹用到64位)。
那麽,初始嚮量究竟起什麽作用呢?這個字節數組有8個元素,就象密鑰一樣,但嚮量和密鑰的作用是不同的,嚮量用來避免DES之類的算法一個特有的問題。在DES之類的算法中,原始數據被分成8字節一塊然後分別處理。DES在加密一塊數據時,要用到前一塊數據的模式,也就是說,如果改動了原始數據中第一塊的某個字符,所有後繼的塊的內容都將隨之改變,從而避免了一係列相連接的塊中出現重複塊的問題。
例如,假設你一時高興,發了一個郵件,內容衹有幾個重複的單詞“Melanie! Melanie! Melanie! Melanie!”,在密鑰和塊序列中前一塊的共同作用下,加密之後的密文不會出現重複現象。然而,進一步考慮這個加密過程可以發現,如果用同一個密鑰加密多個郵件,且郵件開頭的問候語都相同,則郵件開頭的一部分很容易受到攻擊。由於這個原因,我們用初始嚮量來模擬前一個塊。
本文加密/解密工具中的下面這段代碼示範了如何加密文件:
Sub Encrypt(ByVal inName As String , ByVal outName As String )
Try
' 創建緩衝區
Dim storage(4096) As Byte
' 已經寫入的字節數量
Dim totalBytesWritten As Long = 8
' 每次寫入的字節數量
Dim packageSize As Integer
' 聲明文件流
Dim fin As New FileStream(inName, FileMode.Open, FileAccess.Read)
Dim fout As New FileStream(outName, FileMode.OpenOrCreate, FileAccess.Write)
fout.SetLength(0)
' 源文件的大小
Dim totalFileLength As Long = fin.Length
' 創建加密對象
Dim des As New DESCryptoServiceProvider()
Dim crStream As New CryptoStream(fout, _
des.CreateEncryptor(TheKey, Vector), _
CryptoStreamMode.Write)
' 輸出加密後的文件
While totalBytesWritten < totalFileLength
packageSize = fin.Read(storage, 0, 4096)
crStream.Write(storage, 0, packageSize)
totalBytesWritten = Convert.ToInt32(totalBytesWritten + _
packageSize / des.BlockSize * des.BlockSize)
End While
crStream.Close()
Catch e As Exception
MsgBox(e.Message)
End Try
End Sub
註意這段代碼創建了三個文件流:fin,表示明文形式的原始文件;fout,加密結果文件;crStream,加密流,用來把DES加密的結果轉入輸出文件fout。增加一個crStream流的好處是不必把結果保存到臨時文件或緩衝區。
加密過程與解密過程的唯一重要區別是,執行解密時,我們將使用DESCryptoServiceProvider對象的另一個方法CreateDecryptor,除此之外,兩者其餘的處理步驟(包括參數,文件流,等等)基本相同。
五、防止破解
黑客和密碼專傢破解加密文件的辦法主要有兩個,第一是搜索密文是否有重複現象,第二是用暴力破解法獲得密鑰。首先我們考慮一下初始嚮量如何防止重複現象,然後再探討一下防止暴力破解的關鍵問題。
破解密文的第一種方式是搜索樣本——特別是重複的樣本。人們在寫信的時候總是喜歡用同樣的文字開頭,例如“親愛的XXX”、“Dear Sir”等,如果多個郵件的開頭文字相同且通過同一密鑰加密,則每個密文信件的開頭也相同。假設Antonio寫給Melanie的所有加密信件都有相同的問候語“@4^F (2$@Fx”,解密者就會首先檢查開頭的幾個單詞是不是“Dear Melanie”。解密機密文件的一個重要步驟就是猜測文件中應當會出現的幾個單詞,所以我們不應該給解密者提供這種方便。在本文的示例中,初始嚮量的內容被附加到文件的開頭,從而防止了出現重複現象。衹有信件的開頭纔容易受到此類攻擊。
計算機的運算速度和精度要遠遠超過人,特別擅長處理一些重複的任務,例如嘗試每一種可能的密鑰組合最終破解密鑰。DES加密算法本身是不安全的,這種加密算法早在70年代就已經公之於衆。而且,破解者如果想要讓搜索密鑰的過程自動化,同樣可以方便地運用.net的DESCryptoServiceProvider類。
對於一個128位、結合運用密鑰/初始嚮量的加密方案,計算機嘗試每一種可能的密鑰組合要花多少時間?專傢們的看法並不一致,有人認為需要數月,也有人認為裝有專用硬件的價值6位數的計算機每秒能夠驗證數十億個密鑰,破解DES密文衹需數小時。如果你的機密值得花數月時間去破解,那麽最好改用 TripleDES或其他加密算法。從TripleDES的名字也可以猜出,這種加密方式采用三重數據加密標準算法,所以密鑰的長度是192位,而不是 64位的DES密鑰。記住,在其他條件相同的情況下,密鑰越長,安全程度越高。
結束語:現在你已經瞭解了. NET DES加密算法的使用過程,接下去可以研究.net的其他安全功能,包括極具吸引力的公用密鑰加密方案。雖然公用密鑰加密方案執行起來速度慢一些,但加密效果一般要比TripleDES好。本人沒有什麽機密值得運用DES之外的算法,不過你的要求可能有所不同。
結語
在VS 2008和.net 3.5中,我希望你會發現許許多多非常有用的新改進和功能增強。敬請在下幾個星期裏收看我的博客,我將對這些新特性做詳細討論以及討論如何充分利用這些新特性。