0%

C#排序算法的比较 - r163 - 博客园

Excerpt

首先通过图表比较不同排序算法的时间复杂度和稳定性。排序方法平均时间最坏情况最好情况辅助空间稳定性直接插入排序O(n2)O(n2)O(n)O(1)是冒泡排序O(n2)O(n2)O(n)O(1)是简单选择排序O(n2)O(n2)O(n2)O(1)是希尔排序-O(nlog2n)~O(n2)O(nlog2n


首先通过图表比较不同排序算法的时间复杂度和稳定性。

排序方法

平均时间

最坏情况

最好情况

辅助空间

稳定性

直接插入排序

O(n2)

O(n2)

O(n)

O(1)

冒泡排序

O(n2)

O(n2)

O(n)

O(1)

简单选择排序

O(n2)

O(n2)

O(n2)

O(1)

希尔排序-

O(nlog2n)~O(n2)

O(nlog2n)~O(n2)

O(1)

快速排序

O(nlog2n)

O(n2)

O(nlog2n)

O(log2n)

堆排序

O(nlog2n)

O(nlog2n)

O(nlog2n)

O(1)

2-路归并排序

O(nlog2n)

O(nlog2n)

O(nlog2n)

O(n)

基数排序O(d(n + rd))O(d(n + rd))O(d(n + rd))O(rd)

注:1. 算法的时间复杂度一般情况下指最坏情况下的渐近时间复杂度。

        2. 排序算法的稳定性会对多关键字排序产生影响。

下面通过C#代码说明不同的排序算法

插入排序

时间复杂度:平均情况—O(n2) 最坏情况—O(n2) 辅助空间:O(1) 稳定性:稳定

插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。

希尔排序(shell)

时间复杂度:理想情况—O(nlog2n) 最坏情况—O(n2) 稳定性:不稳定

希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比o(n^2)好一些。由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。

冒泡排序

时间复杂度:平均情况—O(n2) 最坏情况—O(n2) 辅助空间:O(1) 稳定性:稳定

冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,我想你是不会再无聊地把他们俩交换一下的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。

快速排序

时间复杂度:平均情况—O(nlog2n) 最坏情况—O(n2) 辅助空间:O(log2n) 稳定性:不稳定

快速排序有两个方向,左边的i下标一直往右走,当a[i] <= a[center_index],其中center_index是中枢元素的数组下标,一般取为数组第0个元素。而右边的j下标一直往左走,当a[j] > a[center_index]。如果i和j都走不动了,i <= j, 交换a[i]和a[j],重复上面的过程,直到i>j。 交换a[j]和a[center_index],完成一趟快速排序。在中枢元素和a[j]交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为 5 3 3 4 3 8 9 10 11, 现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定的排序算法,不稳定发生在中枢元素和a[j]交换的时刻。

C# 代码   复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<span><img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/None.gif" alt=""><span>
<img id="Highlighter_45_648_Open_Image" src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedBlockStart.gif" alt=""><span>int<span> Partition(SqList <span>&amp;<span>L, <span>int<span> low, <span>int<span> high) <span id="Highlighter_45_648_Open_Text"><span>{
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>//<span> 交换顺序表L中子序列L.r[low..high]的记录,使枢轴记录到位,
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>//<span> 并返回其所在位置,此时,在它之前(后)的记录均不大(小)于它<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> KeyType pivotkey;
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> RedType temp;
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> pivotkey <span>=<span> L.r[low].key; <span>//<span> 用子表的第一个记录作枢轴记录<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img id="Highlighter_241_600_Open_Image" src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedSubBlockStart.gif" alt=""> <span>while<span> (low <span>&lt;<span> high) <span id="Highlighter_241_600_Open_Text"><span>{ <span>//<span> 从表的两端交替地向中间扫描<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>while<span> (low <span>&lt;<span> high <span>&amp;&amp;<span> L.r[high].key<span>&gt;=<span>pivotkey) <span>--<span>high;
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> temp<span>=<span>L.r[low];
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> L.r[low]<span>=<span>L.r[high];
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> L.r[high]<span>=<span>temp; <span>//<span> 将比枢轴记录小的记录交换到低端<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>while<span> (low <span>&lt;<span> high <span>&amp;&amp;<span> L.r[low].key <span>&lt;<span> <span>=<span>pivotkey) <span>++<span>low;
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> temp<span>=<span>L.r[low];
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> L.r[low]<span>=<span>L.r[high];
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> L.r[high]<span>=<span>temp; <span>//<span> 将比枢轴记录大的记录交换到高端<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedSubBlockEnd.gif" alt=""> }<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>return<span> low; <span>//<span> 返回枢轴所在位置<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedBlockEnd.gif" alt="">}<span> <span>//<span> Partition <span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/None.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/None.gif" alt="">
<img id="Highlighter_713_1006_Open_Image" src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedBlockStart.gif" alt=""><span>void<span> QSort(SqList <span>&amp;<span>L, <span>int<span> low, <span>int<span> high) <span id="Highlighter_713_1006_Open_Text"><span>{
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>//<span> 对顺序表L中的子序列L.r[low..high]进行快速排序<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>int<span> pivotloc;
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img id="Highlighter_790_1003_Open_Image" src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedSubBlockStart.gif" alt=""> <span>if<span> (low <span>&lt;<span> high) <span id="Highlighter_790_1003_Open_Text"><span>{ <span>//<span> 长度大于1<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> pivotloc <span>=<span> Partition(L, low, high); <span>//<span> 将L.r[low..high]一分为二<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> QSort(L, low, pivotloc<span>-<span>1<span>); <span>//<span> 对低子表递归排序,pivotloc是枢轴位置<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> QSort(L, pivotloc<span>+<span>1<span>, high); <span>//<span> 对高子表递归排序<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedSubBlockEnd.gif" alt=""> }<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedBlockEnd.gif" alt="">}<span> <span>//<span> QSort <span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/None.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/None.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/None.gif" alt="">
<img id="Highlighter_1052_1102_Open_Image" src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedBlockStart.gif" alt=""><span>void<span> QuickSort(SqList <span>&amp;<span>L) <span id="Highlighter_1052_1102_Open_Text"><span>{
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>//<span> 对顺序表L进行快速排序<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> QSort(L, <span>1<span>, L.length);
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedBlockEnd.gif" alt="">}<span> <span>//<span> QuickSort </span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>

选择排序

时间复杂度:平均情况—O(n2) 最坏情况—O(n2) 辅助空间:O(1) 稳定性:不稳定

选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。比较拗口,举个例子,序列5 8 5 2 9, 我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。

堆排序

时间复杂度:平均情况—O(nlog2n) 最坏情况—O(nlog2n) 辅助空间:O(1) 稳定性:不稳定

我们知道堆的结构是节点i的孩子为2*i和2*i+1节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n的序列,堆排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n/2-1, n/2-2, …1这些个父节点选择元素时,就会破坏稳定性。有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法

归并排序

时间复杂度:平均情况—O(nlog2n) 最坏情况—O(nlog2n) 辅助空间:O(n) 稳定性:稳定

归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。所以,归并排序也是稳定的排序算法。

C# 代码   复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<span>
<img id="Highlighter_61_479_Open_Image" src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedBlockStart.gif" alt=""><span>void<span> Merge (RedType SR[], RedType TR[], <span>int<span> i, <span>int<span> m, <span>int<span> n) <span id="Highlighter_61_479_Open_Text"><span>{
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>//<span> 将有序的SR[i..m]和SR[m+1..n]归并为有序的TR[i..n]<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>int<span> j,k;
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img id="Highlighter_167_280_Open_Image" src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedSubBlockStart.gif" alt=""> <span>for<span> (j<span>=<span>m<span>+<span>1<span>, k<span>=<span>i; i <span>&lt;<span> <span>=<span>m <span>&amp;&amp;<span> j <span>&lt;<span> <span>=<span>n; <span>++<span>k) <span id="Highlighter_167_280_Open_Text"><span>{
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>//<span> 将SR中记录由小到大地并入TR<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>if<span> LQ(SR[i].key,SR[j].key) TR[k] <span>=<span> SR[i<span>++<span>];
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>else<span> TR[k] <span>=<span> SR[j<span>++<span>];
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedSubBlockEnd.gif" alt=""> }<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>if<span> (i <span>&lt;<span> <span>=<span>m) <span>//<span> TR[k..n] = SR[i..m]; 将剩余的SR[i..m]复制到TR<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>while<span> (k <span>&lt;<span> <span>=<span>n <span>&amp;&amp;<span> i <span>&lt;<span> <span>=<span>m) TR[k<span>++<span>]<span>=<span>SR[i<span>++<span>];
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>if<span> (j <span>&lt;<span> <span>=<span>n) <span>//<span> 将剩余的SR[j..n]复制到TR<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>while<span> (k <span>&lt;<span> <span>=<span>n <span>&amp;&amp;<span>j <span>&lt;<span> <span>=<span>n) TR[k<span>++<span>]<span>=<span>SR[j<span>++<span>];
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedBlockEnd.gif" alt="">}<span> <span>//<span> Merge <span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/None.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/None.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/None.gif" alt="">
<img id="Highlighter_552_919_Open_Image" src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedBlockStart.gif" alt=""><span>void<span> MSort(RedType SR[], RedType TR1[], <span>int<span> s, <span>int<span> t) <span id="Highlighter_552_919_Open_Text"><span>{
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>//<span> 将SR[s..t]归并排序为TR1[s..t]。<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>int<span> m;
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> RedType TR2[<span>20<span>];
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>if<span> (s<span>==<span>t) TR1[t] <span>=<span> SR[s];
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img id="Highlighter_657_916_Open_Image" src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedSubBlockStart.gif" alt=""> <span>else<span> <span id="Highlighter_657_916_Open_Text"><span>{
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> m<span>=<span>(s<span>+<span>t)<span>/<span>2<span>; <span>//<span> 将SR[s..t]平分为SR[s..m]和SR[m+1..t]<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> MSort(SR,TR2,s,m); <span>//<span> 递归地将SR[s..m]归并为有序的TR2[s..m]<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> MSort(SR,TR2,m<span>+<span>1<span>,t); <span>//<span> 将SR[m+1..t]归并为有序的TR2[m+1..t]<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> Merge(TR2,TR1,s,m,t); <span>//<span> 将TR2[s..m]和TR2[m+1..t]归并到TR1[s..t]<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedSubBlockEnd.gif" alt=""> }<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedBlockEnd.gif" alt="">}<span> <span>//<span> MSort <span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/None.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/None.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/None.gif" alt="">
<img id="Highlighter_964_1019_Open_Image" src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedBlockStart.gif" alt=""><span>void<span> MergeSort(SqList <span>&amp;<span>L) <span id="Highlighter_964_1019_Open_Text"><span>{
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> <span>//<span> 对顺序表L作归并排序。<span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""><span>
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt=""> MSort(L.r, L.r, <span>1<span>, L.length);
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/InBlock.gif" alt="">
<img src="C%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E6%AF%94%E8%BE%83%20-%20r163%20-%20%E5%8D%9A%E5%AE%A2%E5%9B%AD/ExpandedBlockEnd.gif" alt="">}<span> <span>//<span> MergeSort </span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>

首先给出项目的结构

IPublish.cs的源码:

复制代码

1 namespace NsWebChat.PublishSubscribe 2 {
3 ///


4 /// 发布事件基础接口 5 ///

6 public interface IPublish 7 {
8 string PublishTag { get; set; }
9 object PublishData { get; set; } 10 } 11 }

复制代码

IPubSubCenter.cs的源码:

复制代码

1 namespace NsWebChat.PublishSubscribe 2 {
3 ///


4 /// 订阅发布中心基础接口 5 ///

6 public interface IPubSubCenter 7 {
8 ///
9 /// 订阅事件 10 ///

11 bool AddSubscribe(string tag, Func<ISubscribe, object, object> method, object data, string name); 12 bool AddSubscribe(string tag, Func<ISubscribe, object, object> method, string name); 13 bool AddSubscribe(Subscribe subscribe); 14 ///
15 /// 获得指定事件的订阅列表 16 ///

17 SubscribeList GetSubscribeList(string tag); 18 ///
19 /// 获得订阅 20 ///

21 Subscribe GetSubscribe(string tag, string name); 22 ///
23 /// 删除订阅 24 ///

25 void RemoveSubscribe(string tag, string name); 26
27 ///
28 /// 发布事件 29 ///

30 IEnumerable Publish(string tag, object data); 31 IEnumerable Publish(string tag); 32 IEnumerable Publish(Publish publish); 33 } 34 }

复制代码

ISubscribe.cs的源码:

复制代码

1 namespace NsWebChat.PublishSubscribe 2 {
3 ///


4 /// 订阅事件基础接口 5 ///

6 public interface ISubscribe 7 {
8 ///
9 /// 订阅的事件 10 ///

11 string SubscribeTag { get; set; } 12 ///
13 /// 订阅的名称 14 ///

15 string SubscribeName { get; set; } 16 ///
17 /// 响应发布时传递的参数 18 ///

19 object Data { get; set; } 20 ///
21 /// 响应发布的方法 22 ///

23 Func<ISubscribe, object, object> SubscribeMethod { get; set; } 24 ///
25 /// 调用订阅方法 26 ///

27 object Invoke(object val); 28 } 29 }

复制代码

Publish.cs的源码:

复制代码

1 namespace NsWebChat.PublishSubscribe 2 {
3 ///


4 /// 发布事件基类 5 ///

6 public class Publish : IPublish 7 {
8 private string _publishTag; 9 private object _publishData; 10
11 public Publish() 12 { 13 } 14
15 public Publish(string tag) 16 : this(tag, new object()) 17 { 18
19 } 20
21 public Publish(string tag, object val) 22 { 23 _publishTag = tag; 24 _publishData = val; 25 } 26
27 public string PublishTag 28 { 29 get
30 { 31 return _publishTag; 32 } 33 set
34 { 35 _publishTag = value; 36 } 37 } 38
39 public object PublishData 40 { 41 get
42 { 43 return _publishData; 44 } 45 set
46 { 47 _publishData = value; 48 } 49 } 50 } 51 }

复制代码

PubSubCenter.cs的源码:

复制代码

1 namespace NsWebChat.PublishSubscribe 2 {
3 ///


4 /// 订阅发布中心基类 5 ///

6 public class PubSubCenter : IPubSubCenter 7
8 {
9 private Dictionary<string, SubscribeList> _pubsubList = new Dictionary<string, SubscribeList>();
10
11 public bool AddSubscribe(string tag, Func<ISubscribe, object, object> method, object data ,string name) 12 {
13 Subscribe subscribe = new Subscribe(); 14 subscribe.Data = data; 15 subscribe.SubscribeMethod = method; 16 subscribe.SubscribeTag = tag; 17 subscribe.SubscribeName = name; 18 return AddSubscribe(subscribe); 19 }
20
21 public bool AddSubscribe(string tag, Func<ISubscribe, object, object> method, string name) 22 {
23 return AddSubscribe(tag, method, new object(),name);
24 }
25
26 public bool AddSubscribe(Subscribe subscribe) 27 {
28 if (subscribe == null)
29 {
30 return false;
31 }
32 else
33 {
34 if (String.IsNullOrEmpty(subscribe.SubscribeName)) 35 {
36 return false;
37 }
38 Subscribe subscribet = GetSubscribe(subscribe.SubscribeTag, subscribe.SubscribeName); 39 if (subscribet != null)
40 {
41 return false;
42 }
43 else
44 {
45 if (_pubsubList.ContainsKey(subscribe.SubscribeTag)) 46 {
47 _pubsubList[subscribe.SubscribeTag].Add(subscribe);
48 }
49 else
50 {
51 SubscribeList list = new SubscribeList(); 52 list.Add(subscribe);
53 _pubsubList.Add(subscribe.SubscribeTag, list);
54 }
55 return true;
56 }
57 }
58 }
59
60 public SubscribeList GetSubscribeList(string tag) 61 {
62 if (_pubsubList.ContainsKey(tag)) 63 {
64 return _pubsubList[tag]; 65 }
66 else
67 {
68 return new SubscribeList(); 69 }
70 }
71
72 public Subscribe GetSubscribe(string tag, string name) 73 {
74 if (_pubsubList.ContainsKey(tag)) 75 {
76 foreach (Subscribe item in _pubsubList[tag]) 77 {
78 if (item.SubscribeName == name) 79 {
80 return item; 81 }
82 }
83 return null;
84 }
85 else
86 {
87 return null;
88 }
89 }
90
91 public void RemoveSubscribe(string tag, string name) 92 {
93 Subscribe subscribe = GetSubscribe(tag, name); 94 _pubsubList[tag].Remove(subscribe);
95 }
96
97 public IEnumerable<object> Publish(string tag, object data) 98 {
99 if (_pubsubList.ContainsKey(tag)) 100 { 101 foreach (Subscribe item in _pubsubList[tag]) 102 { 103 yield return item.Invoke(data); 104 } 105 } 106 else
107 { 108 yield break; 109 } 110 } 111
112 public IEnumerable<object> Publish(string tag) 113 { 114 return Publish(tag, new object()); 115 } 116
117 public IEnumerable<object> Publish(Publish publish) 118 { 119 return Publish(publish.PublishTag, publish.PublishData); 120 } 121 } 122 }

复制代码

Subscribe.cs源码:

复制代码

1 namespace NsWebChat.PublishSubscribe 2 {
3 ///


4 /// 订阅事件基类 5 ///

6 public class Subscribe : ISubscribe 7 {
8 private object _data; 9 private Func<ISubscribe, object, object> _subscribeMethod; 10 private string _subscribeTag; 11 private string _subscribeName; 12
13 public Subscribe() 14 { 15 } 16
17 public Subscribe(string tag, Func<ISubscribe, object, object> method) 18 : this(tag, method, “”) 19 { 20
21 } 22
23 public Subscribe(string tag, Func<ISubscribe, object, object> method, object data) 24 : this(tag, method, data, “”) 25 { 26
27 } 28
29 public Subscribe(string tag, Func<ISubscribe, object, object> method,string name) 30 : this(tag, method, new object(), name) 31 { 32
33 } 34
35 public Subscribe(string tag, Func<ISubscribe, object, object> method, object data,string name) 36 { 37 _data = data; 38 _subscribeTag = tag; 39 _subscribeMethod = method; 40 _subscribeName = name; 41 } 42
43 public object Data 44 { 45 get
46 { 47 return _data; 48 } 49 set
50 { 51 _data = value; 52 } 53 } 54
55 public Func<ISubscribe, object, object> SubscribeMethod 56 { 57 get
58 { 59 return _subscribeMethod; 60 } 61 set
62 { 63 _subscribeMethod = value; 64 } 65 } 66
67 public object Invoke(object val) 68 { 69 return SubscribeMethod(this, val); 70 } 71
72 public string SubscribeTag 73 { 74 get
75 { 76 return _subscribeTag; 77 } 78 set
79 { 80 _subscribeTag = value; 81 } 82 } 83
84 public string SubscribeName 85 { 86 get
87 { 88 return _subscribeName; 89 } 90 set
91 { 92 _subscribeName = value; 93 } 94 } 95 } 96 }

复制代码

SubscribeList.cs的源码:

复制代码

1 namespace NsWebChat.PublishSubscribe 2 {
3 ///


4 /// 订阅列表 5 ///

6 public class SubscribeList : IList
7 {
8 private List subscribes = new List();
9
10 public int IndexOf(Subscribe item) 11 { 12 return subscribes.IndexOf(item); 13 } 14
15 public void Insert(int index, Subscribe item) 16 { 17 subscribes.Insert(index, item); 18 } 19
20 public void RemoveAt(int index) 21 { 22 subscribes.RemoveAt(index); 23 } 24
25 public Subscribe this[int index] 26 { 27 get
28 { 29 return subscribes[index]; 30 } 31 set
32 { 33 subscribes[index] = value; 34 } 35 } 36
37 public void Add(Subscribe item) 38 { 39 subscribes.Add(item); 40 } 41
42 public void Clear() 43 { 44 subscribes.Clear(); 45 } 46
47 public bool Contains(Subscribe item) 48 { 49 return subscribes.Contains(item); 50 } 51
52 public void CopyTo(Subscribe[] array, int arrayIndex) 53 { 54 subscribes.CopyTo(array, arrayIndex); 55 } 56
57 public int Count 58 { 59 get
60 { 61 return subscribes.Count; 62 } 63 } 64
65 public bool IsReadOnly 66 { 67 get { return false; } 68 } 69
70 public bool Remove(Subscribe item) 71 { 72 return subscribes.Remove(item); 73 } 74
75 public IEnumerator GetEnumerator() 76 { 77 return subscribes.GetEnumerator(); 78 } 79
80 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 81 { 82 return subscribes.GetEnumerator(); 83 } 84 } 85 }

复制代码

下面是单元测试的源码(PubSubCenterTest.cs):

复制代码

1 namespace WebChatUnitTest 2 {
3 [TestClass]
4 public class PubSubCenterTest 5 {
6 public object Test1(ISubscribe subscribe, object data) 7 {
8 Assert.IsTrue(subscribe.Data == data); 9 return data; 10 }
11
12 public object Test2(ISubscribe s, object d) 13 {
14 return d; 15 }
16
17 public object Test3(ISubscribe s, object d) 18 {
19 return d; 20 }
21
22 public object Test4(ISubscribe s, object d) 23 {
24 return d; 25 }
26
27 public object Test5(ISubscribe s, object d) 28 {
29 return s.SubscribeName; 30 }
31
32 //测试删除订阅
33 [TestMethod]
34 public void TestRemoveSubscribe() 35 {
36 PubSubCenter pbc = new PubSubCenter(); 37 pbc.AddSubscribe(“t1”, Test5, “1”);
38 pbc.AddSubscribe(“t1”, Test5, “2”);
39 pbc.RemoveSubscribe(“t1”, “2”);
40 List<string> al = new List<string>();
41 foreach (string item in pbc.Publish(“t1”))
42 {
43 al.Add(item);
44 }
45 Assert.IsTrue(al.Count == 1);
46 Assert.IsTrue(al[0] == “1”);
47 pbc.AddSubscribe(“t1”, Test5, “2”);
48 pbc.RemoveSubscribe(“t1”, “1”);
49 al.Clear();
50 foreach (string item in pbc.Publish(“t1”))
51 {
52 al.Add(item);
53 }
54 Assert.IsTrue(al.Count == 1);
55 Assert.IsTrue(al[0] == “2”);
56 }
57
58 //测试重载版本的订阅发布
59 [TestMethod]
60 public void TestMoreSubscribeAndPublish() 61 {
62 PubSubCenter pbc = new PubSubCenter(); 63 Subscribe subscribe = new Subscribe(“t1”, Test2, “2”, “1”);
64 Subscribe subscribe2 = new Subscribe(“t1”, Test2, “2”);
65 Subscribe subscribe3 = new Subscribe(); 66 subscribe3.SubscribeMethod = Test2; 67 subscribe3.SubscribeName = “3”;
68 subscribe3.SubscribeTag = “t1”;
69 pbc.AddSubscribe(subscribe);
70 pbc.AddSubscribe(subscribe2);
71 pbc.AddSubscribe(subscribe3);
72 pbc.AddSubscribe(“t1”, Test2, “4”);
73 pbc.AddSubscribe(“t1”, Test2, “2”, “5”);
74 List<object> al = new List<object>();
75 foreach (object item in pbc.Publish(“t1”))
76 {
77 al.Add(item);
78 }
79 Assert.IsTrue(al.Count == 5);
80 Assert.IsNotNull(al[0]);
81 Assert.IsNotNull(al[1]);
82 Assert.IsNotNull(al[2]);
83 Assert.IsNotNull(al[3]);
84 Assert.IsNotNull(al[4]);
85 al.Clear();
86 foreach (object item in pbc.Publish(“t1”,”2”))
87 {
88 al.Add(item);
89 }
90 Assert.IsTrue(al.Count == 5);
91 Assert.IsNotNull(al[0]);
92 Assert.IsNotNull(al[1]);
93 Assert.IsNotNull(al[2]);
94 Assert.IsNotNull(al[3]);
95 Assert.IsNotNull(al[4]);
96
97 Publish pub = new Publish(“t1”);
98 al.Clear();
99 foreach (object item in pbc.Publish(pub)) 100 { 101 al.Add(item); 102 } 103 Assert.IsTrue(al.Count == 5); 104 Assert.IsNotNull(al[0]); 105 Assert.IsNotNull(al[1]); 106 Assert.IsNotNull(al[2]); 107 Assert.IsNotNull(al[3]); 108 Assert.IsNotNull(al[4]); 109
110 pub = new Publish(“t1”, “2”); 111 al.Clear(); 112 foreach (object item in pbc.Publish(pub)) 113 { 114 al.Add(item); 115 } 116 Assert.IsTrue(al.Count == 5); 117 Assert.IsNotNull(al[0]); 118 Assert.IsNotNull(al[1]); 119 Assert.IsNotNull(al[2]); 120 Assert.IsNotNull(al[3]); 121 Assert.IsNotNull(al[4]); 122
123 pub = new Publish(); 124 pub.PublishData = “2”; 125 pub.PublishTag = “t1”; 126 al.Clear(); 127 foreach (object item in pbc.Publish(pub)) 128 { 129 al.Add(item); 130 } 131 Assert.IsTrue(al.Count == 5); 132 Assert.IsNotNull(al[0]); 133 Assert.IsNotNull(al[1]); 134 Assert.IsNotNull(al[2]); 135 Assert.IsNotNull(al[3]); 136 Assert.IsNotNull(al[4]); 137 } 138
139 //测试重名订阅事件
140 [TestMethod] 141 public void TestSameNameSubscribe() 142 { 143 PubSubCenter pbc = new PubSubCenter(); 144 pbc.AddSubscribe(“t1”, Test2, “2”); 145 pbc.AddSubscribe(“t1”, Test2, “2”); 146 pbc.AddSubscribe(“t1”, Test2, “3”); 147 List<string> al = new List<string>(); 148 foreach (string item in pbc.Publish(“t1”, “123”)) 149 { 150 al.Add(item); 151 } 152 Assert.IsTrue(al.Count == 2); 153 Assert.IsTrue(al[0] == “123”); 154 Assert.IsTrue(al[1] == “123”); 155 } 156
157 //测试多方法订阅单事件
158 [TestMethod] 159 public void TestMoreSubscribeToOnePublish() 160 { 161 PubSubCenter pbc = new PubSubCenter(); 162 pbc.AddSubscribe(“t1”, Test2, “1”); 163 pbc.AddSubscribe(“t1”, Test3, “2”); 164 pbc.AddSubscribe(“t1”, Test4, “3”); 165 List<string> al = new List<string>(); 166 foreach (string item in pbc.Publish(“t1”, “2”)) 167 { 168 al.Add(item); 169 } 170 Assert.IsTrue(al.Count == 3); 171 Assert.IsTrue(al[0] == “2”); 172 Assert.IsTrue(al[1] == “2”); 173 Assert.IsTrue(al[2] == “2”); 174 } 175
176 //测试单方法订阅多事件
177 [TestMethod] 178 public void TestOneSubscribeToMorePublish() 179 { 180 PubSubCenter pbc = new PubSubCenter(); 181 pbc.AddSubscribe(“t1”, Test1, “2”, “t1”); 182 pbc.AddSubscribe(“t2”, Test1, “3”, “t2”); 183 pbc.AddSubscribe(“t3”, Test1, “4”, “t3”); 184 pbc.AddSubscribe(“t4”, Test1, “5”, “t4”); 185 IList al = new List(); 186 foreach(String item in pbc.Publish(“t1”,”2”)) 187 { 188 al.Add(item); 189 } 190 Assert.IsTrue(al.Count == 1); 191 Assert.IsTrue(al[0] == “2”); 192 al.Clear(); 193 foreach (String item in pbc.Publish(“t2”, “3”)) 194 { 195 al.Add(item); 196 } 197 Assert.IsTrue(al.Count == 1); 198 Assert.IsTrue(al[0] == “3”); 199 al.Clear(); 200 foreach (String item in pbc.Publish(“t3”, “4”)) 201 { 202 al.Add(item); 203 } 204 Assert.IsTrue(al.Count == 1); 205 Assert.IsTrue(al[0] == “4”); 206 al.Clear(); 207 foreach (String item in pbc.Publish(“t4”, “5”)) 208 { 209 al.Add(item); 210 } 211 Assert.IsTrue(al.Count == 1); 212 Assert.IsTrue(al[0] == “5”); 213 } 214 } 215 }

复制代码

这里主要解决文件夹包含文件夹的解压缩问题。
1)下载SharpZipLib.dll,在http://www.icsharpcode.net/OpenSource/SharpZipLib/Download.aspx中有最新免费版本,“Assemblies for .NET 1.1, .NET 2.0, .NET CF 1.0, .NET CF 2.0: Download [297 KB] ”点击Download可以下载,解压后里边有好多文件夹,因为不同的版本,我用的FW2.0。
2)引用SharpZipLib.dll,在项目中点击项目右键–>添加引用–>浏览,找到要添加的DLL–>确认
3)改写了文件压缩和解压缩的两个类,新建两个类名字为ZipFloClass.cs,UnZipFloClass.cs
源码如下
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;

using System.IO;

using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.GZip;

/// 


/// ZipFloClass 的摘要说明
/// 

public class ZipFloClass
{
    public void ZipFile(string strFile, string strZip)
    {
        if (strFile[strFile.Length - 1] != Path.DirectorySeparatorChar)
            strFile += Path.DirectorySeparatorChar;
        ZipOutputStream s = new ZipOutputStream(File.Create(strZip));
        s.SetLevel(6); // 0 - store only to 9 - means best compression
        zip(strFile, s, strFile);
        s.Finish();
        s.Close();
    }

  private void zip(string rootFilePath, string strFile, ICSharpCode.SharpZipLib.Zip.ZipOutputStream s, string staticFile)
  {
    if (strFile[strFile.Length - 1] != Path.DirectorySeparatorChar) strFile += Path.DirectorySeparatorChar;
    Crc32 crc = new Crc32();
    string[] filenames = Directory.GetFileSystemEntries(strFile);
    foreach (string file in filenames)
    {

      if (Directory.Exists(file))
      {
        zip(rootFilePath, file, s, staticFile);
      }

      else // 否则直接压缩文件
      {
        //打开压缩文件
        FileStream fs = File.OpenRead(file);

        byte[] buffer = new byte[fs.Length];
        fs.Read(buffer, 0, buffer.Length);
        //string tempfile = file.Substring(staticFile.LastIndexOf(“\\“) + 1);
        string tempfile = file.Replace(rootFilePath, “”);
        ICSharpCode.SharpZipLib.Zip.ZipEntry entry = new ICSharpCode.SharpZipLib.Zip.ZipEntry(tempfile);

        entry.DateTime = DateTime.Now;
        entry.Size = fs.Length;
        fs.Close();
        crc.Reset();
        crc.Update(buffer);
        entry.Crc = crc.Value;
        s.PutNextEntry(entry);

        s.Write(buffer, 0, buffer.Length);
      }
    }
  }

}

 、、、、、、、、、、、、、、、

using System;
using System.Data;
using System.Web;
using System.Text;
using System.Collections;
using System.IO;
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;

using ICSharpCode.SharpZipLib.BZip2;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Checksums;

/// 


/// UnZipFloClass 的摘要说明
/// 

public class UnZipFloClass
{

    public string unZipFile(string TargetFile, string fileDir)
    {
        string rootFile = “ “;
        try
        {
            //读取压缩文件(zip文件),准备解压缩
            ZipInputStream s = new ZipInputStream(File.OpenRead(TargetFile.Trim()));
            ZipEntry theEntry;
            string path = fileDir;                   
            //解压出来的文件保存的路径

            string rootDir = “ “;                        
            //根目录下的第一个子文件夹的名称
            while ((theEntry = s.GetNextEntry()) != null)
            {
                rootDir = Path.GetDirectoryName(theEntry.Name);                          
                //得到根目录下的第一级子文件夹的名称
                if (rootDir.IndexOf(“\\“) >= 0)
                {
                    rootDir = rootDir.Substring(0, rootDir.IndexOf(“\\“) + 1);
                }
                string dir = Path.GetDirectoryName(theEntry.Name);                    
                //根目录下的第一级子文件夹的下的文件夹的名称
                string fileName = Path.GetFileName(theEntry.Name);                    
                //根目录下的文件名称
                if (dir != “ “ )                                                        
                    //创建根目录下的子文件夹,不限制级别
                {
                    if (!Directory.Exists(fileDir + “\\“ + dir))
                    {
                        path = fileDir + “\\“ + dir;                                                
                        //在指定的路径创建文件夹
                        Directory.CreateDirectory(path);
                    }
                }
                else if (dir == “ “ && fileName != “”)                                              
                    //根目录下的文件
                {
                    path = fileDir;
                    rootFile = fileName;
                }
                else if (dir != “ “ && fileName != “”)                                              
                    //根目录下的第一级子文件夹下的文件
                {
                    if (dir.IndexOf(“\\“) > 0)                                                            
                        //指定文件保存的路径
                    {
                        path = fileDir + “\\“ + dir;
                    }
                }

                if (dir == rootDir)                                                                                  
                    //判断是不是需要保存在根目录下的文件
                {
                    path = fileDir + “\\“ + rootDir;
                }

                //以下为解压缩zip文件的基本步骤
                //基本思路就是遍历压缩文件里的所有文件,创建一个相同的文件。
                if (fileName != String.Empty)
                {
                    FileStream streamWriter = File.Create(path + “\\“ + fileName);

                    int size = 2048;
                    byte[] data = new byte[2048];
                    while (true)
                    {
                        size = s.Read(data, 0, data.Length);
                        if (size > 0)
                        {
                            streamWriter.Write(data, 0, size);
                        }
                        else
                        {
                            break;
                        }
                    }

                    streamWriter.Close();
                }
            }
            s.Close();

            return rootFile;
        }
        catch (Exception ex)
        {
            return “1; “ + ex.Message;
        }
    }   
}

4)引用,新建一个页面,添加两个按钮,为按钮添加Click事件

源码如下

  protected void Button1_Click(object sender, EventArgs e)
    {
        string[] FileProperties = new string[2];
        FileProperties[0] = “D:\\unzipped\\“;//待压缩文件目录
        FileProperties[1] = “D:\\zip\\a.zip”;  //压缩后的目标文件
        ZipFloClass Zc = new ZipFloClass();
        Zc.ZipFile(FileProperties[0], FileProperties[1]);

    }
    protected void Button2_Click(object sender, EventArgs e)
    {
        string[] FileProperties = new string[2];
        FileProperties[0] = “D:\\zip\\b.zip”;//待解压的文件
        FileProperties[1] = “D:\\unzipped\\“;//解压后放置的目标目录
        UnZipFloClass UnZc = new UnZipFloClass();
        UnZc.unZipFile(FileProperties[0], FileProperties[1]);
    }

5)一切OK,可以测试一下。

C#实现自动升级(附源码)_c#升级客户端-CSDN博客

Excerpt

文章浏览阅读5.2k次。对于PC桌面应用程序而言,自动升级功能往往是必不可少的。而自动升级可以作为一个独立的C/S系统来开发,这样,就可以在不同的桌面应用中进行复用。本文将着重介绍OAUS的相关背景、使用方法,至于详细的实现细节,大家可以直接下载源码研究。如果了解了OAUS的使用,源码的理解就非常容易了。如果需要直接部署使用自动升级系统,那么,可下载文末的可执行程序压缩包。一.OAUS的主要功能  目前主_c#升级客户端


     对于PC桌面应用程序而言,自动升级功能往往是必不可少的。而自动升级可以作为一个独立的C/S系统来开发,这样,就可以在不同的桌面应用中进行复用。本文将着重介绍OAUS的相关背景、使用方法,至于详细的实现细节,大家可以直接下载源码研究。如果了解了OAUS的使用,源码的理解就非常容易了。如果需要直接部署使用自动升级系统,那么,可下载文末的可执行程序压缩包。

一.OAUS的主要功能

  目前主流的程序自动升级策略是,重新下载最新的安装包,然后重新安装整个客户端。这种方式虽然简单直观,但是缺陷也很明显。比如,即使整个客户端有100M,而本次更新仅仅只是修改了一个1k大小的dll,那也意味着要重新下载100M的全部内容。这对带宽是极大的浪费,而且延长了升级了时间,相应地也增加了客户茫然等待的时间。

      在上述的场景中,自动升级时,我们能否只更新那个被修改了的1k的dll了?当然,使用OAUS自动升级系统可以轻松地做到这一点。OAUS自动升级系统可以对被分发的客户端程序中的每个文件进行版本管理,每次升级的基础单元不再是整个客户端程序,而是其中的单个文件。针对单个文件的更新,包括三种形式:

(1)文件被修改。

(2)文件被删除。

(3)新增加某个文件。

      OAUS对这三种形式的文件更新都是支持的。每次自动升级,都可以更改N个文件、删除M个文件、新增加L个文件。

二.OAUS的使用

1.OAUS的结构

      OAUS提供了可直接执行的服务端程序和客户端程序:AutoUpdaterSystem.Server.exe 和 AutoUpdater.exe。 OAUS服务端的目录结构如下所示:

           

      OAUS的客户端与服务器之间通过TCP通信,可以在AutoUpdaterSystem.Server.exe.config配置文件中配置服务器通过哪个TCP端口提供自动升级服务。

      FileFolder文件夹初始是空的,其用于部署被分发的程序的各个文件的最新版本。注意,其下的文件结构一定要与被分发的程序正常部署后的结构完全一致 – 即相当于在FileFolder文件夹下部署一个被分发的程序。

      OAUS客户端的目录结构如下:      

      

       可以在AutoUpdater.exe.config配置文件中配置OAUS服务器的IP、端口等信息,其内容如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
<span><span>&lt;</span><span>configuration</span><span>&gt;</span>
<span>&lt;</span><span>appSettings</span><span>&gt;</span>
<span>&lt;!--</span><span>服务器IP </span><span>--&gt;</span>
<span>&lt;</span><span>add </span><span>key</span><span>="ServerIP"</span><span> value</span><span>="127.0.0.1"</span><span>/&gt;</span>
<span>&lt;!--</span><span>服务器端口</span><span>--&gt;</span>
<span>&lt;</span><span>add </span><span>key</span><span>="ServerPort"</span><span> value</span><span>="4530"</span><span>/&gt;</span>
<span>&lt;!--</span><span>升级完成后,将被回调的可执行程序的名称</span><span>--&gt;</span>
<span>&lt;</span><span>add </span><span>key</span><span>="CallbackExeName"</span><span> value</span><span>="Demo.exe"</span><span>/&gt;</span>
<span>&lt;!--</span><span>主窗体的Title</span><span>--&gt;</span>
<span>&lt;</span><span>add </span><span>key</span><span>="Title"</span><span> value</span><span>="文件更新"</span><span>/&gt;</span>
<span>&lt;/</span><span>appSettings</span><span>&gt;</span>
<span>&lt;/</span><span>configuration</span><span>&gt;</span></span>

      请注意配置的CallbackExeName,其表示当升级完成之后,将被启动的分发程序的exe的名称。这个CallbackExeName配置的为什么是名称而不是路径了?这是因为使用和部署OAUS客户端时是有要求的:

(1)被分发的程序的可执行文件exe必须位于部署目录的根目录。

(2)OAUS的客户端(即整个AutoUpdater文件夹)也必须位于这个根目录。

      如此,AutoUpdater就知道分发程序的exe相对自己的路径,如此就可以确定分发程序的exe的绝对路径,所以就可以在升级完成后启动目标exe了。另外,根据上述的两个约定,再结合前面讲到的服务端的FileFolder文件夹的结构约定,当服务端更新一个文件时,AutoUpdater便可以确定该文件在客户端机器上的绝对路径了。

2. OAUS自动升级流程

         下面我们就详细讲讲如何使用OAUS来构建自动升级系统,大概的步骤如下。

(1)运行OAUS服务端。     

           

(2)将被分发的客户端程序的所有内容放到OAUS服务端的FileFolder文件夹下,其结构与客户端程序正常部署后的结构要完全一致。我们以部署VideoChatSystem为例。 

       

(3)使用OAUS服务端为被分发的客户端程序的每个文件生成默认版本号,并创建版本信息配置文件UpdateConfiguration.xml。这个配置文件也将被客户端使用。

         点击服务端界面上的“文件版本管理”按钮,将弹出用于管理各个文件版本的【文件版本信息】窗体。       

   

   当用新版本的文件覆盖老的文件后,点击“”按钮,服务端就会检索FileFolder文件夹下文件的名称、大小、最后更新时间,然后得出本次更新结果:变化了几个文件、新增了几个文件、删除了几个文件。 自动扫描功能把之前的手动操作全部简化了。 

        如果需要手动操作,可以这样做:

        双击列表中的任意一行,可以修改其对应文件的版本的值(int类型的数值)。注意,此列表中的版本信息与文件的真实版本属性(比如dll的版本属性X.X.X.X)可以是没有任何联系的,列表中版本的值只是用于标记文件是否被修改,所以,文件每被修改一次,其列表中对应的版本的值就应该有所增大。

       当关闭【文件版本信息】窗体时,只要有某个文件版本变化,则“最后综合版本”的值(int类型)会递增1。通过比较OAUS的客户端保存的“最后综合版本”的值与OAUS的服务端最新的“最后综合版本”的值,就可以快速地识别客户端是否已经是最新版本了。

       另外,初次打开这个窗口时,将在OAUS服务端的目录下,自动生成一个版本信息配置文件UpdateConfiguration.xml。而且,每当通过该窗体来设置某个文件的新版本时,UpdateConfiguration.xml会自动同步更新。 

(4)将UpdateConfiguration.xml添加到OAUS的客户端程序(即上述的AutoUpdater的文件夹)中。

(5)在创建被分发的客户端的安装程序时,将OAUS的客户端(即AutoUpdater的文件夹)也打包进去,并且像前面说的一样,要将其直接部署在运行目录(BaseDirectory)下(与分发的exe同一目录)。

         如此,准备工作就完成了,当客户端通过安装包安装好了VideoChatSystem之后,其目录结构像下面这样: 

       

(6)当我们有新的版本要发布时,比如要更新某个文件(因为文件被修改),那么可以这样做:

    a.将修改后的文件拷贝到OAUS服务端的FileFolder文件夹下的正确位置(或覆盖旧的文件)。

    b.在OAUS服务端打开【文件版本信息】窗体,双击被修改文件所对应的Row,在弹出的窗体上修改对应文件的版本号,将版本号的数值增加。(如果是删除旧文件或添加新文件,此处也可进行相应的操作)

(7)如此,当客户端再启动AutoUpdater.exe时,就会自动升级,更新那些发生变化的文件。 以下是AutoUpdater.exe运行起来后的截图。

      

(8)当升级完成后,将启动前述的OAUS客户端配置文件中配置的回调exe。(在本例中就是VideoChatSystem.exe)

(9)OAUS客户端会在日志文件UpdateLog.txt(位于AutoUpdater的文件夹下,在OAUS客户端首次运行时自动生成该文件)中,记录每次自动升级的情况。

(10)如果升级的过程中,与服务端连接中断,则会自动重连,在重连成功后,将启动断点续传。

     

       

3.何时启动自动升级客户端?

       假设某个系统是下载客户端形式的,那么客户端该如何知道是否有新版本了?然后又该何时启动AutoUpdater.exe了?

       我们的经验是这样的:客户端登录成功之后,从服务器获取“最后综合版本”的值,然后与本地的“最后综合版本”的值相比较,如果本地的值较小,则表示客户端需要更新。这个过程可以这样做到:

(1)当在OAUS服务端的FileFolder文件夹下放置了新的文件,并通过【文件版本信息】窗体正确的更新了版本号,在关闭【文件版本信息】窗体时,“最后综合版本”的值会自动加1。

(2)系统客户端可以通过调用OAUS.Core.VersionHelper类的静态方法来判断是否有新版本。

(3)如果HasNewVersion方法返回true,则通常有两种模式:由用户选择是否升级,或者是强制升级。

      一般而言,如果最新客户端程序与老版本兼容,不升级也影响不大,则可以交由用户决定是否升级;如果最新客户端程序不兼容老版本,或者是有重大更新,则将启动强制升级。如果流程要进入启动升级,那么只要启动AutoUpdater的文件夹下AutoUpdater.exe就可以了。要注意的是,启动AutoUpdater.exe进程后,要退出当前的客户端进程,否则,有些文件会因为无法被覆盖而导致更新失败。代码大致如下所示: 

1
2
3
4
5
6
<span><span>  if</span> (<span>VersionHelper</span>.HasNewVersion(oausServerIP,oausServerPort)<span>) 
{
</span><span>string</span> updateExePath = AppDomain.<span>CurrentDomain</span>.BaseDirectory + <span>"</span><span>AutoUpdater\\AutoUpdater.exe</span><span>"</span><span>;
System.Diagnostics.<span>Process</span> myProcess </span>=<span> System.Diagnostics.<span>Process</span>.Start(updateExePath);
......</span><span>//</span><span>退出当前进程 </span>
} </span>

三.相关下载

1.自动升级系统OAUS - 源码

2.自动升级系统OAUS(可直接部署)

    有些情况下需要一个调度器专门来处理一些工作,如在网络处理程序中,当接收的数据后把信息存放到队列中,尽可能的更快地处理下一接收操作.而接收的数据信息则由调试器来处理,如数据分析,数据包处理等等工作.既然调度器负责处理工作,那最好给需要处理的工作制定一个规则,方便以后灵活扩展处理.

制定规则接口

1

2

3

4

5

public interface IWorkItem:IDisposable

{

void Execute();

}

其实接口制定很简单就是一个Execute方法,表示对该工作执行;那为什么还要实现IDisposable呢,目的就是为这工作提供一个释放操作描述,当此项工作完成会有一个释放行为处理相关事情.

调度器实现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

public class Despatch:IDisposable

{

public Despatch()

{

System.Threading.Thread thread = new System.Threading.Thread(

new System.Threading.ThreadStart(Run));

thread.Start();

}

private Queue<IWorkItem> mQueues = new Queue<IWorkItem>(1024);

public void Add(IWorkItem item)

{

lock (mQueues)

{

mQueues.Enqueue(item);

}

}

public int Count

{

get

{

return mQueues.Count;

}

}

protected IWorkItem GetItem()

{

lock (mQueues)

{

if (mQueues.Count > 0)

return mQueues.Dequeue();

return null``;

}

}

protected virtual void OnRun()

{

IWorkItem item = GetItem();

if (item != null``)

{

using (item)

{

item.Execute();

}

}

else

System.Threading.Thread.Sleep(2);

}

public virtual void Run()

{

while (!mDispose)

{

OnRun();

}

}

private bool mDispose = false``;

public void Dispose()

{

lock (``this``)

{

if (!mDispose)

{

mDispose = true``;

}

}

}

}

调度器工作原来就是在构造的时候直接启动一个线程来运行Run方法;该方法的工作就是不停地从一个队列中获取一个工作项目并执行,当没有工作项的情况下把线程Sleep 2毫秒;为什么要Sleep呢,其目的就是不要让while在处理空队列的时候把cpu资源给吃光了.

下面实现一个简单的工作项

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

class ReceiveWorkItem : WorkItem

{

public override void Execute()

{

TcpChannel channel = Channel;

DataBuffer db = channel.ReceiveBuffer;

try

{

db.SetBuffer(SocketAsyncEventArgs.Buffer, SocketAsyncEventArgs.BytesTransferred);

channel.CallDataReceive(``new DataReceiveEventArgs() { Buffer = db, Channel = channel });

}

catch (Exception e_)

{

channel.CallChannelError(``new ChannelErrorEventArgs() { Channel = channel, Exception = e_ });

}

}

public override void Dispose()

{

SocketAsyncEventArgs.Enter();

}

}

以上的工作内容就是把接收的数据放到一个Buffer中,并调用CallDataReceive方法触发数据接收方法;工作完成后调用了SocketAsyncEventArgs.Enter()方法把该对象回收到池中.

C#实现之(自动更新) - 野狼谷 - 博客园

Excerpt

做开发的人,尤其是做客户端(C/S)系统开发的人都会遇到一个头疼的问题,就是软件的自动更新;系统发布后怎样自动的更新程序,在下有幸开发过一个自动更新程序,更新程序与任何宿主程序是完全独立的;只要在主程序里面启动更新程序就行了;更新程序也是一个可执行文件,在启动的时候可以设置是否是自动更新和是否是手动


做开发的人,尤其是做客户端(C/S)系统开发的人都会遇到一个头疼的问题,就是软件的自动更新;系统发布后怎样自动的更新程序,在下有幸开发过一个自动更新程序,更新程序与任何宿主程序是完全独立的;只要在主程序里面启动更新程序就行了;更新程序也是一个可执行文件,在启动的时候可以设置是否是自动更新和是否是手动更新,自动更新的意思就是说不需要人工的干预实现从远程服务器下载更新包,而如果是手动更新就会涉及到用户点击程序中的按钮实现更新;在自动更新与手动更新中可以根据项目的需要进行选择,有的程序必须要求用户进行更新才能继续使用,所以程序自动更新是有必要的;手动更新就是用户可以随时更新程序,不需要严格的控制版本问题;下面本人就来讲一下具体的实现细节,我贴出部分代码,源码属公司财产本人不宜上传;

自动更新的目的就是将服务器上的DLL文件拷贝到本地执行目录中,并且覆盖本地同名的文件;流程很简单,但是实现起来有几个地方需要注意:

1.大批量的DLL文件怎么下载到本地来,有多个DLL文件在下载过程中如果网速慢的情况下可能出现丢包、丢文件等情况;本人的实现是将多个文件通过ICSharpCode.SharpZipLib组件进行打包,这样可以省很多事;(如:动态连接库文件dll的名称在传输过程中大小写可能会变化)

2.下载到本地了,怎么覆盖原有的同名文件;本人的实现是先同名的文件的支持删除,然后解压缩;这个过程需要临时保存删除的文件,防止操作失败程序无法启动,要注意有事务性的原理;

3.如果更新的文件不只是单单的DLL文件可能还有一些无限极的文件夹;本人的实现是如果存在同名的文件夹,直接递归的删除,然后将其解压缩到目录中;由于压缩包解压后的顶级目录是压缩文件的名称,所有在复制的过程中需要注意目录的层次关系;

下面我们来走一下实现的整个流程,虽然没有给出整个源码,但是如果看完这篇文章的你基本实现起来没什么大问题了;

为了部署方便我建议大家麻烦点实现一个部署文件的工具,将所有的文件直接打包在里面同时生成服务器端的版本信息文件;

利用这个工具就很方便的实现了对文件进行压缩、生成HASH值、版本文件、更新地址等信息;

这个XML中保存的是服务当前的版本信息、更新文件的名称、更新文件的HASH值,为什么需要HASH就是怕更新文件在某些情况下被人调包了,如果所有的客户端更新后后果很严重;所以我们必须带上HASH值;

工具生成两个文件,一个是版本文件一个是更新包,服务器的任务已经完成,下面就是具体的客户端的实现;

为了知道何时需要进行版本更新所以要在客户端程序目录中保存一份用来记录版本信息的文件;

文件中保存着当前本地的版本号、服务器的更新地址、宿主程序的名称,需要宿主的名称就能在更新的时候将宿主程序重进程中枚举出来然后关掉,这样就不影响我们更新了,当然也可以实现宿主程序不关闭的情况下更新,如果用到某些已经被宿主程序占用的情况会直接影响更新流程,所以以防万一关了为妙;

这是客户端版本文件中保存的信息;

我们上面说了,更新分为手动和自动,我们先来说手动更新吧,手动更新就是需要用户自己去点击更新按钮然后开始更新,这个问题我们可以利用进程的参数传递解决;

当然在更新程序里面需要有这方面的逻辑判断;

入口的地方我们进行判断,更新方式;这里的下载远程更新包是用WebClient对象,也可以用其他的基于Socket的对象;更新开始之前需要先判断本地的版本号是否小于远程版本号,如果小于在进行更新;

因为下载的过程是异步的所以需要用到后台线程建议大家使用System.ComponentModel.BackgroundWorker这个后台线程对象,他对Thread进行了很好的封装;下面来看一下核心的流程代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

        private void Back_thread_DoWork(object sender, DoWorkEventArgs e)

        {

            try

            {

                downclient = new WebClient();

                downclient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(downclient_DownloadProgressChanged);

                downclient.DownloadFileCompleted += new AsyncCompletedEventHandler(downclient_DownloadFileCompleted);

                downclient.DownloadFileAsync(new Uri(Util.GetUpdateUrl() + "down.zip"), Util.GetDictiory() + "\\down.zip");

            }

            catch (Exception err) { System.Diagnostics.Debug.WriteLine(err); }

        }

        void downclient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)

        {

            try

            {

                if (e.Error != null)

                {

                    eventLog1.WriteEntry(e.Error.ToString());

                    MessageBox.Show("在进行远程更新时,发生错误""信息提示", MessageBoxButtons.OK, MessageBoxIcon.Error);

                }

                else

                {

                    Util.KillProcess();

                    if (Util.IsHash(Util.GetHash(Util.GetDictiory() + "\\down.zip"), FileWork.GetDownHash()))

                    {

                        File.Delete(Util.GetDictiory() + "\\down.zip");

                        File.Delete(Util.GetDictiory() + "\\ServerUpdateFiles.xml");

                        MessageBox.Show("远程服务器更新包已发生变化,无法更新""信息提示", MessageBoxButtons.OK, MessageBoxIcon.Error);

                        eventLog1.WriteEntry("远程服务器中的更新包在制作和下载时间段中数据包发生变化,为了安全期间不给予下载!");

                        this.Close();

                    }

                    else

                    {

                        ReduceToUnReduceFile.unZipFile(Util.GetDictiory() + "\\down.zip", Util.GetDictiory());

                        File.Delete(Util.GetDictiory() + "\\down.zip");

                        FileWork.LookFiles(Util.GetDictiory() + "\\down", Util.GetDictiory());

                        FileWork.CopyFileEvent += new FileWork.CopyFileDelegate(FileWork_CopyFileEvent);

                        FileWork.CopyFiles(Util.GetDictiory() + "\\down", Util.GetDictiory());

                        FileWork.DeleteFiles(Util.GetDictiory() + "\\down");

                        if (EventChainReference.GlobalEventChain.OnAutoUpdateDb())

                            Util.UpdateLocalXml();

                        File.Delete(Util.GetDictiory() + "\\ServerUpdateFiles.xml");

                        MessageBox.Show("升级成功!""信息提示");

                        Util.StartProcess();

                        isupdate = true;

                    }

                }

            }

            catch (Exception err) { eventLog1.WriteEntry(err.ToString()); }

            Application.Exit();

        }

这部分代码是串联整个过程的代码;

自动更新大概就讲完了,几个关键的地方都给出了,希望对大家开发自动更新程序有帮助;

  在企业应用系统领域,会面对不同系统之间的通信、集成与整合,尤其当面临异构系统时,这种分布式的调用与通信变得越发重要。其次,系统中一般会有很多对实时性要求不高的但是执行起来比较较耗时的地方,比如发送短信,邮件提醒,更新文章阅读计数,记录用户操作日志等等,如果实时处理的话,在用户访问量比较大的情况下,对系统压力比较大。

面对这些问题,我们一般会将这些请求,放在消息队列MQ中处理;异构系统之间使用消息进行通讯。

    MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。

  MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取或者订阅队列中的消息。

   RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统。他遵循Mozilla Public License开源协议。 

  消息传递相较文件传递与远程过程调用(RPC)而言,似乎更胜一筹,因为它具有更好的平台无关性,并能够很好地支持并发与异步调用。所以如果系统中出现了如下情况:

  • 对操作的实时性要求不高,而需要执行的任务极为耗时;
  • 存在异构系统间的整合;

  一般的可以考虑引入消息队列。对于第一种情况,常常会选择消息队列来处理执行时间较长的任务。引入的消息队列就成了消息处理的缓冲区。消息队列引入的异步通信机制,使得发送方和接收方都不用等待对方返回成功消息,就可以继续执行下面的代码,从而提高了数据处理的能力。尤其是当访问量和数据流量较大的情况下,就可以结合消息队列与后台任务,通过避开高峰期对大数据进行处理,就可以有效降低数据库处理数据的负荷。 

  本文简单介绍在RabbitMQ这一消息代理工具,以及在.NET中如何使用RabbitMQ.

  2.1 安装Erlang语言运行环境

  由于RabbitMQ使用Erlang语言编写,所以先安装Erlang语言运行环境。具体移步博客:windows配置Erlang环境

  2.2 安装RabbitMQ服务端

  地址 http://www.rabbitmq.com/

  下载安装。

  使RabbitMQ以Windows Service的方式在后台运行:打开cmd切换到sbin目录下执行

rabbitmq-service install
rabbitmq-service enable
rabbitmq-service start

   现在RabbitMQ的服务端已经启动起来了。

  要查看和控制RabbitMQ服务端的状态,可以用rabbitmqctl这个脚本。

  比如查看状态:

  

  假如显示node没有连接上,需要到C:\Windows目录下,将.erlang.cookie文件,拷贝到用户目录下 C:\Users\{用户名},这是Erlang的Cookie文件,允许与Erlang进行交互。

   使用命令查看用户:

  RabbitMQ会为我们创建默认的用户名guest和密码guest,guest默认拥有RabbitMQ的所有权限。

  一般的,我们需要新建一个我们自己的用户,设置密码,并授予权限,并将其设置为管理员,可以使用下面的命令来执行这一操作:

rabbitmqctl add_user JC JayChou //创建用户JC密码为JayChou
rabbitmqctl set_permissions JC “.*“ “.*“ “.*“ //赋予JC读写所有消息队列的权限 rabbitmqctl set_user_tags JC administrator //分配用户组

  修改JC密码为123:

rabbitmqctl change_password JC 123

  删除用户JC:

rabbitmqctl delete_user JC

  也可以开启rabbitmq_management插件,在web界面查看和管理RabbitMQ服务

rabbitmq-plugins enable rabbitmq_management

  2.3下载RabbitMQ的Client端dll

  下载地址:http://www.rabbitmq.com/releases/rabbitmq-dotnet-client/

  本人下载了这个 rabbitmq-dotnet-client-3.6.6-dotnet-4.5.zip

   解压,我们需要的是这个文件,以后会引用到vs的项目中:

  3.1在使用RabitMQ之前,先对几个概念做一下说明

  RabbitMQ是一个消息代理。他从消息生产者(producers)那里接收消息,然后把消息送给消息消费者(consumer)在发送和接受之间,他能够根据设置的规则进行路由,缓存和持久化。

  一般提到RabbitMQ和消息,都用到一些专有名词。

  • 生产(Producing)意思就是发送。发送消息的程序就是一个生产者(producer)。我们一般用”P”来表示:

       producer

  • 队列(queue)就是邮箱的名称。消息通过你的应用程序和RabbitMQ进行传输,它们只能存储在队列(queue)中。 队列(queue)容量没有限制,你要存储多少消息都可以——基本上是一个无限的缓冲区。多个生产者(producers)能够把消息发送给同一个队列,同样,多个消费者(consumers)也能从同一个队列(queue)中获取数据。队列可以画成这样(图上是队列的名称):

     queue

  • 消费(Consuming)和获取消息是一样的意思。一个消费者(consumer)就是一个等待获取消息的程序。我们把它画作”C”:

     consumer

  通常,消息生产者,消息消费者和消息代理不在同一台机器上。

3.2 Hello Word

  下面来展示简单的RabbitMQ的使用:

      rabbitmq hello world

 3.2.1 首先创建名为ProjectSend的控制台项目,需要引用RabbitMQ.Client.dll。这个程序作为Producer生产者,用来发送数据:

复制代码

static void Main(string[] args)
{ var factory = new ConnectionFactory();
factory.HostName = “localhost”;//RabbitMQ服务在本地运行
factory.UserName = “guest”;//用户名
factory.Password = “guest”;//密码 using (var connection = factory.CreateConnection())
{ using (var channel = connection.CreateModel())
{
channel.QueueDeclare(“hello”, false, false, false, null);//创建一个名称为hello的消息队列 string message = “Hello World”; //传递的消息内容 var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(“”, “hello”, null, body); //开始传递
Console.WriteLine(“已发送: {0}”, message);
          Console.ReadLine();
}
}
}

复制代码

  首先,需要创建一个ConnectionFactory,设置目标,由于是在本机,所以设置为localhost,如果RabbitMQ不在本机,只需要设置目标机器的IP地址或者机器名称即可,然后设置前面创建的用户名和密码。

  紧接着要创建一个Channel,如果要发送消息,需要创建一个队列,然后将消息发布到这个队列中。在创建队列的时候,只有RabbitMQ上该队列不存在,才会去创建。消息是以二进制数组的形式传输的,所以如果消息是实体对象的话,需要序列化和然后转化为二进制数组。

  现在客户端发送代码已经写好了,运行之后,消息会发布到RabbitMQ的消息队列中,现在需要编写服务端的代码连接到RabbitMQ上去获取这些消息。

3.2.2创建名为ProjectReceive的控制台项目,引用RabbitMQ.Client.dll。作为Consumer消费者,用来接收数据:

复制代码

static void Main(string[] args)
{ var factory = new ConnectionFactory();
factory.HostName = “localhost”;
factory.UserName = “guest”;
factory.Password = “guest”; using (var connection = factory.CreateConnection())
{ using (var channel = connection.CreateModel())
{
channel.QueueDeclare(“hello”, false, false, false, null); var consumer = new EventingBasicConsumer(channel);
channel.BasicConsume(“hello”, false, consumer);
consumer.Received += (model, ea) => { var body = ea.Body; var message = Encoding.UTF8.GetString(body);
Console.WriteLine(“已接收: {0}”, message);
}; Console.ReadLine(); }
}
}

复制代码

   和发送一样,首先需要定义连接,然后声明消息队列。要接收消息,需要定义一个Consume,然后在接收消息的事件中处理数据。

 3.2.3 现在发送和接收的客户端都写好了,让我们编译执行起来

  发送消息:

  现在,名为hello的消息队列中,发送了一条消息。这条消息存储到了RabbitMQ的服务器上了。使用rabbitmqctl 的list_queues可以查看所有的消息队列,以及里面的消息个数,可以看到,目前Rabbitmq上只有一个消息队列,里面只有一条消息:

  也可以在web管理界面查看此queue的相关信息:

 

  接收消息:

   既然消息已经被接收了,那我们再来看queue的内容:

  可见,消息中的内容在接收之后已被删除了。

3.3 工作队列

  前面的例子展示了如何在指定的消息队列发送和接收消息。

  现在我们创建一个工作队列(work queue)来将一些耗时的任务分发给多个工作者(workers):

   rabbitmq-work queue

  工作队列(work queues, 又称任务队列Task Queues)的主要思想是为了避免立即执行并等待一些占用大量资源、时间的操作完成。而是把任务(Task)当作消息发送到队列中,稍后处理。一个运行在后台的工作者(worker)进程就会取出任务然后处理。当运行多个工作者(workers)时,任务会在它们之间共享。

  这个在网络应用中非常有用,它可以在短暂的HTTP请求中处理一些复杂的任务。在一些实时性要求不太高的地方,我们可以处理完主要操作之后,以消息的方式来处理其他的不紧要的操作,比如写日志等等。

准备

  在第一部分,发送了一个包含“Hello World!”的字符串消息。现在发送一些字符串,把这些字符串当作复杂的任务。这里使用time.sleep()函数来模拟耗时的任务。在字符串中加上点号(.)来表示任务的复杂程度,一个点(.)将会耗时1秒钟。比如”Hello…”就会耗时3秒钟。

对之前示例的send.cs做些简单的调整,以便可以发送随意的消息。这个程序会按照计划发送任务到我们的工作队列中。

复制代码

static void Main(string[] args)
{ var factory = new ConnectionFactory();
factory.HostName = “localhost”;
factory.UserName = “yy”;
factory.Password = “hello!”; using (var connection = factory.CreateConnection())
{ using (var channel = connection.CreateModel())
{
channel.QueueDeclare(“hello”, false, false, false, null); string message = GetMessage(args); var properties = channel.CreateBasicProperties();
properties.DeliveryMode = 2; var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(“”, “hello”, properties, body);
Console.WriteLine(“ set {0}”, message);
}
}

Console.ReadKey();

} private static string GetMessage(string[] args)
{ return ((args.Length > 0) ? string.Join(“ “, args) : “Hello World!”);
}

复制代码

接着我们修改接收端,让他根据消息中的逗点的个数来Sleep对应的秒数:

复制代码

static void Main(string[] args)
{ var factory = new ConnectionFactory();
factory.HostName = “localhost”;
factory.UserName = “yy”;
factory.Password = “hello!”; using (var connection = factory.CreateConnection())
{ using (var channel = connection.CreateModel())
{
channel.QueueDeclare(“hello”, false, false, false, null); var consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume(“hello”, true, consumer); while (true)
{ var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue(); var body = ea.Body; var message = Encoding.UTF8.GetString(body); int dots = message.Split(‘.’).Length - 1;
Thread.Sleep(dots * 1000);

            Console.WriteLine("Received {0}", message);
            Console.WriteLine("Done");
        }
    }
}

}

复制代码

轮询分发

  使用工作队列的一个好处就是它能够并行的处理队列。如果堆积了很多任务,我们只需要添加更多的工作者(workers)就可以了,扩展很简单。

现在,我们先启动两个接收端,等待接受消息,然后启动一个发送端开始发送消息。

Send message queue 

  在cmd条件下,发送了5条消息,每条消息后面的逗点表示该消息需要执行的时长,来模拟耗时的操作。

  然后可以看到,两个接收端依次接收到了发出的消息:

receive message queue 

默认,RabbitMQ会将每个消息按照顺序依次分发给下一个消费者。所以每个消费者接收到的消息个数大致是平均的。 这种消息分发的方式称之为轮询(round-robin)。

3.4 消息响应

当处理一个比较耗时得任务的时候,也许想知道消费者(consumers)是否运行到一半就挂掉。在当前的代码中,当RabbitMQ将消息发送给消费者(consumers)之后,马上就会将该消息从队列中移除。此时,如果把处理这个消息的工作者(worker)停掉,正在处理的这条消息就会丢失。同时,所有发送到这个工作者的还没有处理的消息都会丢失。

我们不想丢失任何任务消息。如果一个工作者(worker)挂掉了,我们希望该消息会重新发送给其他的工作者(worker)。

为了防止消息丢失,RabbitMQ提供了消息响应(acknowledgments)_机制_。消费者会通过一个ack(响应),告诉RabbitMQ已经收到并处理了某条消息,然后RabbitMQ才会释放并删除这条消息。

如果消费者(consumer)挂掉了,没有发送响应,RabbitMQ就会认为消息没有被完全处理,然后重新发送给其他消费者(consumer)。这样,即使工作者(workers)偶尔的挂掉,也不会丢失消息。

消息是没有超时这个概念的;当工作者与它断开连的时候,RabbitMQ会重新发送消息。这样在处理一个耗时非常长的消息任务的时候就不会出问题了。

消息响应默认是开启的。在之前的例子中使用了no_ack=True标识把它关闭。是时候移除这个标识了,当工作者(worker)完成了任务,就发送一个响应。

复制代码

channel.BasicConsume(“hello”, false, consumer); while (true)
{ var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue(); var body = ea.Body; var message = Encoding.UTF8.GetString(body); int dots = message.Split(‘.’).Length - 1;
Thread.Sleep(dots * 1000);

Console.WriteLine("Received {0}", message);
Console.WriteLine("Done");

channel.BasicAck(ea.DeliveryTag, false);

}

复制代码

现在,可以保证,即使正在处理消息的工作者被停掉,这些消息也不会丢失,所有没有被应答的消息会被重新发送给其他工作者.

一个很常见的错误就是忘掉了BasicAck这个方法,这个错误很常见,但是后果很严重. 当客户端退出时,待处理的消息就会被重新分发,但是RabitMQ会消耗越来越多的内存,因为这些没有被应答的消息不能够被释放。调试这种case,可以使用rabbitmqct打印messages_unacknoledged字段。

rabbitmqctl list_queues name messages_ready messages_unacknowledged
Listing queues …
hello 0 0 …done.

3.5 消息持久化

前面已经搞定了即使消费者down掉,任务也不会丢失,但是,如果RabbitMQ Server停掉了,那么这些消息还是会丢失。

当RabbitMQ Server 关闭或者崩溃,那么里面存储的队列和消息默认是不会保存下来的。如果要让RabbitMQ保存住消息,需要在两个地方同时设置:需要保证队列和消息都是持久化的。

首先,要保证RabbitMQ不会丢失队列,所以要做如下设置:

bool durable = true;
channel.QueueDeclare(“hello”, durable, false, false, null);

虽然在语法上是正确的,但是在目前阶段是不正确的,因为我们之前已经定义了一个非持久化的hello队列。RabbitMQ不允许我们使用不同的参数重新定义一个已经存在的同名队列,如果这样做就会报错。现在,定义另外一个不同名称的队列:

bool durable = true;
channel.queueDeclare(“task_queue”, durable, false, false, null);

queueDeclare 这个改动需要在发送端和接收端同时设置。

现在保证了task_queue这个消息队列即使在RabbitMQ Server重启之后,队列也不会丢失。 然后需要保证消息也是持久化的, 这可以通过设置IBasicProperties.SetPersistent 为true来实现:

var properties = channel.CreateBasicProperties();
properties.SetPersistent(true);

需要注意的是,将消息设置为持久化并不能完全保证消息不丢失。虽然他告诉RabbitMQ将消息保存到磁盘上,但是在RabbitMQ接收到消息和将其保存到磁盘上这之间仍然有一个小的时间窗口。 RabbitMQ 可能只是将消息保存到了缓存中,并没有将其写入到磁盘上。持久化是不能够一定保证的,但是对于一个简单任务队列来说已经足够。如果需要消息队列持久化的强保证,可以使用publisher confirms

3.6 公平分发

你可能会注意到,消息的分发可能并没有如我们想要的那样公平分配。比如,对于两个工作者。当奇数个消息的任务比较重,但是偶数个消息任务比较轻时,奇数个工作者始终处理忙碌状态,而偶数个工作者始终处理空闲状态。但是RabbitMQ并不知道这些,他仍然会平均依次的分发消息。

为了改变这一状态,我们可以使用basicQos方法,设置perfetchCount=1 。这样就告诉RabbitMQ 不要在同一时间给一个工作者发送多于1个的消息,或者换句话说。在一个工作者还在处理消息,并且没有响应消息之前,不要给他分发新的消息。相反,将这条新的消息发送给下一个不那么忙碌的工作者。

channel.BasicQos(0, 1, false);

3.7 完整实例

现在将所有这些放在一起:

发送端代码如下:

复制代码

static void Main(string[] args)
{ var factory = new ConnectionFactory();
factory.HostName = “localhost”;
factory.UserName = “yy”;
factory.Password = “hello!”; using (var connection = factory.CreateConnection())
{ using (var channel = connection.CreateModel())
{ bool durable = true;
channel.QueueDeclare(“task_queue”, durable, false, false, null); string message = GetMessage(args); var properties = channel.CreateBasicProperties();
properties.SetPersistent(true); var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(“”, “task_queue”, properties, body);
Console.WriteLine(“ set {0}”, message);
}
}

Console.ReadKey();

} private static string GetMessage(string[] args)
{ return ((args.Length > 0) ? string.Join(“ “, args) : “Hello World!”);
}

复制代码

接收端代码如下:

复制代码

static void Main(string[] args)
{ var factory = new ConnectionFactory();
factory.HostName = “localhost”;
factory.UserName = “yy”;
factory.Password = “hello!”; using (var connection = factory.CreateConnection())
{ using (var channel = connection.CreateModel())
{ bool durable = true;
channel.QueueDeclare(“task_queue”, durable, false, false, null);
channel.BasicQos(0, 1, false); var consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume(“task_queue”, false, consumer); while (true)
{ var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue(); var body = ea.Body; var message = Encoding.UTF8.GetString(body); int dots = message.Split(‘.’).Length - 1;
Thread.Sleep(dots * 1000);

            Console.WriteLine("Received {0}", message);
            Console.WriteLine("Done");

            channel.BasicAck(ea.DeliveryTag, false);
        }
    }
}

}

复制代码

RabbitMQ管理界面,通过该界面可以查看RabbitMQ Server 当前的状态,该界面是以插件形式提供的,并且在安装RabbitMQ的时候已经自带了该插件。需要做的是在RabbitMQ控制台界面中启用该插件,命令如下:

rabbitmq-plugins enable rabbitmq_management

rabbitmq management

现在,在浏览器中输入 http://server-name:15672/ server-name换成机器地址或者域名,如果是本地的,直接用localhost(RabbitMQ 3.0之前版本端口号为55672)在输入之后,弹出登录界面,使用我们之前创建的用户登录。

RabbitMQ Web management .

在该界面上可以看到当前RabbitMQServer的所有状态。

本文简单介绍了消息队列的相关概念,并介绍了RabbitMQ消息代理的基本原理以及在Windows 上如何安装RabbitMQ和在.NET中如何使用RabbitMQ。消息队列在构建分布式系统和提高系统的可扩展性和响应性方面有着很重要的作用,希望本文对您了解消息队列以及如何使用RabbitMQ有所帮助。

C#使用UdpClient发送和接收UDP数据示例 - 无网不进 - 博客园

Excerpt

C#使用UdpClient发送和接收UDP数据示例


复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
<span>  1</span> <span>using</span><span> System;
</span><span> 2</span> <span>using</span><span> System.Collections.Generic;
</span><span> 3</span> <span>using</span><span> System.ComponentModel;
</span><span> 4</span> <span>using</span><span> System.Data;
</span><span> 5</span> <span>using</span><span> System.Drawing;
</span><span> 6</span> <span>using</span><span> System.Linq;
</span><span> 7</span> <span>using</span><span> System.Text;
</span><span> 8</span> <span>using</span><span> System.Threading.Tasks;
</span><span> 9</span> <span>using</span><span> System.Windows.Forms;
</span><span> 10</span>
<span> 11</span> <span>//</span><span>本段代码中需要新增加的命名空间</span>
<span> 12</span> <span>using</span><span> System.Net.Sockets;
</span><span> 13</span> <span>using</span><span> System.Net;
</span><span> 14</span> <span>using</span><span> System.Threading;
</span><span> 15</span>
<span> 16</span> <span>namespace</span><span> WinForm1
</span><span> 17</span> <span>{
</span><span> 18</span> <span>public</span> <span>partial</span> <span>class</span><span> Form1 : Form
</span><span> 19</span> <span> {
</span><span> 20</span> <span>public</span><span> Form1()
</span><span> 21</span> <span> {
</span><span> 22</span> <span> InitializeComponent();
</span><span> 23</span> <span> }
</span><span> 24</span> <span>///</span> <span>&lt;summary&gt;</span>
<span> 25</span> <span>///</span><span> 用于UDP发送的网络服务类
</span><span> 26</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span> 27</span> <span>private</span><span> UdpClient udpcSend;
</span><span> 28</span> <span>///</span> <span>&lt;summary&gt;</span>
<span> 29</span> <span>///</span><span> 用于UDP接收的网络服务类
</span><span> 30</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span> 31</span> <span>private</span><span> UdpClient udpcRecv;
</span><span> 32</span>
<span> 33</span> <span>///</span> <span>&lt;summary&gt;</span>
<span> 34</span> <span>///</span><span> 按钮:发送数据
</span><span> 35</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span> 36</span> <span>///</span> <span>&lt;param name="sender"&gt;&lt;/param&gt;</span>
<span> 37</span> <span>///</span> <span>&lt;param name="e"&gt;&lt;/param&gt;</span>
<span> 38</span> <span>private</span> <span>void</span> btnSend_Click(<span>object</span><span> sender, EventArgs e)
</span><span> 39</span> <span> {
</span><span> 40</span> <span>if</span> (<span>string</span><span>.IsNullOrWhiteSpace(txtSendMssg.Text))
</span><span> 41</span> <span> {
</span><span> 42</span> MessageBox.Show(<span>"</span><span>请先输入待发送内容</span><span>"</span><span>);
</span><span> 43</span> <span>return</span><span>;
</span><span> 44</span> <span> }
</span><span> 45</span>
<span> 46</span> <span>//</span><span> 匿名发送
</span><span> 47</span> <span>//</span><span>udpcSend = new UdpClient(0); </span><span>//</span><span> 自动分配本地IPv4地址
</span><span> 48</span> <span>//</span><span> 实名发送</span>
<span> 49</span> IPEndPoint localIpep = <span>new</span> IPEndPoint(IPAddress.Parse(<span>"</span><span>127.0.0.1</span><span>"</span>), <span>12345</span>); <span>//</span><span> 本机IP,指定的端口号</span>
<span> 50</span> udpcSend = <span>new</span><span> UdpClient(localIpep);
</span><span> 51</span> Thread thrSend = <span>new</span><span> Thread(SendMessage);
</span><span> 52</span> <span> thrSend.Start(txtSendMssg.Text);
</span><span> 53</span> <span> }
</span><span> 54</span>
<span> 55</span> <span>///</span> <span>&lt;summary&gt;</span>
<span> 56</span> <span>///</span><span> 发送信息
</span><span> 57</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span> 58</span> <span>///</span> <span>&lt;param name="obj"&gt;&lt;/param&gt;</span>
<span> 59</span> <span>private</span> <span>void</span> SendMessage(<span>object</span><span> obj)
</span><span> 60</span> <span> {
</span><span> 61</span> <span>string</span> message = (<span>string</span><span>)obj;
</span><span> 62</span> <span>byte</span>[] sendbytes =<span> Encoding.Unicode.GetBytes(message);
</span><span> 63</span> IPEndPoint remoteIpep = <span>new</span> IPEndPoint(IPAddress.Parse(<span>"</span><span>127.0.0.1</span><span>"</span>), <span>8848</span>); <span>//</span><span> 发送到的IP地址和端口号</span>
<span> 64</span> <span> udpcSend.Send(sendbytes, sendbytes.Length, remoteIpep);
</span><span> 65</span> <span> udpcSend.Close();
</span><span> 66</span> <span> ResetTextBox(txtSendMssg);
</span><span> 67</span> <span> }
</span><span> 68</span>
<span> 69</span> <span>///</span> <span>&lt;summary&gt;</span>
<span> 70</span> <span>///</span><span> 开关:在监听UDP报文阶段为true,否则为false
</span><span> 71</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span> 72</span> <span>bool</span> IsUdpcRecvStart = <span>false</span><span>;
</span><span> 73</span> <span>///</span> <span>&lt;summary&gt;</span>
<span> 74</span> <span>///</span><span> 线程:不断监听UDP报文
</span><span> 75</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span> 76</span> <span> Thread thrRecv;
</span><span> 77</span>
<span> 78</span> <span>///</span> <span>&lt;summary&gt;</span>
<span> 79</span> <span>///</span><span> 按钮:接收数据开关
</span><span> 80</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span> 81</span> <span>///</span> <span>&lt;param name="sender"&gt;&lt;/param&gt;</span>
<span> 82</span> <span>///</span> <span>&lt;param name="e"&gt;&lt;/param&gt;</span>
<span> 83</span> <span>private</span> <span>void</span> btnRecv_Click(<span>object</span><span> sender, EventArgs e)
</span><span> 84</span> <span> {
</span><span> 85</span> <span>if</span> (!IsUdpcRecvStart) <span>//</span><span> 未监听的情况,开始监听</span>
<span> 86</span> <span> {
</span><span> 87</span> IPEndPoint localIpep = <span>new</span> IPEndPoint(IPAddress.Parse(<span>"</span><span>127.0.0.1</span><span>"</span>), <span>8848</span>); <span>//</span><span> 本机IP和监听端口号</span>
<span> 88</span> udpcRecv = <span>new</span><span> UdpClient(localIpep);
</span><span> 89</span> thrRecv = <span>new</span><span> Thread(ReceiveMessage);
</span><span> 90</span> <span> thrRecv.Start();
</span><span> 91</span> IsUdpcRecvStart = <span>true</span><span>;
</span><span> 92</span> ShowMessage(txtRecvMssg, <span>"</span><span>UDP监听器已成功启动</span><span>"</span><span>);
</span><span> 93</span> <span> }
</span><span> 94</span> <span>else</span> <span>//</span><span> 正在监听的情况,终止监听</span>
<span> 95</span> <span> {
</span><span> 96</span> thrRecv.Abort(); <span>//</span><span> 必须先关闭这个线程,否则会异常</span>
<span> 97</span> <span> udpcRecv.Close();
</span><span> 98</span> IsUdpcRecvStart = <span>false</span><span>;
</span><span> 99</span> ShowMessage(txtRecvMssg, <span>"</span><span>UDP监听器已成功关闭</span><span>"</span><span>);
</span><span>100</span> <span> }
</span><span>101</span> <span> }
</span><span>102</span>
<span>103</span> <span>///</span> <span>&lt;summary&gt;</span>
<span>104</span> <span>///</span><span> 接收数据
</span><span>105</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span>106</span> <span>///</span> <span>&lt;param name="obj"&gt;&lt;/param&gt;</span>
<span>107</span> <span>private</span> <span>void</span> ReceiveMessage(<span>object</span><span> obj)
</span><span>108</span> <span> {
</span><span>109</span> IPEndPoint remoteIpep = <span>new</span> IPEndPoint(IPAddress.Any, <span>0</span><span>);
</span><span>110</span> <span>while</span> (<span>true</span><span>)
</span><span>111</span> <span> {
</span><span>112</span> <span>try</span>
<span>113</span> <span> {
</span><span>114</span> <span>byte</span>[] bytRecv = udpcRecv.Receive(<span>ref</span><span> remoteIpep);
</span><span>115</span> <span>string</span> message = Encoding.Unicode.GetString(bytRecv, <span>0</span><span>, bytRecv.Length);
</span><span>116</span> ShowMessage(txtRecvMssg,<span>string</span>.Format(<span>"</span><span>{0}[{1}]</span><span>"</span><span>, remoteIpep, message));
</span><span>117</span> <span> }
</span><span>118</span> <span>catch</span><span> (Exception ex)
</span><span>119</span> <span> {
</span><span>120</span> <span> ShowMessage(txtRecvMssg, ex.Message);
</span><span>121</span> <span>break</span><span>;
</span><span>122</span> <span> }
</span><span>123</span> <span> }
</span><span>124</span> <span> }
</span><span>125</span>
<span>126</span> <span>//</span><span> 向RichTextBox中添加文本</span>
<span>127</span> <span>delegate</span> <span>void</span> ShowMessageDelegate(RichTextBox txtbox, <span>string</span><span> message);
</span><span>128</span> <span>private</span> <span>void</span> ShowMessage(RichTextBox txtbox, <span>string</span><span> message)
</span><span>129</span> <span> {
</span><span>130</span> <span>if</span><span> (txtbox.InvokeRequired)
</span><span>131</span> <span> {
</span><span>132</span> ShowMessageDelegate showMessageDelegate =<span> ShowMessage;
</span><span>133</span> txtbox.Invoke(showMessageDelegate, <span>new</span> <span>object</span><span>[] { txtbox, message });
</span><span>134</span> <span> }
</span><span>135</span> <span>else</span>
<span>136</span> <span> {
</span><span>137</span> txtbox.Text += message + <span>"</span><span>\r\n</span><span>"</span><span>;
</span><span>138</span> <span> }
</span><span>139</span> <span> }
</span><span>140</span>
<span>141</span> <span>//</span><span> 清空指定RichTextBox中的文本</span>
<span>142</span> <span>delegate</span> <span>void</span><span> ResetTextBoxDelegate(RichTextBox txtbox);
</span><span>143</span> <span>private</span> <span>void</span><span> ResetTextBox(RichTextBox txtbox)
</span><span>144</span> <span> {
</span><span>145</span> <span>if</span><span> (txtbox.InvokeRequired)
</span><span>146</span> <span> {
</span><span>147</span> ResetTextBoxDelegate resetTextBoxDelegate =<span> ResetTextBox;
</span><span>148</span> txtbox.Invoke(resetTextBoxDelegate, <span>new</span> <span>object</span><span>[] { txtbox });
</span><span>149</span> <span> }
</span><span>150</span> <span>else</span>
<span>151</span> <span> {
</span><span>152</span> txtbox.Text = <span>""</span><span>;
</span><span>153</span> <span> }
</span><span>154</span> <span> }
</span><span>155</span>
<span>156</span> <span>///</span> <span>&lt;summary&gt;</span>
<span>157</span> <span>///</span><span> 关闭程序,强制退出
</span><span>158</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span>159</span> <span>///</span> <span>&lt;param name="sender"&gt;&lt;/param&gt;</span>
<span>160</span> <span>///</span> <span>&lt;param name="e"&gt;&lt;/param&gt;</span>
<span>161</span> <span>private</span> <span>void</span> Form1_FormClosing(<span>object</span><span> sender, FormClosingEventArgs e)
</span><span>162</span> <span> {
</span><span>163</span> Environment.Exit(<span>0</span><span>);
</span><span>164</span> <span> }
</span><span>165</span> <span> }
</span><span>166</span> }

复制代码

C#加密算法总结 - WeihanLi - 博客园

Excerpt

1.MD5加密
2.AES加密
3.SHA1加密
4.SHA256加密
5.SHA384加密
6.SHA512加密
7.DES加密
8.加密方法封装


C#加密算法总结

MD5加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<span> 1</span>         <span>///</span> <span>&lt;summary&gt;</span>
<span> 2</span> <span>///</span><span> MD5加密
</span><span> 3</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span> 4</span> <span>///</span> <span>&lt;param name="strPwd"&gt;</span><span>原字符串</span><span>&lt;/param&gt;</span>
<span> 5</span> <span>///</span> <span>&lt;returns&gt;</span><span>加密后字符串</span><span>&lt;/returns&gt;</span>
<span> 6</span> <span>public</span> <span>static</span> <span>string</span> GetMD5(<span>string</span><span> strPwd)
</span><span> 7</span> <span> {
</span><span> 8</span> <span>//</span><span>MD5 对象创建的两种方式
</span><span> 9</span> <span>//</span><span>MD5 md5 = MD5.Create();</span>
<span>10</span> MD5 md5 = <span>new</span><span> MD5CryptoServiceProvider();
</span><span>11</span> <span>//</span><span>将输入的密码转换成字节数组</span>
<span>12</span> <span>byte</span>[] bPwd =<span> Encoding.UTF8.GetBytes(strPwd);
</span><span>13</span> <span>//</span><span>计算指定字节数组的哈希值</span>
<span>14</span> <span>byte</span>[] bMD5 =<span> md5.ComputeHash(bPwd);
</span><span>15</span> <span>//</span><span>释放加密服务提供类的所有资源</span>
<span>16</span> <span> md5.Clear();
</span><span>17</span> StringBuilder sbMD5Pwd = <span>new</span><span> StringBuilder();
</span><span>18</span> <span>for</span> (<span>int</span> i = <span>0</span>; i &lt; bMD5.Length; i++<span>)
</span><span>19</span> <span> {
</span><span>20</span> <span>//</span><span>将每个字节数据转换为2位的16进制的字符</span>
<span>21</span> sbMD5Pwd.Append(bMD5[i].ToString(<span>"</span><span>x2</span><span>"</span><span>));
</span><span>22</span> <span> }
</span><span>23</span> <span>return</span><span> sbMD5Pwd.ToString();
</span><span>24</span> }

SHA1加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<span> 1</span>         <span>///</span> <span>&lt;summary&gt;</span>
<span> 2</span> <span>///</span><span> 使用 SHA1 加密算法来加密
</span><span> 3</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span> 4</span> <span>///</span> <span>&lt;param name="sourceString"&gt;</span><span>原字符串</span><span>&lt;/param&gt;</span>
<span> 5</span> <span>///</span> <span>&lt;returns&gt;</span><span>加密后字符串</span><span>&lt;/returns&gt;</span>
<span> 6</span> <span>public</span> <span>static</span> <span>string</span> SHA1_Encrypt(<span>string</span><span> sourceString)
</span><span> 7</span> <span> {
</span><span> 8</span> <span>byte</span>[] StrRes =<span> Encoding.UTF8.GetBytes(sourceString);
</span><span> 9</span> HashAlgorithm iSHA = <span>new</span><span> SHA1CryptoServiceProvider();
</span><span>10</span> StrRes =<span> iSHA.ComputeHash(StrRes);
</span><span>11</span> StringBuilder EnText = <span>new</span><span> StringBuilder();
</span><span>12</span> <span>foreach</span> (<span>byte</span> iByte <span>in</span><span> StrRes)
</span><span>13</span> <span> {
</span><span>14</span> EnText.AppendFormat(<span>"</span><span>{0:x2}</span><span>"</span><span>, iByte);
</span><span>15</span> <span> }
</span><span>16</span> <span>return</span><span> EnText.ToString();
</span><span>17</span> }

SHA256加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<span> 1</span>         <span>///</span> <span>&lt;summary&gt;</span>
<span> 2</span> <span>///</span><span> SHA256 加密
</span><span> 3</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span> 4</span> <span>///</span> <span>&lt;param name="sourceString"&gt;</span><span>原字符串</span><span>&lt;/param&gt;</span>
<span> 5</span> <span>///</span> <span>&lt;returns&gt;</span><span>加密后字符串</span><span>&lt;/returns&gt;</span>
<span> 6</span> <span>public</span> <span>static</span> <span>string</span> SHA256_Encrypt(<span>string</span><span> sourceString)
</span><span> 7</span> <span> {
</span><span> 8</span> <span>byte</span>[] data =<span> Encoding.UTF8.GetBytes(sourceString);
</span><span> 9</span> SHA256 shaM =<span> SHA256.Create();
</span><span>10</span> <span>byte</span>[] result =<span> shaM.ComputeHash(data);
</span><span>11</span> StringBuilder EnText = <span>new</span><span> StringBuilder();
</span><span>12</span> <span>foreach</span> (<span>byte</span> iByte <span>in</span><span> result)
</span><span>13</span> <span> {
</span><span>14</span> EnText.AppendFormat(<span>"</span><span>{0:x2}</span><span>"</span><span>, iByte);
</span><span>15</span> <span> }
</span><span>16</span> <span>return</span><span> EnText.ToString();
</span><span>17</span> }

SHA384加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<span> 1</span>         <span>///</span> <span>&lt;summary&gt;</span>
<span> 2</span> <span>///</span><span> SHA384 加密
</span><span> 3</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span> 4</span> <span>///</span> <span>&lt;param name="sourceString"&gt;</span><span>原字符串</span><span>&lt;/param&gt;</span>
<span> 5</span> <span>///</span> <span>&lt;returns&gt;</span><span>加密后字符串</span><span>&lt;/returns&gt;</span>
<span> 6</span> <span>public</span> <span>static</span> <span>string</span> SHA384_Encrypt(<span>string</span><span> sourceString)
</span><span> 7</span> <span> {
</span><span> 8</span> <span>byte</span>[] data =<span> Encoding.UTF8.GetBytes(sourceString);
</span><span> 9</span> SHA384 shaM =<span> SHA384.Create();
</span><span>10</span> <span>byte</span>[] result =<span> shaM.ComputeHash(data);
</span><span>11</span> StringBuilder EnText = <span>new</span><span> StringBuilder();
</span><span>12</span> <span>foreach</span> (<span>byte</span> iByte <span>in</span><span> result)
</span><span>13</span> <span> {
</span><span>14</span> EnText.AppendFormat(<span>"</span><span>{0:x2}</span><span>"</span><span>, iByte);
</span><span>15</span> <span> }
</span><span>16</span> <span>return</span><span> EnText.ToString();
</span><span>17</span> }

SHA512加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<span> 1</span>         <span>///</span> <span>&lt;summary&gt;</span>
<span> 2</span> <span>///</span><span> SHA512_加密
</span><span> 3</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span> 4</span> <span>///</span> <span>&lt;param name="sourceString"&gt;</span><span>原字符串</span><span>&lt;/param&gt;</span>
<span> 5</span> <span>///</span> <span>&lt;returns&gt;</span><span>加密后字符串</span><span>&lt;/returns&gt;</span>
<span> 6</span> <span>public</span> <span>static</span> <span>string</span> SHA512_Encrypt(<span>string</span><span> sourceString)
</span><span> 7</span> <span> {
</span><span> 8</span> <span>byte</span>[] data =<span> Encoding.UTF8.GetBytes(sourceString);
</span><span> 9</span> SHA512 shaM = <span>new</span><span> SHA512Managed();
</span><span>10</span> <span>byte</span>[] result =<span> shaM.ComputeHash(data);
</span><span>11</span> StringBuilder EnText = <span>new</span><span> StringBuilder();
</span><span>12</span> <span>foreach</span> (<span>byte</span> iByte <span>in</span><span> result)
</span><span>13</span> <span> {
</span><span>14</span> EnText.AppendFormat(<span>"</span><span>{0:x2}</span><span>"</span><span>, iByte);
</span><span>15</span> <span> }
</span><span>16</span> <span>return</span><span> EnText.ToString();
</span><span>17</span> }

AES加密、解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<span> 1</span>         <span>///</span> <span>&lt;summary&gt;</span>
<span> 2</span> <span>///</span><span> AES加密
</span><span> 3</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span> 4</span> <span>///</span> <span>&lt;param name="encryptStr"&gt;</span><span>加密字符串</span><span>&lt;/param&gt;</span>
<span> 5</span> <span>///</span> <span>&lt;param name="encryptKey"&gt;</span><span>密钥</span><span>&lt;/param&gt;</span>
<span> 6</span> <span>///</span> <span>&lt;returns&gt;</span><span>密码</span><span>&lt;/returns&gt;</span>
<span> 7</span> <span>public</span> <span>static</span> <span>string</span> AESEncrypt(<span>string</span> encryptStr, <span>string</span><span> encryptKey)
</span><span> 8</span> <span> {
</span><span> 9</span> <span>if</span> (<span>string</span><span>.IsNullOrWhiteSpace(encryptStr))
</span><span>10</span> <span>return</span> <span>string</span><span>.Empty;
</span><span>11</span>
<span>12</span> encryptKey = StringHelper.SubString(encryptKey, <span>32</span><span>);
</span><span>13</span> encryptKey = encryptKey.PadRight(<span>32</span>, <span>'</span> <span>'</span><span>);
</span><span>14</span>
<span>15</span> <span>//</span><span>分组加密算法</span>
<span>16</span> SymmetricAlgorithm des =<span> Rijndael.Create();
</span><span>17</span> <span>byte</span>[] inputByteArray = Encoding.UTF8.GetBytes(encryptStr);<span>//</span><span>得到需要加密的字节数组
</span><span>18</span> <span>//</span><span>设置密钥及密钥向量</span>
<span>19</span> des.Key =<span> Encoding.UTF8.GetBytes(encryptKey);
</span><span>20</span> des.IV =<span> _aeskeys;
</span><span>21</span> <span>byte</span>[] cipherBytes = <span>null</span><span>;
</span><span>22</span> <span>using</span> (MemoryStream ms = <span>new</span><span> MemoryStream())
</span><span>23</span> <span> {
</span><span>24</span> <span>using</span> (CryptoStream cs = <span>new</span><span> CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))
</span><span>25</span> <span> {
</span><span>26</span> cs.Write(inputByteArray, <span>0</span><span>, inputByteArray.Length);
</span><span>27</span> <span> cs.FlushFinalBlock();
</span><span>28</span> cipherBytes = ms.ToArray();<span>//</span><span>得到加密后的字节数组</span>
<span>29</span> <span> cs.Close();
</span><span>30</span> <span> ms.Close();
</span><span>31</span> <span> }
</span><span>32</span> <span> }
</span><span>33</span> <span>return</span><span> Convert.ToBase64String(cipherBytes);
</span><span>34</span> <span> }
</span><span>35</span>
<span>36</span> <span>///</span> <span>&lt;summary&gt;</span>
<span>37</span> <span>///</span><span> AES解密
</span><span>38</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span>39</span> <span>///</span> <span>&lt;param name="decryptStr"&gt;</span><span>解密字符串</span><span>&lt;/param&gt;</span>
<span>40</span> <span>///</span> <span>&lt;param name="decryptKey"&gt;</span><span>密钥</span><span>&lt;/param&gt;</span>
<span>41</span> <span>///</span> <span>&lt;returns&gt;</span><span>原码</span><span>&lt;/returns&gt;</span>
<span>42</span> <span>public</span> <span>static</span> <span>string</span> AESDecrypt(<span>string</span> decryptStr, <span>string</span><span> decryptKey)
</span><span>43</span> <span> {
</span><span>44</span> <span>if</span> (<span>string</span><span>.IsNullOrWhiteSpace(decryptStr))
</span><span>45</span> <span>return</span> <span>string</span><span>.Empty;
</span><span>46</span>
<span>47</span> decryptKey = StringHelper.SubString(decryptKey, <span>32</span><span>);
</span><span>48</span> decryptKey = decryptKey.PadRight(<span>32</span>, <span>'</span> <span>'</span><span>);
</span><span>49</span>
<span>50</span> <span>byte</span>[] cipherText =<span> Convert.FromBase64String(decryptStr);
</span><span>51</span>
<span>52</span> SymmetricAlgorithm des =<span> Rijndael.Create();
</span><span>53</span> des.Key =<span> Encoding.UTF8.GetBytes(decryptKey);
</span><span>54</span> des.IV =<span> _aeskeys;
</span><span>55</span> <span>byte</span>[] decryptBytes = <span>new</span> <span>byte</span><span>[cipherText.Length];
</span><span>56</span> <span>using</span> (MemoryStream ms = <span>new</span><span> MemoryStream(cipherText))
</span><span>57</span> <span> {
</span><span>58</span> <span>using</span> (CryptoStream cs = <span>new</span><span> CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Read))
</span><span>59</span> <span> {
</span><span>60</span> cs.Read(decryptBytes, <span>0</span><span>, decryptBytes.Length);
</span><span>61</span> <span> cs.Close();
</span><span>62</span> <span> ms.Close();
</span><span>63</span> <span> }
</span><span>64</span> <span> }
</span><span>65</span> <span>return</span> Encoding.UTF8.GetString(decryptBytes).Replace(<span>"</span><span>\0</span><span>"</span>, <span>""</span>);<span>//</span><span>将字符串后尾的'\0'去掉</span>
<span>66</span> }

DES加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<span> 1</span>         <span>///</span> <span>&lt;summary&gt;</span>
<span> 2</span> <span>///</span><span> DES加密字符串
</span><span> 3</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span> 4</span> <span>///</span> <span>&lt;param name="encryptString"&gt;</span><span>待加密的字符串</span><span>&lt;/param&gt;</span>
<span> 5</span> <span>///</span> <span>&lt;param name="encryptKey"&gt;</span><span>加密密钥,要求为8位</span><span>&lt;/param&gt;</span>
<span> 6</span> <span>///</span> <span>&lt;returns&gt;</span><span>加密成功返回加密后的字符串,失败返回源串</span><span>&lt;/returns&gt;</span>
<span> 7</span> <span>public</span> <span>string</span> EncryptDES(<span>string</span> encryptString, <span>string</span><span> encryptKey)
</span><span> 8</span> <span> {
</span><span> 9</span> <span>try</span>
<span>10</span> <span> {
</span><span>11</span> <span>byte</span>[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(<span>0</span>, <span>8</span><span>));
</span><span>12</span> <span>byte</span>[] rgbIV =<span> Keys;
</span><span>13</span> <span>byte</span>[] inputByteArray =<span> Encoding.UTF8.GetBytes(encryptString);
</span><span>14</span> DESCryptoServiceProvider dCSP = <span>new</span><span> DESCryptoServiceProvider();
</span><span>15</span> MemoryStream mStream = <span>new</span><span> MemoryStream();
</span><span>16</span> CryptoStream cStream = <span>new</span><span> CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
</span><span>17</span> cStream.Write(inputByteArray, <span>0</span><span>, inputByteArray.Length);
</span><span>18</span> <span> cStream.FlushFinalBlock();
</span><span>19</span> <span>return</span><span> Convert.ToBase64String(mStream.ToArray());
</span><span>20</span> <span> }
</span><span>21</span> <span>catch</span>
<span>22</span> <span> {
</span><span>23</span> <span>return</span><span> encryptString;
</span><span>24</span> <span> }
</span><span>25</span> <span> }
</span><span>26</span>
<span>27</span> <span>///</span> <span>&lt;summary&gt;</span>
<span>28</span> <span>///</span><span> DES解密字符串
</span><span>29</span> <span>///</span> <span>&lt;/summary&gt;</span>
<span>30</span> <span>///</span> <span>&lt;param name="decryptString"&gt;</span><span>待解密的字符串</span><span>&lt;/param&gt;</span>
<span>31</span> <span>///</span> <span>&lt;param name="decryptKey"&gt;</span><span>解密密钥,要求为8位,和加密密钥相同</span><span>&lt;/param&gt;</span>
<span>32</span> <span>///</span> <span>&lt;returns&gt;</span><span>解密成功返回解密后的字符串,失败返源串</span><span>&lt;/returns&gt;</span>
<span>33</span> <span>public</span> <span>string</span> DecryptDES(<span>string</span> decryptString, <span>string</span><span> decryptKey)
</span><span>34</span> <span> {
</span><span>35</span> <span>try</span>
<span>36</span> <span> {
</span><span>37</span> <span>byte</span>[] rgbKey = Encoding.UTF8.GetBytes(decryptKey.Substring(<span>0</span>, <span>8</span><span>));
</span><span>38</span> <span>byte</span>[] rgbIV =<span> Keys;
</span><span>39</span> <span>byte</span>[] inputByteArray =<span> Convert.FromBase64String(decryptString);
</span><span>40</span> DESCryptoServiceProvider DCSP = <span>new</span><span> DESCryptoServiceProvider();
</span><span>41</span> MemoryStream mStream = <span>new</span><span> MemoryStream();
</span><span>42</span> CryptoStream cStream = <span>new</span><span> CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
</span><span>43</span> cStream.Write(inputByteArray, <span>0</span><span>, inputByteArray.Length);
</span><span>44</span> <span> cStream.FlushFinalBlock();
</span><span>45</span> <span>return</span><span> Encoding.UTF8.GetString(mStream.ToArray());
</span><span>46</span> <span> }
</span><span>47</span> <span>catch</span>
<span>48</span> <span> {
</span><span>49</span> <span>return</span><span> decryptString;
</span><span>50</span> <span> }
</span><span>51</span> }

加密方法封装代码:

View Code

C#动态调用WCF接口,两种方式任你选。 - Danny Chen - 博客园

Excerpt

REFERENCE FROM : http://www.cnblogs.com/Leo_wl/p/4762784.html 写在前面 接触WCF还是它在最初诞生之处,一个分布式应用的巨作。 从开始接触到现在断断续续,真正使用的项目少之又少,更谈不上深入WCF内部实现机制和原理去研究,最近自己做一个项


REFERENCE FROM : http://www.cnblogs.com/Leo_wl/p/4762784.html

写在前面

           接触WCF还是它在最初诞生之处,一个分布式应用的巨作。 从开始接触到现在断断续续,真正使用的项目少之又少,更谈不上深入WCF内部实现机制和原理去研究,最近自己做一个项目时用到了WCF。 从这个小项目中我学会了两个地方: 1、利用IIS部署WCF服务,也就是大家接触到的发布SVC文件。2、动态调用WCF接口。

           在这个项目中接触WCF时遇到的其实不仅仅是这两个问题,甚至连IIS支持SVC文件也让我折腾了好几把,IIS都重新卸载了两次。 我在这篇文章里用两种方式来实现。

如何使用

      1、第一种方式比较简单,而且也是大家喜欢的,因为不需要任何配置文件就可解决,只需知道服务契约接口和服务地址就可以调用。

      2、使用Invoke的方式,但是需要在调用客户端配置WCF,配置后在Invoke类里封装服务契约接口即可。

客户端调用DEMO

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

<system.serviceModel>

   <behaviors>

     <endpointBehaviors>

       <behavior name="NewBehavior">

         <dataContractSerializer maxItemsInObjectGraph="65536000" />

       </behavior>

     </endpointBehaviors>

   </behaviors>

   <bindings>

     <basicHttpBinding>    

       <binding name="BasicHttpBinding_IDoubleService"

                closeTimeout="01:00:00"

                openTimeout="01:00:00"

                sendTimeout="01:00:00"

                receiveTimeout="01:00:00"

                maxBufferSize="2147483647"

                maxBufferPoolSize="524288"

                maxReceivedMessageSize="2147483647">

         <readerQuotas maxDepth="128" maxStringContentLength="2147483647" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />

       </binding>

     </basicHttpBinding>

     <netMsmqBinding>

       <binding name="NetMsmqBinding_IAsyncSender">

         <security mode="None" />

       </binding>

     </netMsmqBinding>

   </bindings>

   <client>

               binding="basicHttpBinding"

               bindingConfiguration="BasicHttpBinding_IDoubleService"

               contract="DoubleStone.WebHost.IDoubleService"

               name="BasicHttpBinding_IDoubleService" />

   </client>

 </system.serviceModel>

第一种调用方式

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

public class WcfInvokeFactory

   {

       #region WCF服务工厂

       public static T CreateServiceByUrl<T>(string url)

       {

           return CreateServiceByUrl<T>(url, "basicHttpBinding");

       }

       public static T CreateServiceByUrl<T>(string url, string bing)

       {

           try

           {

               if (string.IsNullOrEmpty(url)) throw new NotSupportedException("This url is not Null or Empty!");

               EndpointAddress address = new EndpointAddress(url);

               Binding binding = CreateBinding(bing);

               ChannelFactory<T> factory = new ChannelFactory<T>(binding, address);

               return factory.CreateChannel();

           }

           catch (Exception ex)

           {

               throw new Exception("创建服务工厂出现异常.");

           }

       }

       #endregion

       #region 创建传输协议

       /// <summary>

       /// 创建传输协议

       /// </summary>

       /// <param name="binding">传输协议名称</param>

       /// <returns></returns>

       private static Binding CreateBinding(string binding)

       {

           Binding bindinginstance = null;

           if (binding.ToLower() == "basichttpbinding")

           {

               BasicHttpBinding ws = new BasicHttpBinding();

               ws.MaxBufferSize = 2147483647;

               ws.MaxBufferPoolSize = 2147483647;

               ws.MaxReceivedMessageSize = 2147483647;

               ws.ReaderQuotas.MaxStringContentLength = 2147483647;

               ws.CloseTimeout = new TimeSpan(0, 30, 0);

               ws.OpenTimeout = new TimeSpan(0, 30, 0);

               ws.ReceiveTimeout = new TimeSpan(0, 30, 0);

               ws.SendTimeout = new TimeSpan(0, 30, 0);

               bindinginstance = ws;

           }

           else if (binding.ToLower() == "nettcpbinding")

           {

               NetTcpBinding ws = new NetTcpBinding();

               ws.MaxReceivedMessageSize = 65535000;

               ws.Security.Mode = SecurityMode.None;

               bindinginstance = ws;

           }

           else if (binding.ToLower() == "wshttpbinding")

           {

               WSHttpBinding ws = new WSHttpBinding(SecurityMode.None);

               ws.MaxReceivedMessageSize = 65535000;

               ws.Security.Message.ClientCredentialType = System.ServiceModel.MessageCredentialType.Windows;

               ws.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Windows;

               bindinginstance = ws;

           }

           return bindinginstance;

       }

       #endregion

   }

第二种调用方式

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class WCFInvoke

    {

        /// <summary>

        /// 你需要调用的服务契约

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <param name="func"></param>

        /// <returns></returns>

        public static T Invoke<T>(Func<IDoubleService, T> func)

        {

            IServiceInvoker serviceInvoker=new WCFServiceInvoker();

            return serviceInvoker.InvokeService(func);

        }

    }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

public interface IServiceInvoker

    {

        void InvokeService<T>(Action<T> invokeHandler) where T : class;

        TReslt InvokeService<T, TReslt>(Func<T, TReslt> invokeHandler) where T : class;

    }

public class WCFServiceInvoker:IServiceInvoker

    {

        private static readonly ChannelFactoryManager FactoryManager = new ChannelFactoryManager();

        private static readonly ClientSection ClientSection =

            ConfigurationManager.GetSection("system.serviceModel/client"as ClientSection;

        public void InvokeService<T>(Action<T> invokeHandler) where T : class

        {

            KeyValuePair<stringstring> endpointNameAddressPair = GetEndpointNameAddressPair(typeof(T));

            var arg = FactoryManager.CreateChannel<T>(endpointNameAddressPair.Key, endpointNameAddressPair.Value);

            var obj2 = (ICommunicationObject)arg;

            try

            {

                invokeHandler(arg);

            }

            finally

            {

                try

                {

                    if (obj2.State != CommunicationState.Faulted)

                    {

                        obj2.Close();

                    }

                }

                catch

                {

                    obj2.Abort();

                }

            }

        }

        public TReslt InvokeService<T, TReslt>(Func<T, TReslt> invokeHandler) where T : class

        {

            KeyValuePair<stringstring> endpointNameAddressPair = GetEndpointNameAddressPair(typeof(T));

            var arg = FactoryManager.CreateChannel<T>(endpointNameAddressPair.Key, endpointNameAddressPair.Value);

            var obj2 = (ICommunicationObject)arg;

            try

            {

                return invokeHandler(arg);

            }

            finally

            {

                try

                {

                    if (obj2.State != CommunicationState.Closed || obj2.State != CommunicationState.Faulted)

                    {

                        obj2.Close();

                    }

                }

                catch

                {

                    obj2.Abort();

                }

            }

        }

        private KeyValuePair<stringstring> GetEndpointNameAddressPair(Type serviceContractType)

        {

            var configException =

                new ConfigurationErrorsException(

                    string.Format(

                        "No client endpoint found for type {0}. Please add the section <client><endpoint name=\"myservice\" address=\"http://address/\" binding=\"basicHttpBinding\" contract=\"{0}\"/></client> in the config file.",

                        serviceContractType));

            if (((ClientSection == null) || (ClientSection.Endpoints == null)) || (ClientSection.Endpoints.Count < 1))

            {

                throw configException;

            }

            foreach (ChannelEndpointElement element in ClientSection.Endpoints)

            {

                if (element.Contract == serviceContractType.ToString())

                {

                    return new KeyValuePair<stringstring>(element.Name, element.Address.AbsoluteUri);

                }

            }

            throw configException;

        }

    }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

public class ChannelFactoryManager : IDisposable

    {

        private static readonly Dictionary<Type, ChannelFactory> Factories = new Dictionary<Type, ChannelFactory>();

        private static readonly object SyncRoot = new object();

        public void Dispose()

        {

            Dispose(true);

        }

        public virtual T CreateChannel<T>() where T : class

        {

            return CreateChannel<T>("*"null);

        }

        public virtual T CreateChannel<T>(string endpointConfigurationName) where T : class

        {

            return CreateChannel<T>(endpointConfigurationName, null);

        }

        public virtual T CreateChannel<T>(string endpointConfigurationName, string endpointAddress) where T : class

        {

            T local = GetFactory<T>(endpointConfigurationName, endpointAddress).CreateChannel();

            ((IClientChannel)local).Faulted += ChannelFaulted;

            return local;

        }

        protected virtual ChannelFactory<T> GetFactory<T>(string endpointConfigurationName, string endpointAddress)

            where T : class

        {

            lock (SyncRoot)

            {

                ChannelFactory factory;

                if (!Factories.TryGetValue(typeof(T), out factory))

                {

                    factory = CreateFactoryInstance<T>(endpointConfigurationName, endpointAddress);

                    Factories.Add(typeof(T), factory);

                }

                return (factory as ChannelFactory<T>);

            }

        }

        private ChannelFactory CreateFactoryInstance<T>(string endpointConfigurationName, string endpointAddress)

        {

            ChannelFactory factory = null;

            factory = !string.IsNullOrEmpty(endpointAddress) ? new ChannelFactory<T>(endpointConfigurationName, new EndpointAddress(endpointAddress)) : new ChannelFactory<T>(endpointConfigurationName);

            factory.Faulted += FactoryFaulted;

            factory.Open();

            return factory;

        }

        private void ChannelFaulted(object sender, EventArgs e)

        {

            var channel = (IClientChannel)sender;

            try

            {

                channel.Close();

            }

            catch

            {

                channel.Abort();

            }

        }

        private void FactoryFaulted(object sender, EventArgs args)

        {

            var factory = (ChannelFactory)sender;

            try

            {

                factory.Close();

            }

            catch

            {

                factory.Abort();

            }

            Type[] genericArguments = factory.GetType().GetGenericArguments();

            if ((genericArguments.Length == 1))

            {

                Type key = genericArguments[0];

                if (Factories.ContainsKey(key))

                {

                    Factories.Remove(key);

                }

            }

        }

        protected virtual void Dispose(bool disposing)

        {

            if (disposing)

            {

                lock (SyncRoot)

                {

                    foreach (Type type in Factories.Keys)

                    {

                        ChannelFactory factory = Factories[type];

                        try

                        {

                            factory.Close();

                        }

                        catch

                        {

                            factory.Abort();

                        }

                    }

                    Factories.Clear();

                }

            }

        }

    }

posted @ 2017-05-02 14:57  Danny Chen  阅读(1554)  评论()  编辑  收藏  举报