0%

C# 经典排序算法大全-CSDN博客

Excerpt

文章浏览阅读84次。C# 经典排序算法大全选择排序using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace sorter{ public class SelectionSorter { private int min; …_c# case复杂排序


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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace sorter

{

public class SelectionSorter

{

private int min;

public void Sort(int[] arr)

{

for (int i = 0; i < arr.Length - 1; ++i)

{

min = i;

for (int j = i + 1; j < arr.Length; ++j)

{

if (arr[j] < arr[min])

{

min = j;

}

}

int t = arr[min];

arr[min] = arr[i];

arr[i] = t;

}

}

}

class Program

{

static void Main(string[] args)

{

int[] arrInt = new int[] { 4, 2, 7, 1, 8, 3, 9, 0, 5, 6 };

SelectionSorter selSor = new SelectionSorter();

selSor.Sort(arrInt);

foreach (int i in arrInt)

{

Console.WriteLine(i);

}

Console.ReadKey();

}

}

}

冒泡排序

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
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace sorter

{

public class EbullitionSorter

{

public void Sort(int[] arr)

{

int i, j, temp;

bool done = false;

j = 1;

while ((j < arr.Length) && (!done))

{

done = true;

for (i = 0; i < arr.Length - j; i++)

{

if (arr[i] > arr[i + 1])

{

done = false;

temp = arr[i];

arr[i] = arr[i + 1];

arr[i + 1] = temp;

}

}

j++;

}

}

}

class Program

{

static void Main(string[] args)

{

int[] arrInt = new int[] { 4, 2, 7, 1, 8, 3, 9, 0, 5, 6 };

EbullitionSorter selSor = new EbullitionSorter();

selSor.Sort(arrInt);

foreach (int i in arrInt)

{

Console.WriteLine(i);

}

Console.ReadKey();

}

}

}

高速排序

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
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace sorter

{

public class QuickSorter

{

private void swap(ref int l, ref int r)

{

int temp;

temp = l;

l = r;

r = temp;

}

public void Sort(int[] list, int low, int high)

{

int pivot;

int l, r;

int mid;

if (high <= low)

{

return;

}

else if (high == low + 1)

{

if (list[low] > list[high])

{

swap(ref list[low], ref list[high]);

}

return;

}

mid = (low + high) >> 1;

pivot = list[mid];

swap(ref list[low], ref list[mid]);

l = low + 1;

r = high;

do

{

while (l <= r && list[l] < pivot)

{

l++;

}

while (list[r] >= pivot)

{

r--;

}

if (l < r)

{

swap(ref list[l], ref list[r]);

}

} while (l < r);

list[low] = list[r];

list[r] = pivot;

if (low + 1 < r)

{

Sort(list, low, r - 1);

}

if (r + 1 < high)

{

Sort(list, r + 1, high);

}

}

}

class Program

{

static void Main(string[] args)

{

int[] arrInt = new int[] { 4, 2, 7, 1, 8, 3, 9, 0, 5, 6 };

QuickSorter selSor = new QuickSorter();

selSor.Sort(arrInt, 0, arrInt.Length - 1);

foreach (int i in arrInt)

{

Console.WriteLine(i);

}

Console.ReadKey();

}

}

}

插入排序

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
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace sorter

{

public class InsertionSorter

{

public void Sort(int[] arr)

{

for (int i = 1; i < arr.Length; i++)

{

int t = arr[i];

int j = i;

while ((j > 0) && (arr[j - 1] > t))

{

arr[j] = arr[j - 1];

--j;

}

arr[j] = t;

}

}

}

class Program

{

static void Main(string[] args)

{

int[] arrInt = new int[] { 4, 2, 7, 1, 8, 3, 9, 0, 5, 6 };

InsertionSorter selSor = new InsertionSorter();

selSor.Sort(arrInt);

foreach (int i in arrInt)

{

Console.WriteLine(i);

}

Console.ReadKey();

}

}

}

希尔排序

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
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace sorter

{

public class ShellSorter

{

public void Sort(int[] arr)

{

int inc;

for (inc = 1; inc <= arr.Length / 9; inc = 3 * inc + 1) ;

for (; inc > 0; inc /= 3)

{

for (int i = inc + 1; i <= arr.Length; i += inc)

{

int t = arr[i - 1];

int j = i;

while ((j > inc) && (arr[j - inc - 1] > t))

{

arr[j - 1] = arr[j - inc - 1];

j -= inc;

}

arr[j - 1] = t;

}

}

}

}

class Program

{

static void Main(string[] args)

{

int[] arrInt = new int[] { 4, 2, 7, 1, 8, 3, 9, 0, 5, 6 };

ShellSorter selSor = new ShellSorter();

selSor.Sort(arrInt);

foreach (int i in arrInt)

{

Console.WriteLine(i);

}

Console.ReadKey();

}

}

}

归并排序

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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace Merge

{

public class Function

{

private int Groups;

private int CopyGroups;

private int mergerows;

private int[] Array27;

private static Random ran = new Random();

public Function(int length)

{

Array27 = new int[length];

for (int i = 0; i < length; i++)

Array27[i] = ran.Next(1, 100);

}

public void ToMergeSort()

{

MergeSort(Array27);

}

public void ToRecursiveMergeSort()

{

RecursiveMergeSort(Array27, 0, Array27.Length - 1);

}

public void ToNaturalMergeSort()

{

NaturalMergeSort(Array27);

}

public void RecursiveMergeSort(int[] Array, int left, int right)

{

int middle = (left + right) / 2;

if (left < right)

{

RecursiveMergeSort(Array, left, middle);

RecursiveMergeSort(Array, middle + 1, right);

MergeOne(Array, left, middle, right);

}

}

public void MergeOne(int[] Array, int left, int middle, int right)

{

int leftindex = left;

int rightindex = middle + 1;

int[] merge = new int[right + 1];

int index = 0;

while (leftindex <= middle && rightindex <= right)

merge[index++] = (Array[leftindex] - Array[rightindex]) >= 0 ? Array[rightindex++] : Array[leftindex++];

if (leftindex <= middle)

{

for (int i = leftindex; i <= middle; i++)

merge[index++] = Array[i];

}

if (rightindex <= right)

{

for (int i = rightindex; i <= right; i++)

merge[index++] = Array[i];

}

index = 0;

for (int i = left; i <= right; i++)

Array[i] = merge[index++];

}

public void MergeSort(int[] Array)

{

int[] merge = new int[Array.Length];

int P = 0;

while (true)

{

int index = 0;

int ENumb = (int)Math.Pow(2, P);

if (ENumb < Array.Length)

{

while (true)

{

int TorFAndrightindex = index;

if (TorFAndrightindex <= Array.Length - 1)

MergeTwo(Array, merge, index, ENumb);

else

break;

index += 2 * ENumb;

}

}

else

break;

P++;

}

}

public void MergeTwo(int[] Array, int[] merge, int index, int ENumb)

{

int left = index;

int middle = left + ENumb - 1;

if (middle >= Array.Length)

{

middle = index;

}

int mergeindex = index;

int right;

int middleTwo = (index + ENumb - 1) + 1;

right = index + ENumb + ENumb - 1;

if (right >= Array.Length - 1)

{

right = Array.Length - 1;

}

while (left <= middle && middleTwo <= right)

{

merge[mergeindex++] = Array[left] >= Array[middleTwo] ? Array[middleTwo++] : Array[left++];

}

if (left <= middle)

{

while (left <= middle && mergeindex < merge.Length)

merge[mergeindex++] = Array[left++];

}

if (middleTwo <= right)

{

while (middleTwo <= right)

merge[mergeindex++] = Array[middleTwo++];

}

if (right + 1 >= Array.Length)

Copy(Array, merge);

}

public void NaturalMergeSort(int[] Array)

{

int[,] PointsSymbol = LinearPoints(Array);

if (PointsSymbol[0, 1] == Array.Length - 1)

return;

else

NaturalMerge(Array, PointsSymbol);

}

public void NaturalMerge(int[] Array, int[,] PointsSymbol)

{

int left;

int right;

int leftend;

int rightend;

mergerows = GNumberTwo(Groups);

CopyGroups = Groups;

int[] TempArray = new int[Array.Length];

while (true)

{

int[,] TempPointsSymbol = new int[mergerows, 2];

int row = 0;

do

{

if (row != CopyGroups - 1)

{

left = PointsSymbol[row, 0];

leftend = PointsSymbol[row, 1];

right = PointsSymbol[row + 1, 0];

rightend = PointsSymbol[row + 1, 1];

MergeThree(Array, TempArray, left, leftend, right, rightend);

MergePointSymbol(PointsSymbol, TempPointsSymbol, row);

}

else

{

默认剩下的单独一个子数组已经虚拟合并。然后Copy进TempArray。

int TempRow = PointsSymbol[row, 0];

int TempCol = PointsSymbol[row, 1];

while (TempRow <= TempCol)

TempArray[TempRow] = Array[TempRow++];

TempPointsSymbol[row / 2, 0] = PointsSymbol[row, 0];

TempPointsSymbol[row / 2, 1] = PointsSymbol[row, 1];

break;

}

row += 2;

if (TempPointsSymbol[0, 1] == Array.Length - 1)

break;

}

while (row <= CopyGroups - 1);

Copy(Array, TempArray);

UpdatePointSymbol(PointsSymbol, TempPointsSymbol, row);

mergerows = GNumber(mergerows);

CopyGroups = GNumberTwo(CopyGroups);

if (PointsSymbol[0, 1] == Array.Length - 1)

break;

}

}

public int GNumber(int Value)

{

if (Value % 2 == 0)

Value /= 2;

else

Value -= 1;

return Value;

}

public int GNumberTwo(int Value)

{

if (Value % 2 == 0)

mergerows = Value / 2;

else

mergerows = Value / 2 + 1;

return mergerows;

}

public void MergeThree(int[] Array, int[] Temp, int left, int leftend, int right, int rightend)

{

int index = left;

while (left <= leftend && right <= rightend)

Temp[index++] = Array[left] >= Array[right] ? Array[right++] : Array[left++];

while (left <= leftend)

Temp[index++] = Array[left++];

while (right <= rightend)

Temp[index++] = Array[right++];

}

public void MergePointSymbol(int[,] PointsSymbol, int[,] TempPointsSymbol, int row)

{

int rowindex = row / 2;

TempPointsSymbol[rowindex, 0] = PointsSymbol[row, 0];

TempPointsSymbol[rowindex, 1] = PointsSymbol[row + 1, 1];

}

public void UpdatePointSymbol(int[,] PointsSymbol, int[,] TempPointsSymbol, int rows)

{

int row = 0;

for (; row < TempPointsSymbol.GetLength(0); row++)

{

for (int col = 0; col < 2; col++)

PointsSymbol[row, col] = TempPointsSymbol[row, col];

}

for (; row < PointsSymbol.GetLength(0); row++)

{

for (int col2 = 0; col2 < 2; col2++)

PointsSymbol[row, col2] = 0;

}

补剩下的index组,

// int row3 = TempPointsSymbol.GetLength(0); // PointsSymbol[row3, 0] = PointsSymbol[rows, 0]; // PointsSymbol[row3, 1] = PointsSymbol[rows, 1]; // //后面的清零 // for (int row4 = row3 + 1; row4 < PointsSymbol.GetLength(0); row4++) // { // for (int col4 = 0; col4 < 2; col4++) // PointsSymbol[row4, col4] = 0; // } //} } public int[,] LinearPoints(int[] Array) { Groups = 1; int StartPoint = 0; int row = 0; int col = 0; //最糟糕的情况就是有Array.Length行。 int[,] PointsSet = new int[Array.Length, 2]; //线性扫描Array,划分数组 //初始前index=0 PointsSet[row, col] = 0; do { //推断升序子数组终于的index开关 bool Judge = false; //从Array第二个数推断是否要结束或者是否是升序子数组. while (++StartPoint < Array.Length && Array[StartPoint] < Array[StartPoint - 1]) { //打开第一个升序子数组结束的index开关 Judge = true; //又一次開始第二个升序子数组的前index PointsSet[row, col + 1] = StartPoint - 1; //计算子数组个数 Groups++; //换行记录自然子数组的index row++; break; //–StartPoint; } //升序子数组结束index if (Judge) PointsSet[row, col] = StartPoint; //else // –StartPoint; } while (StartPoint < Array.Length); //终于index=StartPoint - 1,可是糟糕情况下还有剩余若干行为: 0,0 … PointsSet[row, col + 1] = StartPoint - 1; //调用展示方法 DisplaySubarray(Array, PointsSet, Groups); return PointsSet; } public void DisplaySubarray(int[] Array, int[,] PointsSet, int Groups) { Console.WriteLine(“Subarray is {0}:”, Groups); //展示子数组的前后index for (int r = 0; r < Groups; r++) { for (int c = 0; c < PointsSet.GetLength(1); c++) { Console.Write(PointsSet[r, c]); if (c < PointsSet.GetLength(1) - 1) Console.Write(“,”); } Console.Write(“\t\t”); } Console.WriteLine(); //展示分出的子数组 for (int v = 0; v < Groups; v++) { int i = 1; for (int r = PointsSet[v, 0]; r <= PointsSet[v, 1]; r++) { Console.Write(Array[r] + “ “); i++; } if (i <= 3) Console.Write(“\t\t”); else Console.Write(“\t”); if (PointsSet[v, 1] == Array.Length) break; } } public void Copy(int[] Array, int[] merge) { //一部分排好序的元素替换掉原来Array中的元素 for (int i = 0; i < Array.Length; i++) { Array[i] = merge[i]; } } //输出 public override string ToString() { string temporary = string.Empty; foreach (var element in Array27) temporary += element + “ “; temporary += “\n”; return temporary; } } class Program { static void Main(string[] args) { while (true) { Console.WriteLine(“请选择:”); Console.WriteLine(“1.归并排序(非递归)”); Console.WriteLine(“2.归并排序(递归)”); Console.WriteLine(“3.归并排序(自然合并)”); Console.WriteLine(“4.退出”); int Arraynum = Convert.ToInt32(Console.ReadLine()); switch (Arraynum) { case 4: Environment.Exit(0); break; case 1: Console.WriteLine(“Please Input Array Length”); int Leng271 = Convert.ToInt32(Console.ReadLine()); Function obj1 = new Function(Leng271); Console.WriteLine(“The original sequence:”); Console.WriteLine(obj1); Console.WriteLine(“‘MergeSort’ Finaly Sorting Result:”); obj1.ToMergeSort(); Console.WriteLine(obj1); break; case 2: Console.WriteLine(“Please Input Array Length”); int Leng272 = Convert.ToInt32(Console.ReadLine()); Function obj2 = new Function(Leng272); Console.WriteLine(“The original sequence:”); Console.WriteLine(obj2); Console.WriteLine(“‘RecursiveMergeSort’ Finaly Sorting Result:”); obj2.ToRecursiveMergeSort(); Console.WriteLine(obj2); break; case 3: Console.WriteLine(“Please Input Array Length”); int Leng273 = Convert.ToInt32(Console.ReadLine()); Function obj3 = new Function(Leng273); Console.WriteLine(“The original sequence:”); Console.WriteLine(obj3); obj3.ToNaturalMergeSort(); Console.WriteLine(); Console.WriteLine(); Console.WriteLine(“‘NaturalMergeSort’ Finaly Sorting Result:”); Console.WriteLine(obj3); break; } } } } }

基数排序

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
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace Merge

{

public class RadixSorter

{

public int[] RadixSort(int[] ArrayToSort, int digit)

{

for (int k = 1; k <= digit; k++)

{

int[] tmpArray = new int[ArrayToSort.Length];

int[] tmpCountingSortArray = new int[10] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

for (int i = 0; i < ArrayToSort.Length; i++)

{

int tmpSplitDigit = ArrayToSort[i] / (int)Math.Pow(10, k - 1) - (ArrayToSort[i] / (int)Math.Pow(10, k)) * 10;

tmpCountingSortArray[tmpSplitDigit] += 1;

}

for (int m = 1; m < 10; m++)

{

tmpCountingSortArray[m] += tmpCountingSortArray[m -

1];

}

for (int n = ArrayToSort.Length - 1; n >= 0; n--)

{

int tmpSplitDigit = ArrayToSort[n] / (int)Math.Pow(10, k - 1) -

(ArrayToSort[n] / (int)Math.Pow(10, k)) * 10;

tmpArray[tmpCountingSortArray[tmpSplitDigit] - 1] = ArrayToSort

[n];

tmpCountingSortArray[tmpSplitDigit] -= 1;

}

for (int p = 0; p < ArrayToSort.Length; p++)

{

ArrayToSort[p] = tmpArray[p];

}

}

return ArrayToSort;

}

}

class Program

{

static void Main(string[] args)

{

int[] intArray = new int[] { 5, 3, 7, 4, 8, 2, 9, 1, 0, 6 };

int[] newIntArray = intArray;

RadixSorter rS=new RadixSorter();

newIntArray = rS.RadixSort(intArray, intArray.Length);

foreach (int i in intArray)

{

Console.Write(i + " ");

}

Console.ReadKey();

}

}

}

计数排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace Merge

{

class Program

{

/// 要求: /// arrayToSort的元素必须大于等于0。或者经过一定的转换使其元素在 /// 大于等于0范围内。比如有例如以下序列(-1,-8,10,11),那么依据最小值8, /// 将各个数字加8转化为(7,0,18,19),然后进行计数排序。结果为(0,7,18,19), /// 然后再将结果个数字减8即为(-8,-1,10,11) /// /// 要排序的数组 /// 数组的最大值加一 /// 计数排序后的结果 public static int[] CountingSort(int[] arrayToSort, int k) { // 排序后的结果存储 int[] sortedArray = new int[arrayToSort.Length]; // 计数数组 int[] countingArray = new int[k]; // 计数数组初始化 for (int i = 0; i < countingArray.Length; i++) { countingArray[i] = 0; } // 单个元素计数(经过该步骤countingArray[i]的含义为数字i的个数为countingArray[i]) for (int i = 0; i < arrayToSort.Length; i++) { countingArray[arrayToSort[i]] = countingArray[arrayToSort[i]] + 1; } // 计算小于等于某数的个数(经过该步骤countingArray[i]的含义为小于等于数字i的元素个数为countingArray[i]) for (int i = 1; i < countingArray.Length; i++) { countingArray[i] += countingArray[i - 1]; } // 得到排序后的结果 for (int i = 0; i < sortedArray.Length; i++) { int numIndex = countingArray[arrayToSort[i]] - 1; sortedArray[numIndex] = arrayToSort[i]; countingArray[arrayToSort[i]] = countingArray[arrayToSort[i]] - 1; } return sortedArray; } static void Main(string[] args) { int[] intArray = new int[] { 5, 3, 7, 4, 8, 2, 9, 1, 0, 6 }; int[] intNewArray = intArray; intNewArray = CountingSort(intArray, intArray.Length); foreach (int i in intNewArray) { Console.Write(i + “ “); } Console.ReadKey(); } } }

堆排序

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
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace Merge

{

class Program

{

private static void HeapSortFunction(int[] array)

{

try

{

BuildMaxHeap(array);

for (int i = array.Length - 1; i > 0; i--)

{

Swap(ref array[0], ref array[i]);

MaxHeapify(array, 0, i);

}

}

catch (Exception ex)

{

Console.Write(ex.Message);

}

}

private static void BuildMaxHeap(int[] array)

{

try

{

for (int i = array.Length / 2 - 1; i >= 0; i--)

{

MaxHeapify(array, i, array.Length);

}

}

catch (Exception ex)

{

Console.Write(ex.Message);

}

}

private static void MaxHeapify(int[] array, int currentIndex, int heapSize)

{

try

{

int left = 2 * currentIndex + 1;

int right = 2 * currentIndex + 2;

int large = currentIndex;

if (left < heapSize && array[left] > array[large])

{

large = left;

}

if (right < heapSize && array[right] > array[large])

{

large = right;

}

if (currentIndex != large)

{

Swap(ref array[currentIndex], ref array[large]);

MaxHeapify(array, large, heapSize);

}

}

catch (Exception ex)

{

Console.Write(ex.Message);

}

}

private static void Swap(ref int a, ref int b)

{

int temp = 0;

temp = a;

a = b;

b = temp;

}

static void Main(string[] args)

{

int[] intArray = new int[] { 5, 3, 7, 4, 8, 2, 9, 1, 0, 6 };

HeapSortFunction(intArray);

foreach (int i in intArray)

{

Console.Write(i + " ");

}

Console.ReadKey();

}

}

}

排序的分类/稳定性/时间复杂度和空间复杂度总结

版权声明:本文博客原创文章。博客,未经同意,不得转载。

复制代码

public class MQHelper
{ private const string CacheKey_MQConnectionSetting = “MQConnectionSetting”; private const string CacheKey_MQMaxConnectionCount = “MQMaxConnectionCount”; // 空闲连接对象队列
private readonly static ConcurrentQueue FreeConnectionQueue; //使用中(忙)连接对象字典
private readonly static ConcurrentDictionary<IConnection, bool> BusyConnectionDic; //连接池使用率
private readonly static ConcurrentDictionary<IConnection, int> MQConnectionPoolUsingDicNew; private readonly static Semaphore MQConnectionPoolSemaphore; //释放和添加连接时的锁对象
private readonly static object freeConnLock = new object(), addConnLock = new object(), getConnLock = new object(); // 连接总数
private static int connCount = 0; //默认最大保持可用连接数
public const int DefaultMaxConnectionCount = 50; //默认最大连接数可访问次数
public const int DefaultMaxConnectionUsingCount = 10000; public const int DefaultRetryConnectionCount = 1;//默认重试连接次数

    /// <summary>
    /// 初始化最大连接数 /// </summary>
    private static int MaxConnectionCount
    { get { //if (HttpRuntime.Cache\[CacheKey\_MQMaxConnectionCount\] != null) //{ // return Convert.ToInt32(HttpRuntime.Cache\[CacheKey\_MQMaxConnectionCount\]); //} //else //{ // int mqMaxConnectionCount = 0; // string mqMaxConnectionCountStr = ConfigurationManager.AppSettings\[CacheKey\_MQMaxConnectionCount\]; // if (!int.TryParse(mqMaxConnectionCountStr, out mqMaxConnectionCount) || mqMaxConnectionCount <= 0) // { // mqMaxConnectionCount = DefaultMaxConnectionCount; // } // string appConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App.config"); // HttpRuntime.Cache.Insert(CacheKey\_MQMaxConnectionCount, mqMaxConnectionCount, new CacheDependency(appConfigPath));
                return 50; //}

}

    } /// <summary>
    /// 建立连接 /// </summary>
    /// <param name="hostName">服务器地址</param>
    /// <param name="userName">登录账号</param>
    /// <param name="passWord">登录密码</param>
    /// <returns></returns>
    private ConnectionFactory CrateFactory()
    { var mqConfigDom = MqConfigDomFactory.CreateConfigDomInstance(); //获取MQ的配置
        var connectionfactory = new ConnectionFactory();
        connectionfactory.HostName \= mqConfigDom.MqHost;
        connectionfactory.UserName \= mqConfigDom.MqUserName;
        connectionfactory.Password \= mqConfigDom.MqPassword;
        connectionfactory.Port \= mqConfigDom.MqPort;
        connectionfactory.VirtualHost \= mqConfigDom.MqVirtualHost; return connectionfactory;
    } /// <summary>
    /// 创建connection连接 /// </summary>
    /// <returns></returns>
    public IConnection CreateMQConnection()
    { var factory = CrateFactory();
        factory.AutomaticRecoveryEnabled \= true;//自动重连
        var connection = factory.CreateConnection();
        connection.AutoClose \= false; return connection;
    } /// <summary>
    /// 初始化 /// </summary>
    static MQHelper()
    {
        FreeConnectionQueue \= new ConcurrentQueue<IConnection>();
        BusyConnectionDic \= new ConcurrentDictionary<IConnection, bool\>();
        MQConnectionPoolUsingDicNew \= new ConcurrentDictionary<IConnection, int\>();//连接池使用率
        string semaphoreName = "MQConnectionPoolSemaphore"; try { if (null == MQConnectionPoolSemaphore)
            { bool semaphoreWasCreated;
                SemaphoreSecurity sems \= new SemaphoreSecurity();
                MQConnectionPoolSemaphore \= new Semaphore(MaxConnectionCount, MaxConnectionCount, "MQConnectionPoolSemaphore", out semaphoreWasCreated);//信号量,控制同时并发可用线程数

                if (semaphoreWasCreated)
                {
                    MQConnectionPoolSemaphore \= Semaphore.OpenExisting("MQConnectionPoolSemaphore", SemaphoreRights.FullControl);
                }

            }
        } catch (WaitHandleCannotBeOpenedException)
        { bool semaphoreWasCreated; string user = Environment.UserDomainName + "\\\\" + Environment.UserName;
            SemaphoreSecurity semSec \= new SemaphoreSecurity();

            SemaphoreAccessRule rule \= new SemaphoreAccessRule(user, SemaphoreRights.Synchronize | SemaphoreRights.Modify, AccessControlType.Deny);
            semSec.AddAccessRule(rule);

            rule \= new SemaphoreAccessRule(user, SemaphoreRights.ReadPermissions | SemaphoreRights.ChangePermissions, AccessControlType.Allow);
            semSec.AddAccessRule(rule); // Create a Semaphore object that represents the system // semaphore named by the constant 'semaphoreName', with // maximum count three, initial count three, and the // specified security access. The Boolean value that // indicates creation of the underlying system object is // placed in semaphoreWasCreated. //
            MQConnectionPoolSemaphore = new Semaphore(MaxConnectionCount, MaxConnectionCount, semaphoreName, out semaphoreWasCreated, semSec);
        } catch (UnauthorizedAccessException ex)
        {
            MQConnectionPoolSemaphore \= Semaphore.OpenExisting(semaphoreName, SemaphoreRights.ReadPermissions | SemaphoreRights.ChangePermissions); // Get the current ACL. This requires // SemaphoreRights.ReadPermissions.
            SemaphoreSecurity semSec = MQConnectionPoolSemaphore.GetAccessControl(); string user = Environment.UserDomainName + "\\\\" + Environment.UserName;
            SemaphoreAccessRule rule \= new SemaphoreAccessRule(user, SemaphoreRights.Synchronize | SemaphoreRights.Modify, AccessControlType.Deny);
            semSec.RemoveAccessRule(rule);//移除 // Now grant the user the correct rights.
            rule = new SemaphoreAccessRule(user, SemaphoreRights.Synchronize | SemaphoreRights.Modify, AccessControlType.Allow);
            semSec.AddAccessRule(rule); //重新授权

MQConnectionPoolSemaphore.SetAccessControl(semSec);
MQConnectionPoolSemaphore = Semaphore.OpenExisting(semaphoreName);
}
}

    //释放连接 public void CreateNewConnection2FreeQueue()
    {
        IConnection mqConnection \= null; if (FreeConnectionQueue.Count + BusyConnectionDic.Count < MaxConnectionCount)//如果已有连接数小于最大可用连接数,则直接创建新连接

{ lock (addConnLock)
{ if (FreeConnectionQueue.Count + BusyConnectionDic.Count < MaxConnectionCount)
{
mqConnection = CreateMQConnection();
FreeConnectionQueue.Enqueue(mqConnection);//加入到空闲队列连接集合中
MQConnectionPoolUsingDicNew[mqConnection] = 0;
}
}
}
} public string MqConnectionInfo()
{ int scount = 0; try {
scount=MQConnectionPoolSemaphore.Release();
MQConnectionPoolSemaphore.WaitOne(1);
scount -= 1;
} catch(SemaphoreFullException ex)
{
scount = MaxConnectionCount;
} return $”当前信号量计数={scount},当前空闲连接长度 ={ FreeConnectionQueue.Count},当前忙连接长度 ={ BusyConnectionDic.Count},连接使用频率信息如下:已达最大使用次数的有:{MQConnectionPoolUsingDicNew.Where(l=>l.Value>= DefaultMaxConnectionUsingCount).Count()},剩余{MQConnectionPoolUsingDicNew.Where(l => l.Value < DefaultMaxConnectionUsingCount).Count()}\r\n {JsonConvert.SerializeObject(MQConnectionPoolUsingDicNew)}”;
} ///


/// 在mq连接池中创建新连接 ///

///
public IConnection CreateMQConnectionInPoolNew(ref StringBuilder spanMsg)
{
Stopwatch sw = new Stopwatch(); long spanSum = 0;
sw.Start(); //
IConnection mqConnection = null; bool waitFree = false; int tryTimeCount = 0; try {
TryEnter:
waitFree = MQConnectionPoolSemaphore.WaitOne(10);//当<MaxConnectionCount时,会直接进入,否则会等待10ms继续监测直到空闲连接信号出现
if(!waitFree)
{
tryTimeCount++;
spanMsg.AppendLine($”阻塞10ms,空闲信号=[{waitFree}],进入第[{tryTimeCount}]次尝试,”); if (tryTimeCount <= 99)
{ goto TryEnter;
}
}
spanMsg.Append($”空闲信号=[{waitFree}],”); if (!FreeConnectionQueue.TryDequeue(out mqConnection)) //没有可用的
{
sw.Stop();
spanSum += sw.ElapsedMilliseconds;
spanMsg.Append($”尝试获取可用空闲连接,没有可用空闲连接,span:{sw.ElapsedMilliseconds}ms,”);
sw.Restart(); if (FreeConnectionQueue.Count + BusyConnectionDic.Count < MaxConnectionCount)//如果已有连接数小于最大可用连接数,则直接创建新连接
{ lock (addConnLock)
{ if (FreeConnectionQueue.Count + BusyConnectionDic.Count < MaxConnectionCount)
{
mqConnection = CreateMQConnection();
BusyConnectionDic[mqConnection] = true;//加入到忙连接集合中
MQConnectionPoolUsingDicNew[mqConnection] = 1;
sw.Stop();
spanSum += sw.ElapsedMilliseconds;
spanMsg.Append($”创建一个新连接,并加到使用中连接集合中,span:{sw.ElapsedMilliseconds}ms,”); return mqConnection;
}
}
}
sw.Stop();
spanSum += sw.ElapsedMilliseconds;
spanMsg.Append($”没有空闲连接,已到最大连接数{FreeConnectionQueue.Count + BusyConnectionDic.Count},等待连接释放,span:{sw.ElapsedMilliseconds}ms,”); if(waitFree)//重试需要释放之前占用的信号量
{ int scount=MQConnectionPoolSemaphore.Release();
waitFree = false;
spanMsg.Append($”释放空闲信号,当前信号计数={scount},”);
} return CreateMQConnectionInPoolNew();
} else if (MQConnectionPoolUsingDicNew[mqConnection] + 1 > DefaultMaxConnectionUsingCount || !mqConnection.IsOpen) //如果取到空闲连接,判断是否使用次数是否超过最大限制,超过则释放连接并重新创建
{ if (mqConnection.IsOpen)
{
mqConnection.Close();
}
mqConnection.Dispose();
mqConnection = CreateMQConnection();
MQConnectionPoolUsingDicNew[mqConnection] = 0;
sw.Stop();
spanSum += sw.ElapsedMilliseconds;
spanMsg.Append($”获取到的可用空闲连接可能因累计使用通道次数{MQConnectionPoolUsingDicNew[mqConnection] + 1 }>最大可用通道次数{DefaultMaxConnectionUsingCount},或连接状态处于不是开启状态={mqConnection.IsOpen},释放当前连接并重建一个连接,span:{sw.ElapsedMilliseconds}ms,”);
}
sw.Restart();
BusyConnectionDic[mqConnection] = true;//加入到忙连接集合中
MQConnectionPoolUsingDicNew[mqConnection] = MQConnectionPoolUsingDicNew[mqConnection] + 1;//使用次数加1
sw.Stop();
spanSum += sw.ElapsedMilliseconds;
spanMsg.AppendLine($”将获取到得空闲连接放入到忙集合中,并累加使用次数+1={MQConnectionPoolUsingDicNew[mqConnection] + 1},span:{sw.ElapsedMilliseconds}ms,”); return mqConnection;

        } catch(UnauthorizedAccessException ex)
        { throw ex;
        } catch (Exception ex)
        { if (null != mqConnection)
            {
                ResetMQConnectionToFree(mqConnection);
            } else  if(waitFree)//信号量没释放,则进行释放

{
MQConnectionPoolSemaphore.Release();
} return null;
} finally {
spanMsg.AppendLine( $”获取一个可用连接过程耗费{spanSum}ms,当前空闲连接长度={FreeConnectionQueue.Count},当前忙连接长度={BusyConnectionDic.Count}”); }
} ///


/// 在mq连接池中创建新连接 ///

///
public IConnection CreateMQConnectionInPoolNew()
{ //string spanMsg = string.Empty;
StringBuilder spanMsg = new StringBuilder(); return CreateMQConnectionInPoolNew(ref spanMsg);
} ///
/// 释放连接池中的连接 ///

///
private void ResetMQConnectionToFree(IConnection connection)
{ try { lock (freeConnLock)
{ bool result = false; if (BusyConnectionDic.TryRemove(connection, out result)) //从忙队列中取出
{
} else { //if(!BusyConnectionDic.TryRemove(connection,out result))
} if (FreeConnectionQueue.Count + BusyConnectionDic.Count > MaxConnectionCount)//如果因为高并发出现极少概率的>MaxConnectionCount,则直接释放该连接
{
connection.Close();
connection.Dispose();
} else if (connection.IsOpen) //如果OPEN状态才加入空闲链接队列
{
FreeConnectionQueue.Enqueue(connection);//加入到空闲队列,以便持续提供连接服务
}
}
} catch { throw;
} finally {
MQConnectionPoolSemaphore.Release();//释放一个空闲连接信号
}
} ///
/// 发送消息 ///

/// 消息队列连接对象
/// 消息类型
/// 队列名称
/// 是否持久化
/// 消息
///
public string SendMsg(IConnection connection, string queueName, string msg, bool durable = true, string exchange = “”, string type = “fanout”)
{ bool reTry = false; int reTryCount = 0; string sendErrMsg = string.Empty; do {
reTry = false; try { using (var channel = connection.CreateModel())//建立通讯信道
{ // 参数从前面开始分别意思为:队列名称,是否持久化,独占的队列,不使用时是否自动删除,其他参数
channel.QueueDeclare(queueName, durable, false, false, null); if (!exchange.IsNullOrEmpty())
{
channel.ExchangeDeclare(exchange: exchange, type: type, durable: durable);
}
channel.QueueBind(queueName, exchange, “”); //ExchangeDeclare(model, exchange, RabbitMqProxyConfig.ExchangeType.Fanout, isProperties); //QueueDeclare(model, queue, isProperties); //model.QueueBind(queue, exchange, routingKey);

                    var properties = channel.CreateBasicProperties();
                    properties.DeliveryMode \= 2;//1表示不持久,2.表示持久化
                                                ////properties.Type = ""; ////properties.CorrelationId

                    if (!durable)
                        properties \= null; var body = Encoding.UTF8.GetBytes(msg);
                    channel.BasicPublish(exchange, queueName, properties, body);
                }
                sendErrMsg \= string.Empty;
            } catch (Exception ex)
            { if ((++reTryCount) <= DefaultRetryConnectionCount)
                {
                    ResetMQConnectionToFree(connection);
                    connection \= CreateMQConnectionInPoolNew();
                    reTry \= true;
                } return ex.ToString();
            } finally { if (!reTry)
                {
                    ResetMQConnectionToFree(connection);
                }
            }
        } while (reTry); return sendErrMsg;
    } /// <summary>
    /// 消费消息 /// </summary>
    /// <param name="connection">消息队列连接对象</param>
    /// <param name="queueName">队列名称</param>
    /// <param name="durable">是否持久化</param>
    /// <param name="dealMessage">消息处理函数</param>
    /// <param name="saveLog">保存日志方法,可选</param>
    public void ConsumeMsg(IConnection connection, string queueName, bool durable, Func<string, ConsumeAction> dealMessage, Action<string, Exception> saveLog = null)
    { try { using (var channel = connection.CreateModel())
            {
                channel.QueueDeclare(queueName, durable, false, false, null); //获取队列 
                channel.BasicQos(0, 1, false); //分发机制为触发式

                var consumer = new QueueingBasicConsumer(channel); //建立消费者 // 从左到右参数意思分别是:队列名称、是否读取消息后直接删除消息,消费者
                channel.BasicConsume(queueName, false, consumer); while (true)  //如果队列中有消息

{
ConsumeAction consumeResult = ConsumeAction.RETRY; var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue(); //获取消息
string message = null; try { var body = ea.Body;
message = Encoding.UTF8.GetString(body);
consumeResult = dealMessage(message);
} catch (Exception ex)
{ if (saveLog != null)
{
saveLog(message, ex);
}
} if (consumeResult == ConsumeAction.ACCEPT)
{
channel.BasicAck(ea.DeliveryTag, false); //消息从队列中删除
} else if (consumeResult == ConsumeAction.RETRY)
{
channel.BasicNack(ea.DeliveryTag, false, true); //消息重回队列
} else {
channel.BasicNack(ea.DeliveryTag, false, false); //消息直接丢弃
}
}
}

        } catch (Exception ex)
        { if (saveLog != null)
            {
                saveLog("QueueName:" + queueName, ex);
            } throw ex;
        } finally {
            ResetMQConnectionToFree(connection);
        }
    } /// <summary>
    /// 依次获取单个消息 /// </summary>
    /// <param name="connection">消息队列连接对象</param>
    /// <param name="QueueName">队列名称</param>
    /// <param name="durable">持久化</param>
    /// <param name="dealMessage">处理消息委托</param>
    public void ConsumeMsgSingle(IConnection connection, string QueueName, bool durable, Func<string, ConsumeAction> dealMessage)
    { try { using (var channel = connection.CreateModel())
            {
                channel.QueueDeclare(QueueName, durable, false, false, null); //获取队列 
                channel.BasicQos(0, 1, false); //分发机制为触发式

                uint msgCount = channel.MessageCount(QueueName); if (msgCount > 0)
                { var consumer = new QueueingBasicConsumer(channel); //建立消费者 // 从左到右参数意思分别是:队列名称、是否读取消息后直接删除消息,消费者
                    channel.BasicConsume(QueueName, false, consumer);

                    ConsumeAction consumeResult \= ConsumeAction.RETRY; var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue(); //获取消息
                    try { var body = ea.Body; var message = Encoding.UTF8.GetString(body);
                        consumeResult \= dealMessage(message);
                    } catch (Exception ex)
                    { throw ex;
                    } finally { if (consumeResult == ConsumeAction.ACCEPT)
                        {
                            channel.BasicAck(ea.DeliveryTag, false);  //消息从队列中删除

} else if (consumeResult == ConsumeAction.RETRY)
{
channel.BasicNack(ea.DeliveryTag, false, true); //消息重回队列
} else {
channel.BasicNack(ea.DeliveryTag, false, false); //消息直接丢弃
}
}
} else {
dealMessage(string.Empty);
}
}

        } catch (Exception ex)
        { throw ex;
        } finally {
            ResetMQConnectionToFree(connection);
        }
    } /// <summary>
    /// 获取队列消息数 /// </summary>
    /// <param name="connection"></param>
    /// <param name="QueueName"></param>
    /// <returns></returns>
    public int GetMessageCount(IConnection connection, string QueueName)
    { int msgCount = 0; bool reTry = false; int reTryCount = 0; do {
            reTry \= false; try { using (var channel = connection.CreateModel())
                {
                    channel.QueueDeclare(QueueName, true, false, false, null); //获取队列
                    msgCount = (int)channel.MessageCount(QueueName);
                }
            } catch (Exception ex)
            { //if (BaseUtil.IsIncludeException<SocketException>(ex))

{ if ((++reTryCount) <= DefaultRetryConnectionCount)//可重试1次
{
ResetMQConnectionToFree(connection);
connection = CreateMQConnectionInPoolNew();
reTry = true;
}
} throw ex;
} finally { if (!reTry)
{
ResetMQConnectionToFree(connection);
}
}

        } while (reTry); return msgCount;
    }
} public enum ConsumeAction
{
    ACCEPT, // 消费成功
    RETRY,   // 消费失败,可以放回队列重新消费
    REJECT,  // 消费失败,直接丢弃
}

复制代码

在站点开启多个工作进程的情况下,信号量的控制显得很重要,它可以有效的控制并发。信号量是针对单机而言的。通过计数可以有效控制使用中的连接个数。从而有效控制连接池的总体数量,防止过度的创建带来的毁灭性打击。

Socket的TCP通讯

一、 socket****的通讯原理

服务器端的步骤如下。

(1)建立服务器端的Socket,开始侦听整个网络中的连接请求。

(2)当检测到来自客户端的连接请求时,向客户端发送收到连接请求的信息,并建立与客户端之间的连接。

(3)当完成通信后,服务器关闭与客户端的Socket连接。

客户端的步骤如下。

(1)建立客户端的Socket,确定要连接的服务器的主机名和端口。

(2)发送连接请求到服务器,并等待服务器的回馈信息。

(3)连接成功后,与服务器进行数据的交互。

(4)数据处理完毕后,关闭自身的Socket连接。

二、 socket****的通讯方式

socket通讯方式有两种:同步和异步

同步工作方式:

用TCP协议进行编程时程序执行到发送、接收和监听语句的时候,在未完成工作前不再继续往下执行,即处于阻塞状态,直到该语句完成某个工作后才继续执行下一条语句。

异步工作方式

程序执行到发送、接收和监听语句的时候,不论工作是否完成,都会继续往下执行。

三、 socket****的C#实现

  1. 1. 同步:

服务端客户端通信

在与服务端的连接建立以后,我们就可以通过此连接来发送和接收数据。端口与端口之间以流(Stream)的形式传输数据,因为几乎任何对象都可以保存到流中,所以实际上可以在客户端与服务端之间传输任何类型的数据。对客户端来说,往流中写入数据,即为向服务器传送数据;从流中读取数据,即为从服务端接收数据。对服务端来说,往流中写入数据,即为向客户端发送数据;从流中读取数据,即为从客户端接收数据。

服务端:

(1)服务端对端口进行侦听:

服务器端建立一个socket,设置好本机的ip和监听的端口与socket进行绑定,开始监听连接请求,当接收到连接请求后,发送确认,同客户端建立连接,开始与客户端进行通信。

TcpListener listener =new TcpListener(new IPEndPoint(IPAddress.Parse(ip), port));//ip为服务器IP地址,port为监听的端口

Listener.Start();//开启监听

(2)检测来自客户端的连接请求

TcpClient remoteClient = listener.AcceptTcpClient();

//接收客户端  这里体现了同步的含义,如果客户端对该服务端发起连接的时候,程序在这里就会等待(阻塞),直到有客户端的连接请求为止

(3)建立和连接的客户端的数据流(传输数据)

NetworkStream streamToClient = remoteClient.GetStream();

该数据流只要是用来接收和发送数据,同步也分多客户端和单个客户端,如果分的详细一点的话,还有客户端的一条以及多条数据的情况,如果是单个客户端的多条数据的话,连接客户端之后,在建立数据流的前面添加一个循环就可以了,如果是多个客户端的话,在(2)前面加个循环就可以了。为了接收数据的效率,建议不管是同步还是异步,服务端都做成线程,详细见Demo

(4)接收客户端发送过来的数据(用缓存来接收)

byte``[] buffer = new byte``[BufferSize];

int bytesRead;

try

{

lock (streamToClient)

{

bytesRead = streamToClient.Read(buffer, 0, BufferSize);

}

(5)向连接的客户端发送数据

lock (streamToClient)

{

streamToClient.Write(buffer, 0, buffer.Length);

}

      (6)释放数据流和TcpClient(以便下次的数据以及客户端的收发)

streamToClient.Dispose();

remoteClient.Close();

客户端

(1)   连接服务器

TcpClient tcp = new TcpClient();

tcp.Connect(IP,Port);

if (tcp.Connected)

{

ShowGetData(``"成功连接上了服务器:"``, this``.strIP.Text.ToString());

}

这里需要注意的是,不管是使用有参数的构造函数与服务器连接,或者是通过Connect()方法与服务器建立连接,都是同步方法(或者说是阻塞的,英文叫block)。它的意思是说,客户端在与服务端连接成功、从而方法返回,或者是服务端不存、从而抛出异常之前,是无法继续进行后继操作的。这里还有一个名为BeginConnect()的方法,用于实施异步的连接,这样程序不会被阻塞,可以立即执行后面的操作,这是因为可能由于网络拥塞等问题,连接需要较长时间才能完成。网络编程中有非常多的异步操作,凡事都是由简入难,关于异步操作,我们后面再讨论,现在只看同步操作。

(2)   建立连接服务端的数据流

NetworkStream streamToServer = tcp.GetStream();

(3)   接收和发送数据

byte``[] buffer = Encoding.Unicode.GetBytes(msg);

try

{

lock (streamToServer)

{

streamToServer.Write(buffer, 0, buffer.Length);

}

buffer = new byte``[BufferSize];

lock (streamToServer)

{

bytesRead = streamToServer.Read(buffer, 0, BufferSize);

}

}

  1. 2. 异步

        相对于同步,异步中的连接,接收和发送数据的方法都不一样,都有一个回调函数,就是即使不能连接或者接收不到数据,程序还是会一直执行下去,如果连接上了或者接到数据,程序会回到这个回调函数的地方重新往下执行。详细见下面:

服务器

1、 开启侦听接口

private TcpListener listener;

listener = new TcpListener(``new IPEndPoint(IPAddress.Parse(ip), port));

listener.Start();

或者

listener.Start(``int i);

2、 接收客户端

listener.BeginAcceptSocket(clientConnect, listener);

/// <summary>

/// 接收回调函数

/// </summary>

/// <param name="ar"></param>

private void clientConnect(IAsyncResult ar)

{

try

{

TcpListener listener = (TcpListener)ar.AsyncState;

Socket client = listener.EndAcceptSocket(ar);

}

catch { }

}

3、 接收客户端发送的数据

/// <summary>

/// 异步接收数据

/// </summary>

private void receiveData(Socket client)

{

IAsyncResult iar = client.BeginReceive(buffer, 0, BagSize, SocketFlags.None, out errorCode, receiveCallback, buffer);

}

}

/// <summary>

/// 接收数据回调函数

/// </summary>

/// <param name="ar"></param>

private void receiveCallback(IAsyncResult ar)

{

int receLen = 0;

try

{

receLen = client.EndReceive(ar, out errorCode);

if (receLen > 0)

{

OnReceiveData(client);

}

}

catch {     }

}

else { }

}

4、接收成功之后,回发数据给客户端

/// <summary>

/// 异步发送报文

/// </summary>

/// <param name="data"></param>

private void OnReceiveData (Socket socket)

{

string strLogin = “succeed recived”;

byte``[] data = Encoding.ASCII.GetBytes(strLogin);

socket.BeginSend(data, 0, data.Length, SocketFlags.None, out errorCode, sendCallBack, socket);

}

else

{ }

}

/// <summary>

/// 异步发送回调事件

/// </summary>

/// <param name="ar"></param>

private void sendCallBack(IAsyncResult ar)

{

socket.EndSend(ar, out errorCode);

}

客户端

1、连接服务器

private TcpClient tcpcz = null

tcpcz = new TcpClient()

tcpcz.BeginConnect(ipaddress, Convert.ToInt32(port), new AsyncCallback(ConnectCallback), tcpcz);

/// <summary>

/// 异步连接的回调函数

/// </summary>

/// <param name="ar"></param>

private void ConnectCallback(IAsyncResult ar)

{

TcpClient t = (TcpClient)ar.AsyncState;

try

{

if (t.Connected)

{

t.EndConnect(ar);

}

else

{

}

}

catch () {    }

}

2、发送和接收字符串

NetworkStream stream = tcp.GetStream();

string strLogin = “``this is socket example”;

byte``[] data = Encoding.ASCII.GetBytes(strLogin);

stream.BeginWrite(data, 0, data.Length, new AsyncCallback(SendCallback),stream);

byte``[] result = new byte``[tcp.Available];

try

{

stream.BeginRead(result, 0, result.Length, new AsyncCallback(ReadCallback), stream);

}

catch { }

string strResponse = Encoding.ASCII.GetString(result).Trim();

}

}

catch ()

{

}

}

以上是这一段时间对socket的一些心得,还在不断学习中,如果上面的讲解有什么不到位的或者错误的,可以交流一下。

image

接上篇,我留到这里才介绍怎样测试,因为不会做的话也不会测做得对不对。说是单元测试的话,其实应该设计好 Model 后,定好大概 VM 内要干什么之后,马上可以动手写测试代码。

很多公司没有规定如何测试,更加没有单元测试,也没考虑 TDD,或许是有他们的原因的,不一定是因为水平问题的,也不一定是同事能力所限。还真的有很多人,很多老开发,不愿意写测试。这测试只是一种开发方法,是其中一个而已。没有所谓绝对的最佳办法。

我这里说的,是一些情况,你必须要这么测,才有比较好的效率,才能确保代码正确无误。

想一想,如果你负责写模块,同时,另一位负责写 Shell,(比如用 PRISM 的 Modularity),你在没有 Shell 的情况下,点击 Debug Run 帮不了你。你的 View 全是 UserControl,你的入口,是个 IModule 的东西而且它只负责注册视图。你真要运行来看效果来「人肉」测试的话,你只能自己写一个 Shell 出来(?!),在 IModule 注册菜单后,再由 Shell 打开视图。

一个只为测试而做的 Shell ,可能很快能写出来。同样地,一个测试项目也一样很快能写完。它们之间的分别,是测试项目能自动重复运行。

如果 TDD 就更加不用说了。

废话讲完。入正题。

测试对象:ICommand 的 CanExecute

假设,用例之一,是「在没有选择明细行前,删除行按钮禁用」。在上几篇实现了的一个功能。

以下省略了测试描述,只含代码:

image

有没有选中明细行,这是视图中 DataGrid 对它 SelectedItem 属性(绑定 VM 的 CurrentRow)赋值的一个操作,可以像上面代码一样模拟它。注意,如果你 VM 构造函数,有检查参数是否为 null 然后是 null 就抛异常的话,上面代码就当然不行,要写个模拟,如下部分的做法。

上面代码,除了 new 什么出来测以外,真正的代码只有两行,一个是为 CurrentRow 赋值,另一个是运行 CanExecute 委托让它返回 true/false。额,当然还有 assertion 啦。

这测试,其实只是检查 { return CurrentRow != null; } 这一句委托而已,作用不大,但想象一下,如果这是 VM 包含了数据验证用了 IDataErrorInfo,测试目标是保存按钮能不能点击,那委托,就不是那么简单了。

测试对象:ICommand 的 Execute

假设,用例中,要求明细行内,单行中的物料栏,要能打开另一个选择窗口来选择物料

image

这里做了几件事,这几件事是从用例而来,是操作的顺序。target.ItemCodeSelectionCommand 没有必要 Execute,运行了也只是有个空白窗口,标题是 Mock Empty Window 的而已,写这样纯粹为示范模拟类用到依赖注入上。

这测试能通过,但或许会有错误信息,不过你看看就知道是 BackgroundWorker 的事,我不作解释了,它跟这里无关。

其他

额,其他我不说了。测属性没什么好说,特别是在这里没数据验证的情况下。

总结

我觉得测试中,MVVM 跟其他的,不一样的地方就是这个 ICommand。上面写了它两方面的测试运作。我就单单写这部分算了。

测试中,没有调用采购订单的视图,连弹出窗口也是假的,这测试,要测的是 ViewModel 本身。MVVM 在单独测试有优势,就是 ViewModel 它自己能做到完全与 View 隔开,单独运行。好处是:

  1. 外壳没写就已经开始写和测试 ViewModel
  2. 弹出窗口没写也可以写和测试 ViewModel
  3. 减少了依赖,发生错误时,查找的范围只限于 ViewModel 本身
  4. 每次改动后,重复测试,能确保 ViewModel 按照原定的行为跑

至于 Model 呢,Model 本身只是个放值的容器,除 get /set 外没有任何东西,如果 EF 等等的 ORM 它更是自动生成,我认为没必要分离出来,它错的机会低,而且分出来也有相当大的工作量。

再说一篇这只是其中一个方法,再说这做法与外面、书本上说的不一样,有点歪曲了的做法。对我管用。实际用不用,请自行考虑。

采购订单示例,到此结束。往后的是,MVVM 应用时的各种情况,下一篇将会是树结构在 MVVM 的绑定

树结构很有趣,很多算法都跟它有关,它也随处可见,比如菜单,比如产品结构 BOM 、组织架构等等。虽然有趣,但用起来痛苦,过往你试过写代码历遍 node 找一个元素,然后一堆 boxing / unboxing,你懂的。用 MVVM 会是一个解脱,一部分解脱。下回继续

我在这群里,欢迎加入交流:
开发板玩家群 578649319开发板玩家群 578649319
硬件创客 (10105555)硬件创客 (10105555)

本文说明怎样把 DoubleClick 连接至 ICommand。方法很多。推荐使用 Attach Property 方式,因为它能把任何 RoutedEvent 接上任何 ICommand。

之前写过一篇博文关于 MVVM 中双击事件触发 ICommand 的办法,我说要么你自己写 Attached Property,要么下载别人写好的,比如支持 Collections 的 CommandBehaviors。我认为这两个办法是比较好的。有网友说我没有解释清楚,因为我觉得 Attached Property 有点离题,跟 MVVM 关系不太大。反正有得用就行了。

下面以 ListView 为例。

1. InputBindings

先不说 Attached Property,看看有什么办法可以把双击绑定到 ICommand。最简单的办法是 InputBindings。

XAML:

<ListView.InputBindings><MouseBinding Gesture=“LeftDoubleClick” Command=“”/></ListView.InputBindings>

支持 KeyBinding (键盘),和 MouseBinding (鼠标)。能做到,如果只需要管键盘或鼠标,这是比较简单。

2. 隐形 Button (不建议)

我见过第二个办法,隐形 Button, (Visibility=”Collapsed”),ICommand 绑定进去,ListView MouseDoubleClick 在视图建立句柄,由它再触发 Button 的 Command.Execute(object)。

XAML:

<Button Name=“button1” Visibility=“Collapsed” Command=“”/><ListView MouseDoubleClick=“ListView_MouseDoubleClick”/>

Code:

privatevoid ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
button1.Command.Execute(null);
}

这比较傻,不建议。

3. Attached Property

MSDN 有介绍怎样为控件添加新的属性,这里不详细说了。关键是静态方法 Set,和静态 DependencyProperty。(MSDN 说 GET SET 都要,但其实写 XAML 时只用到 SET,后续启动后,你需要拿回属性值才需要 GET)。

先看一下,Attached Property 是怎样写的,热热身:

CODE:

publicstaticclass MyProperty { publicstaticreadonly DependencyProperty ParameterProperty = DependencyProperty.RegisterAttached( “Parameter”, typeof(Object), typeof(MyProperty), new FrameworkPropertyMetadata(null)
); publicstatic Object GetParameter(UIElement obj) { return obj.GetValue(ParameterProperty);
} publicstaticvoid SetParameter(UIElement obj, Object value) {
obj.SetValue(ParameterProperty, value);
}
}

get、set 参数 UIElement 类型是为了确保所有控件能用它。这 Parameter 没有配置CallBack,这个MyProperty不对值变化做什么动作,也不设置默认值,所以 RegisterAttached 时候 FrameworkPropertyMetadata是 null。

命名规范必须跟从,MSDN 有说明。当你希望在 XAML 这属性叫做 Parameter 的时候(RegisterAttached 的第一个参数),它的get、set 方法必须命名为 GetParameter 和 SetParameter。编译后 XAML 可用。

XAML:

<Window x:Class=“WpfApplication1.MainWindow” xmlns=“http://schemas.microsoft.com/winfx/2006/xaml/presentation“ xmlns:x=“http://schemas.microsoft.com/winfx/2006/xaml“ xmlns:y=“clr-namespace:WpfApplication1” Title=“MainWindow” Height=“350” Width=“525”><Grid><ListView y:MyProperty.Parameter=“ABC”/></Grid></Window>

新手记得加上正确的 XML namespace,xmlns:y=”clr-namespace:WpfApplication1” 是因为我把MyProperty类放在这 WpfApplication1 项目的最外层。

知道了怎么写 Attached Property 之后,入正题,加入 ICommand。为灵活性,做法是让程序员配置要绑的 RoutedEvent ,和对应要触发的 ICommand 同时作为 DependencyProperty,让程序员自己配置哪个Event 接哪个 ICommand。(注:handler 那 Dictionary 的做法,和 Detach Attach 是参考某大神的)。为缩短代码,只写 ICommand 和 Event,没写 ICommand 的命令参数。

(以下代码网上其实很多,也有很多版本,大同小异)

CODE:

using System.Collections.Generic; using System.Windows; using System.Windows.Input; namespace WpfApplication1 { publicstaticclass CommandBehavior { // UI,Handler Listprivatestatic Dictionary<UIElement, RoutedEventHandler> handlers =new Dictionary<UIElement, RoutedEventHandler>(); #region Command Propertypublicstaticreadonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached( “Command”, typeof(ICommand), typeof(CommandBehavior), new FrameworkPropertyMetadata() {
DefaultValue =null,
PropertyChangedCallback =new PropertyChangedCallback(OnCommandPropertyChanged)
}
); publicstatic ICommand GetCommand(UIElement obj) { return (ICommand)obj.GetValue(CommandProperty);
} publicstaticvoid SetCommand(UIElement obj, ICommand value) {
obj.SetValue(CommandProperty, value);
} #endregion#region Event Propertypublicstaticreadonly DependencyProperty EventProperty = DependencyProperty.RegisterAttached( “Event”, typeof(RoutedEvent), typeof(CommandBehavior), new FrameworkPropertyMetadata() {
DefaultValue =null,
PropertyChangedCallback =new PropertyChangedCallback(OnEventPropertyChanged)
}
); publicstatic RoutedEvent GetEvent(DependencyObject obj) { return (RoutedEvent)obj.GetValue(EventProperty);
} publicstaticvoid SetEvent(DependencyObject obj, RoutedEvent value) {
obj.SetValue(EventProperty, value);
} #endregion#region CallBacksprivatestaticvoid OnCommandPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) {
UIElement element = obj as UIElement;
ICommand oldCommand = args.OldValue as ICommand;
ICommand newCommand = args.NewValue as ICommand;
RoutedEvent routedEvent = element.GetValue(EventProperty) as RoutedEvent;

        Detach(element, routedEvent, oldCommand);
        Attach(element, routedEvent, newCommand);
    } privatestaticvoid OnEventPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) {
        UIElement element \= obj as UIElement;
        RoutedEvent oldEvent \= args.OldValue as RoutedEvent;
        RoutedEvent newEvent \= args.NewValue as RoutedEvent;
        ICommand command \= element.GetValue(CommandProperty) as ICommand;

        Detach(element, oldEvent, command);
        Attach(element, newEvent, command);
    } #endregionprivatestaticvoid Attach(UIElement element, RoutedEvent Event, ICommand command) { if (Event !=null&& element !=null&& command !=null) {
            RoutedEventHandler InvokeCommandHandler \=new RoutedEventHandler(delegate {
                command.Execute(null);
            });
            handlers.Add(element, InvokeCommandHandler);
            element.AddHandler(Event, InvokeCommandHandler);
        }
    } privatestaticvoid Detach(UIElement element, RoutedEvent Event, ICommand command) { if (Event !=null&& element !=null&& command !=null) {
            RoutedEventHandler handler \= handlers\[element\]; if (handler !=null) {
                element.RemoveHandler(Event, handler);
                handlers.Remove(element);
            }
        }
    }
}

}

跟之前那个 Parameter 例子很像,只是同一个静态类,做了两个属性,一个叫做 Event,一个叫做 Command。另外,多了一个 Dictionary,还有,这次 Event 和 Command 的变化,都注册了 PropertyChangedCallback 的句柄。最下面的 Attach Detach 的 private 帮助方法,只是重构时从PropertyChangedCallBack 的句柄抽出来而已。

控件、事件、命令,三者是一起的组合,某 UIElement 的某 RoutedEvent 触发到某 ICommand 的 Execute。但RoutedEvent 触发的是 RoutedEventHandler 句柄,不是 ICommand。所以这个静态类所做最重要的事,见 private static void Attach(),就是创建新的 RoutedEventHandler,让它执行委托运行 command 的 Execute,然后把准备好 RoutedEventHandler 之后粘上 UIElement,即 AddHandler(RoutedEvent,RoutedEventHandler)。把这搭配,UIElement 和已做好ICommand委托的 RoutedEventHandler,放在 Dictionary,是为了 Detach 时候找回。

要做 Detach 是因为,DependencyProperty 的值是能变化的(上例中是 Event和Command这两个,都能在运行时变),不一定是写死在 XAML,比如 {Binding Path=XXX} 这情况。万一 Command 变了,或者 RoutedEvent 变了,上述做好了的搭配就失效,是需要 RemoveHandler 然后重新组合。所以,PropertyChangedCallBack 所做的,都是先 Detach 旧值(args.OldValue),然后再 Attach 粘上新值(args.NewValue)。不管 Event 变还是 Command 变,都需要如此。

这静态类的解释到此为止。不复杂。用法如下:

XAML:

<Window x:Class=“WpfApplication1.MainWindow” xmlns=“http://schemas.microsoft.com/winfx/2006/xaml/presentation“ xmlns:x=“http://schemas.microsoft.com/winfx/2006/xaml“ xmlns:y=“clr-namespace:WpfApplication1” Title=“MainWindow” Height=“350” Width=“525”><Grid><ListView y:CommandBehavior.Command=“{Binding Path=TestCommand}” y:CommandBehavior.Event=“ListView.MouseDoubleClick”></ListView></Grid></Window>

因为一开始设置了Command 和 Event 的默认值为 null (RegisterAttached 时候的 FrameworkPropertyMetadata 内,DefaultValue),所以 XAML 运行写入值时,值变化触发 CallBack,完成了我们需要的连接。

最后,改一下 CommandBehavior,让它能接受参数,传过去 ICommand。因为 ICommand 的命令参数类型是 object,所以写的 CommandParameter 类型也是 object。

完整版本 CODE:

using System.Collections.Generic; using System.Windows; using System.Windows.Input; namespace WpfApplication1 { publicstaticclass CommandBehavior { // UI,Handler Listprivatestatic Dictionary<UIElement, RoutedEventHandler> handlers =new Dictionary<UIElement, RoutedEventHandler>(); #region Command Propertypublicstaticreadonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached( “Command”, typeof(ICommand), typeof(CommandBehavior), new FrameworkPropertyMetadata() {
DefaultValue =null,
PropertyChangedCallback =new PropertyChangedCallback(OnCommandPropertyChanged)
}
); publicstatic ICommand GetCommand(UIElement obj) { return (ICommand)obj.GetValue(CommandProperty);
} publicstaticvoid SetCommand(UIElement obj, ICommand value) {
obj.SetValue(CommandProperty, value);
} #endregion#region Event Propertypublicstaticreadonly DependencyProperty EventProperty = DependencyProperty.RegisterAttached( “Event”, typeof(RoutedEvent), typeof(CommandBehavior), new FrameworkPropertyMetadata() {
DefaultValue =null,
PropertyChangedCallback =new PropertyChangedCallback(OnEventPropertyChanged)
}
); publicstatic RoutedEvent GetEvent(DependencyObject obj) { return (RoutedEvent)obj.GetValue(EventProperty);
} publicstaticvoid SetEvent(DependencyObject obj, RoutedEvent value) {
obj.SetValue(EventProperty, value);
} #endregion#region CommandParameter Propertypublicstaticreadonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached( “CommandParameter”, typeof(object), typeof(CommandBehavior), new FrameworkPropertyMetadata(null)
); publicstaticobject GetCommandParameter(UIElement obj) { return obj.GetValue(CommandParameterProperty);
} publicstaticvoid SetCommandParameter(UIElement obj, object value) {
obj.SetValue(CommandParameterProperty, value);
} #endregion#region CallBacksprivatestaticvoid OnCommandPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) {
UIElement element = obj as UIElement;
ICommand oldCommand = args.OldValue as ICommand;
ICommand newCommand = args.NewValue as ICommand;
RoutedEvent routedEvent = element.GetValue(EventProperty) as RoutedEvent; object commandParameter = element.GetValue(CommandParameterProperty);

        Detach(element, routedEvent, oldCommand);
        Attach(element, routedEvent, newCommand, commandParameter);
    } privatestaticvoid OnEventPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) {
        UIElement element \= obj as UIElement;
        RoutedEvent oldEvent \= args.OldValue as RoutedEvent;
        RoutedEvent newEvent \= args.NewValue as RoutedEvent;
        ICommand command \= element.GetValue(CommandProperty) as ICommand; object commandParameter \= element.GetValue(CommandParameterProperty);

        Detach(element, oldEvent, command);
        Attach(element, newEvent, command, commandParameter);
    } #endregionprivatestaticvoid Attach(UIElement element, RoutedEvent Event, ICommand command, object commandParameter) { if (Event !=null&& element !=null&& command !=null) {
            RoutedEventHandler InvokeCommandHandler \=new RoutedEventHandler(delegate {
                command.Execute(commandParameter);
            });
            handlers.Add(element, InvokeCommandHandler);
            element.AddHandler(Event, InvokeCommandHandler);
        }
    } privatestaticvoid Detach(UIElement element, RoutedEvent Event, ICommand command) { if (Event !=null&& element !=null&& command !=null) {
            RoutedEventHandler handler \= handlers\[element\]; if (handler !=null) {
                element.RemoveHandler(Event, handler);
                handlers.Remove(element);
            }
        }
    }
}

}

完整版本的 CommandBehavior 在 XAML 用法:

XAML:

<Window x:Class=“WpfApplication1.MainWindow” xmlns=“http://schemas.microsoft.com/winfx/2006/xaml/presentation“ xmlns:x=“http://schemas.microsoft.com/winfx/2006/xaml“ xmlns:y=“clr-namespace:WpfApplication1” Title=“MainWindow” Height=“350” Width=“525”><Grid><ListView y:CommandBehavior.Command=“{Binding Path=TestCommand}” y:CommandBehavior.Event=“ListView.MouseDoubleClick” y:CommandBehavior.CommandParameter=“TestParameter”/></Grid></Window>

Attach Property 方法介绍到此为止。点击这里下载最终版本的代码

这类简单,用来解释工作原理比较合适。但我之前博文没用这个类,因为以上代码,有一个明显缺陷。源于 Dictionary<UIElement, RoutedEventHandler> 这样的简单搭配,UIElement 作为 Key。而且 CommandBehavior 这静态类,没有集合暴露给 XAML。这意味着,一个控件,只能设置一次。比如,当一个控件你有两个 RoutedEvent 希望绑定到两个ICommand,这代码不支持。

为了解决这问题,网上已经有很多人写好了一个叫做 CommandBehaviorCollection 的类(懒到搜索都不想搜的,点击这里),很多不同的版本,功能其实都一样,让你在 XAML 内一个控件能同时配置多个 Event 和 Command 的组合。这个类就是我在之前博文上用到的那个。我不打算解释里面内容,其工作基本原理,与上述代码一摸一样,只是它暴露了集合让你在 XAML 内填多个组合。

我在这群里,欢迎加入交流:
开发板玩家群 578649319开发板玩家群 578649319
硬件创客 (10105555)硬件创客 (10105555)

WPF 没有用到 PictureBox, 而是用Image代替.

下面我试着加载显示一个图片 。

XAML

1
<Image x:Name="srcImg"Width="400"Height="300"></Image>

CS Attempt 1:

1
2
Image<Bgr,Byte>My_Image=newImage<Bgr,byte>(Openfile.FileName);
srcImg.Source=My_Image.ToBitmap();

Error Message

1
2
Cannot implicitly convert type 'System.Drawing.Bitmap' 
to 'System.Windows.Media.ImageSource'

CS Attempt 2:

1
2
Image<Bgr,Byte>My_Image=newImage<Bgr,byte>(Openfile.FileName);
srcImg.Source=newBitmapImage(My_Image);

Error Message

1
2
Error1The best overloaded method match for'System.Windows.Media.Imaging.BitmapImage.BitmapImage(System.Uri)' has some invalid arguments  
Error2Argument1: cannot convert from'Emgu.CV.Image<Emgu.CV.Structure.Bgr,byte>' to 'System.Uri'

Image<Bgr, Byte> My_Image = new Image<Bgr, byte>(Openfile.FileName);
srcImg.Source = BitmapSourceConvert.ToBitmapSource(myImage);

复制代码

public static class BitmapSourceConvert
{
[DllImport(“gdi32”)] private static extern int DeleteObject(IntPtr o); public static BitmapSource ToBitmapSource(IImage image)
{ using (System.Drawing.Bitmap source = image.Bitmap)
{
IntPtr ptr = source.GetHbitmap();

        BitmapSource bs \= System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
            ptr,
            IntPtr.Zero,
            Int32Rect.Empty,
            System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());

        DeleteObject(ptr); return bs;
    }
}

}

复制代码

复制代码

Bitmap bm = (Bitmap)Bitmap.FromFile(@”D:\my_testfiles\Main.png”); int count = 10000000; for (int i = 0; i < count; i++)
{ Commons.BitMapToImageSource(bm.Clone() as Bitmap);

        }

复制代码

结果:

当循环 1527次,  耗时 00:00:17.8860231后  报错。

ErrorTrace:

at System.Drawing.Bitmap.GetHbitmap(Color background)
at System.Drawing.Bitmap.GetHbitmap()
at SimpleClient.Commons.BitMapToImageSource(Bitmap bitmap) in D:\my_svn\universal-project\SimpleClient\Program.cs:line 165

ErrorMessage:

system.OutOfMemoryException: Out of memory.

改进:

写一个视频显示控件,内部实现显示Bitmap转换成BitmapSource。为控件Source开辟一块内存控件,然后一直刷新。首先我们使用EmuguCV里面的Capture。
控件继承Image、IDisposable接口并开放一个属性

VideoSource。

_代码如下:
_

复制代码

public class CapPlayer : Image, IDisposable
{ private Capture videoSource; public Capture VideoSource
    { set { if (value != null)
            { this.videoSource = value; this.LaodCapPlayer();
            }
        }
    } private InteropBitmap bitmapSource; private IntPtr map; private IntPtr section; public CapPlayer()
    {
        Application.Current.Exit += new ExitEventHandler(Current\_Exit);
    } private void LaodCapPlayer()
    {
        videoSource.ImageGrabbed += new Capture.GrabEventHandler(videoSource\_ImageGrabbed);
        videoSource.Start(); uint pcount = (uint)(videoSource.Width \* videoSource.Height \* PixelFormats.Bgr32.BitsPerPixel / 8);
        section \= CreateFileMapping(new IntPtr(-1), IntPtr.Zero, 0x04, 0, pcount, null);
        map \= MapViewOfFile(section, 0xF001F, 0, 0, pcount);
        bitmapSource \= Imaging.CreateBitmapSourceFromMemorySection(section, (int)videoSource.Width, (int)videoSource.Height, PixelFormats.Bgr32,
            (int)(videoSource.Width \* PixelFormats.Bgr32.BitsPerPixel / 8), 0) as InteropBitmap; this.Source = bitmapSource;
    } void videoSource\_ImageGrabbed(object sender, EventArgs e)
    {
        Capture camera \= sender as Capture; var frame = camera.RetrieveBgrFrame().Bitmap; if (this.Dispatcher != null)
        { this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, (SendOrPostCallback)delegate { if (map != IntPtr.Zero)
                { try {
                        System.Drawing.Imaging.BitmapData bmpData \= frame.LockBits(new System.Drawing.Rectangle(0, 0, (int)videoSource.Width, (int)videoSource.Height),
                        System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppRgb); /\* Get the pointer to the pixels \*/ IntPtr pBmp \= bmpData.Scan0; int srcStride = bmpData.Stride; int pSize = srcStride \* (int)videoSource.Height;
                        CopyMemory(map, pBmp, pSize);
                        frame.UnlockBits(bmpData);
                    } catch (Exception ex)
                    {
                        Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(ex, "Info", 0);
                    }

                } if (bitmapSource != null)
                {
                    bitmapSource.Invalidate();
                }
            }, null);

        } if (captureLock)
        { try { lock (mutCurrentImg)
                {
                    camera.RetrieveBgrFrame().Save(imageFileName);
                }
            } catch (System.Exception ex)
            {
                Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(ex, "Info", 0);
            }
            captureLock \= false;
        }
    } void Current\_Exit(object sender, ExitEventArgs e)
    { this.Dispose();
    } object mutCurrentImg = new object(); bool captureLock = false; string imageFileName = string.Empty; public void ImaggingCapture(string imageFileName)
    { this.imageFileName = imageFileName; this.captureLock = true;
    } #region IDisposable Members

    public void Dispose()
    { if (videoSource != null)
            videoSource.Stop();
        Application.Current.Exit \-= new ExitEventHandler(Current\_Exit);
    } #endregion \[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory")\] private static extern void CopyMemory(IntPtr Destination, IntPtr Source, int Length);

    \[DllImport("kernel32.dll", SetLastError = true)\] static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpFileMappingAttributes, uint flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, string lpName);

    \[DllImport("kernel32.dll", SetLastError = true)\] static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap);

}

复制代码

测试结果:无异常,但是发现比较占用CPU 10%左右,内存 122M 左右,线程数32.

感觉不太理想CPU占用太多,我们把EmuguCV的Capture改为AForge.NEt的VideoCaptureDevice,代码如下:

复制代码

public class CapPlayer : Image, IDisposable
{ private VideoCaptureDevice videoSource; private InteropBitmap bitmapSource; private IntPtr map; private IntPtr section; public CapPlayer()
    {

        Application.Current.Exit += new ExitEventHandler(Current\_Exit); this.Loaded += new RoutedEventHandler(CapPlayer\_Loaded);
        videoSource \= new VideoCaptureDevice(@"device:pnp:\\\\?\\usb#vid\_0ac8&pid\_305b#5&ee85354&0&2#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\\global");  
  
    } void CapPlayer\_Loaded(object sender, RoutedEventArgs e)
    {
        videoSource.NewFrame += new NewFrameEventHandler(videoSource\_NewFrame);
        videoSource.Start(); uint pcount = (uint)(this.Width \* this.Height \* PixelFormats.Bgr32.BitsPerPixel / 8);
        section \= CreateFileMapping(new IntPtr(-1), IntPtr.Zero, 0x04, 0, pcount, null);
        map \= MapViewOfFile(section, 0xF001F, 0, 0, pcount);
        bitmapSource \= System.Windows.Interop.Imaging.CreateBitmapSourceFromMemorySection(section, (int)this.Width, (int)this.Height, PixelFormats.Bgr32,
            (int)(this.Width \* PixelFormats.Bgr32.BitsPerPixel / 8), 0) as InteropBitmap; this.Source = bitmapSource;
    } void Current\_Exit(object sender, ExitEventArgs e)
    { this.Dispose();
    } object mutCurrentImg = new object(); bool captureLock = false; string imageFileName = string.Empty; public void ImaggingCapture(string imageFileName)
    { this.imageFileName = imageFileName; this.captureLock = true;
    } private void videoSource\_NewFrame(object sender, NewFrameEventArgs eventArgs)
    { //eventArgs.Frame.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);

        if (this.Dispatcher != null)
        { this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, (SendOrPostCallback)delegate { if (map != IntPtr.Zero)
                {
                    System.Drawing.Imaging.BitmapData bmpData \= eventArgs.Frame.LockBits(new System.Drawing.Rectangle(0, 0, (int)this.Width, (int)this.Height),
                    System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppRgb); /\* Get the pointer to the pixels \*/ IntPtr pBmp \= bmpData.Scan0; int srcStride = bmpData.Stride; int pSize = srcStride \* (int)this.Height;
                    CopyMemory(map, pBmp, pSize);
                    eventArgs.Frame.UnlockBits(bmpData);
                } if (bitmapSource != null)
                {
                    bitmapSource.Invalidate();
                }
            }, null);

        } if (captureLock)
        { try { lock (mutCurrentImg)
                {
                    eventArgs.Frame.Save(imageFileName);
                }
            } catch (System.Exception ex)
            {
            }
            captureLock \= false;
        }
    } #region IDisposable Members

    public void Dispose()
    { if (videoSource != null)
            videoSource.SignalToStop();
    } #endregion \[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory")\] private static extern void CopyMemory(IntPtr Destination, IntPtr Source, int Length);

    \[DllImport("kernel32.dll", SetLastError = true)\] static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpFileMappingAttributes, uint flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, string lpName);

    \[DllImport("kernel32.dll", SetLastError = true)\] static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap);

}

复制代码

测试结果:无异常,但是发现占用CPU 1%左右,内存 72M 左右,线程数28. 是不是比较理想呢!!

CPU

内存

线程数

EmuguCV

10%左右

122M左右

32

Aforge.Net

1%左右

72M左右

28

我的电脑配置.

摄像头:FrameRate 30,Width 320 Height 240.

电脑型号 戴尔 Precision WorkStation T3400 Tower
操作系统 Windows 7 旗舰版 32位 SP1 ( DirectX 11 )
处理器 英特尔 酷睿2 双核 E8400 @ 3.00GHz
主板 戴尔 0HY553 (英特尔 X38/X48 Express 芯片组 - ICH9R)
内存 4 GB ( 海力士 DDR2 800MHz / 金士顿 DDR2 667MHz )
主硬盘 西数 WDC WD3200AAKS-75L9A0 ( 320 GB / 7200 转/分 )
显卡 Nvidia Quadro NVS 290 ( 256 MB / Nvidia )
显示器 戴尔 DELA04A DELL E170S ( 17.1 英寸 )
光驱 飞利浦-建兴 DVD-ROM DH-16D5S DVD光驱
声卡 Analog Devices AD1984 @ 英特尔 82801I(ICH9) 高保真音频
网卡 博通 BCM5754 NetXtreme Gigabit Ethernet / 戴尔

系统文件gdi32.dll是存放在Windows系统文件夹中的重要文件,通常情况下是在安装操作系统过程中自动创建的,对于系统正常运行来说至关重要。除非用户电脑被木马病毒、或是流氓软件篡改导致出现gdi32.dll丢失、缺失损坏等弹窗现象,否则不建议用户对该类文件(gdi32.dll)进行随意的修改。

gdi32.dll是Windows GDI图形用户界面相关程序,包含的函数用来绘制图像和显示文字。

复制代码

try { //取得图片大小
System.Drawing.Size size = new System.Drawing.Size(Convert.ToInt32(w), Convert.ToInt32(h)); //新建一个bmp图片
System.Drawing.Image bmp = new System.Drawing.Bitmap(size.Width, size.Height); //新建一个画板
g = System.Drawing.Graphics.FromImage(bmp); //设置高质量插值法
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear; //设置高质量,低速度呈现平滑程度
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; //清空一下画布
g.Clear(System.Drawing.Color.Transparent); //在指定位置画图
g.DrawImage(img, new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), new System.Drawing.Rectangle(cwidth / 2, cheigh_h / 2, img.Width - cwidth, img.Height - cheigh_h), System.Drawing.GraphicsUnit.Pixel);//cwidth要裁剪尺寸 //保存高清晰度的缩略图
bmp.Save(newMapPath, System.Drawing.Imaging.ImageFormat.Jpeg);
} finally {
g.Dispose();
}

复制代码

复制代码

相关参数:
SmoothingMode { 对直线、曲线和已填充区域的边缘采用锯齿消除功能, 它不能控制路径渐变画笔 }
Invalid // 一个无效模式
Default // 不消除锯齿, 等效于 HighSpeed、None
HighSpeed // 不消除锯齿
HighQuality // 消除锯齿, 等效于 AntiAlias
None // 不消除锯齿
AntiAlias // 消除锯齿
InterpolationMode { 插补模式确定如何计算两个像素点之间的中间值 }
Invalid // 等效于 QualityMode 枚举的 Invalid 元素.
Default // 默认模式.
Low // 低质量插值法.
High // 高质量插值法.
Bilinear // 双线性插值法; 不进行预筛选; 将图像收缩为原始大小的 50% 以下时此模式不适用.
Bicubic // 双三次插值法; 不进行预筛选; 将图像收缩为原始大小的 25% 以下时此模式不适用.
NearestNeighbor // 最临近插值法.
HighQualityBilinear // 高质量的双线性插值法; 执行预筛选以确保高质量的收缩.
HighQualityBicubic // 高质量的双三次插值法; 执行预筛选以确保高质量的收缩; 可产生质量最高的转换图像.
CompositingMode { 颜色合成模式 }
SourceOver // 与背景色混合; 该混合由透明度确定
SourceCopy // 改写背景色
CompositingQuality { 图像合成时, 源像素与目标像素和合成方式 }
Invalid // 无效质量
Default // 默认质量
HighSpeed // 高速度、低质量
HighQuality // 高质量、低速度复合
GammaCorrected // 使用灰度校正
AssumeLinear // 假定线性值
PixelOffsetMode { 像素偏移模式 }
Invalid // 无效模式.
Default // 默认模式.
HighSpeed // 高速度、低质量呈现.
HighQuality // 高质量、低速度呈现.
None // 没有任何像素偏移.
Half // 像素在水平和垂直距离上均偏移 -0.5 个单位, 以进行高速锯齿消除.

复制代码

思路如下(参照源代码):

  1、 frmServer启动两个网络侦听,主连接侦听,协助打洞的侦听。

  2、 frmClientA和frmClientB分别与frmServer的主连接保持联系。

  3、 当frmClientA需要和frmClientB建立直接的udp连接时,首先连接frmServer的协助打洞端口,并发送协助连接申请,同时在该端口号上启动侦听。

     4、  frmServer的协助打洞连接收到frmClientA的申请后通过主连接通知frmClientB,并将frmClientA经过NAT-A转换后的公网IP地址和端口等信息告诉frmClientB。

  5、 frmClientB收到frmServer的连接通知后首先与frmServer的协助打洞端口连接,发送一些数据后立即断开,目的是让frmServer能知道frmClientB经过NAT-B转换后的公网IP和端口号。

  6、 frmClientB尝试与frmClientA的经过NAT-A转换后的公网IP地址和端口进行connect,不同的路由器会有不同的结果,多数路由器对未知不请自到的SYN请求包直接丢弃而导致connect失败,但NAT-A会纪录此次连接的源地址和端口号,为接下来真正的连接做好了准备,这就是所谓的打洞,即frmClientB向frmClientA打了一个洞,下次frmClientA就能直接连接到frmClientB刚才使用的端口号了。

  7、 客户端frmClientB打洞的同时在相同的端口上启动侦听。frmClientB在一切准备就绪以后通过与frmServer的主连接回复消息“可以了,已经准备”,frmServer在收到以后将frmClientB经过NAT-B转换后的公网IP和端口号告诉给frmClientA。

  8、 frmClientA收到frmServer回复的frmClientB的公网IP和端口号等信息以后,开始连接到frmClientB公网IP和端口号,由于在步骤6中frmClientB曾经尝试连接过frmClientA的公网IP地址和端口,NAT-A纪录了此次连接的信息,所以当frmClientA主动连接frmClientB时,NAT-B会认为是合法的SYN数据,并允许通过,从而直接的udp连接建立起来了。

  • frmClientB

客户端核心代码:

1 private void Run() 2 {
3 try
4 {
5 byte[] buffer;//接受数据用
6 while (true)
7 {
8 buffer = _client.Receive(ref _remotePoint);//_remotePoint变量返回当前连接的用户IP地址
9
10 object msgObj = ObjectSerializer.Deserialize(buffer); 11 Type msgType = msgObj.GetType(); 12 DoWriteLog(“接收到消息:” + msgType.ToString() + “ From:” + _remotePoint.ToString()); 13
14 if (msgType == typeof(S2C_UserListMessage)) 15 { 16 // 更新用户列表
17 S2C_UserListMessage usersMsg = (S2C_UserListMessage)msgObj; 18 _userList.Clear(); 19
20 foreach (User user in usersMsg.UserList) 21 _userList.Add(user); 22
23 this.DisplayUsers(_userList); 24 } 25 else if (msgType == typeof(S2C_UserAction)) 26 { 27 //用户动作,新用户登录/用户登出
28 S2C_UserAction msgAction = (S2C_UserAction)msgObj; 29 if (msgAction.Action == UserAction.Login) 30 { 31 _userList.Add(msgAction.User); 32 this.DisplayUsers(_userList); 33 } 34 else if (msgAction.Action == UserAction.Logout) 35 { 36 User user = _userList.Find(msgAction.User.UserName); 37 if (user != null) _userList.Remove(user); 38 this.DisplayUsers(_userList); 39 } 40 } 41 else if (msgType == typeof(S2C_HolePunchingMessage)) 42 { 43 //接受到服务器的打洞命令
44 S2C_HolePunchingMessage msgHolePunching = (S2C_HolePunchingMessage)msgObj; 45
46 //NAT-B的用户给NAT-A的用户发送消息,此时UDP包肯定会被NAT-A丢弃, 47 //因为NAT-A上并没有A->NAT-B的合法Session, 但是现在NAT-B上就建立了有B->NAT-A的合法session了!
48 P2P_HolePunchingTestMessage msgTest = new P2P_HolePunchingTestMessage(_LocalUserName); 49 this.SendMessage(msgTest, msgHolePunching.RemotePoint); 50 } 51 else if (msgType == typeof(P2P_HolePunchingTestMessage)) 52 { 53 //UDP打洞测试消息 54 //_HoleAccepted = true;
55 P2P_HolePunchingTestMessage msgTest = (P2P_HolePunchingTestMessage)msgObj; 56 UpdateConnection(msgTest.UserName, _remotePoint); 57
58 //发送确认消息
59 P2P_HolePunchingResponse response = new P2P_HolePunchingResponse(_LocalUserName); 60 this.SendMessage(response, _remotePoint); 61 } 62 else if (msgType == typeof(P2P_HolePunchingResponse)) 63 { 64 //_HoleAccepted = true;//打洞成功
65 P2P_HolePunchingResponse msg = msgObj as P2P_HolePunchingResponse; 66 UpdateConnection(msg.UserName, _remotePoint); 67
68 } 69 else if (msgType == typeof(P2P_TalkMessage)) 70 { 71 //用户间对话消息
72 P2P_TalkMessage workMsg = (P2P_TalkMessage)msgObj; 73 DoWriteLog(workMsg.Message); 74 } 75 else
76 { 77 DoWriteLog(“收到未知消息!”); 78 } 79 } 80 } 81 catch (Exception ex) { DoWriteLog(ex.Message); } 82 }

View Code

  • frmClientA

服务端核心代码:

1 private void Run() 2 {
3 byte[] msgBuffer = null;
4
5 while (true)
6 {
7 msgBuffer = _server.Receive(ref _remotePoint); //接受消息
8 try
9 { 10 //将消息转换为对象
11 object msgObject = ObjectSerializer.Deserialize(msgBuffer); 12 if (msgObject == null) continue; 13
14 Type msgType = msgObject.GetType(); 15 DoWriteLog(“接收到消息:” + msgType.ToString()); 16 DoWriteLog(“From:” + _remotePoint.ToString()); 17
18 //新用户登录
19 if (msgType == typeof(C2S_LoginMessage)) 20 { 21 C2S_LoginMessage lginMsg = (C2S_LoginMessage)msgObject; 22 DoWriteLog(string.Format(“用户’{0}’已登录!”, lginMsg.FromUserName)); 23
24 // 添加用户到列表
25 IPEndPoint userEndPoint = new IPEndPoint(_remotePoint.Address, _remotePoint.Port); 26 User user = new User(lginMsg.FromUserName, userEndPoint); 27 _userList.Add(user); 28
29 this.DoUserChanged(_userList); 30
31 //通知所有人,有新用户登录
32 S2C_UserAction msgNewUser = new S2C_UserAction(user, UserAction.Login); 33 foreach (User u in _userList) 34 { 35 if (u.UserName == user.UserName) //如果是自己,发送所有在线用户列表
36 this.SendMessage(new S2C_UserListMessage(_userList), u.NetPoint); 37 else
38 this.SendMessage(msgNewUser, u.NetPoint); 39 } 40 } 41 else if (msgType == typeof(C2S_LogoutMessage)) 42 { 43 C2S_LogoutMessage lgoutMsg = (C2S_LogoutMessage)msgObject; 44 DoWriteLog(string.Format(“用户’{0}’已登出!”, lgoutMsg.FromUserName)); 45
46 // 从列表中删除用户
47 User logoutUser = _userList.Find(lgoutMsg.FromUserName); 48 if (logoutUser != null) _userList.Remove(logoutUser); 49
50 this.DoUserChanged(_userList); 51
52 //通知所有人,有用户登出
53 S2C_UserAction msgNewUser = new S2C_UserAction(logoutUser, UserAction.Logout); 54 foreach (User u in _userList) 55 this.SendMessage(msgNewUser, u.NetPoint); 56 } 57 else if (msgType == typeof(C2S_HolePunchingRequestMessage)) 58 { 59 //接收到A给B打洞的消息,打洞请求,由客户端发送给服务器端
60 C2S_HolePunchingRequestMessage msgHoleReq = (C2S_HolePunchingRequestMessage)msgObject; 61
62 User userA = _userList.Find(msgHoleReq.FromUserName); 63 User userB = _userList.Find(msgHoleReq.ToUserName); 64
65 // 发送打洞(Punching Hole)消息
66 DoWriteLog(string.Format(“用户:[{0} IP:{1}]想与[{2} IP:{3}]建立对话通道.”, 67 userA.UserName, userA.NetPoint.ToString(), 68 userB.UserName, userB.NetPoint.ToString())); 69
70 //由Server发送消息给B,将A的IP的IP地址信息告诉B,然后由B发送一个测试消息给A.
71 S2C_HolePunchingMessage msgHolePunching = new S2C_HolePunchingMessage(_remotePoint); 72 this.SendMessage(msgHolePunching, userB.NetPoint); //Server->B
73 } 74 else if (msgType == typeof(C2S_GetUsersMessage)) 75 { 76 // 发送当前用户信息
77 S2C_UserListMessage srvResMsg = new S2C_UserListMessage(_userList); 78 this.SendMessage(srvResMsg, _remotePoint); 79 } 80 } 81 catch (Exception ex) { DoWriteLog(ex.Message); } 82 } 83 }

View Code

  • frmServer

 

如转载请注明本文来自易学网http://www.vjsdn.com/

    近期做一个小的功能需求,用到了队列,用的时候出了很多问题,现在总结一下,希望能对有需要的人提供帮助。

    我的需求很简单,就是多个客户端连接到我的一个小型的数据转发服务器上,开始使用的是Socket通信实现这个功能,一旦数据服务器接收到来自不同客户端发来的消息,就对这些消息进行处理(我这里是将数据接收到后再转发到另一个服务器上),但考虑到客户端是每隔一个很短的时间周期向服务器发送信息,并且连接客服端数量比较多的时候,担心会产生并发访问的问题,也希望避免 数据转发服务器 频繁地从多个不同线程获取信息而出现其他未知问题,所以在处理客户端向数据转发服务器发送信息的时候采取队列的方式。

    一般情况下,使用MSMQ,要先安装消息服务,这个直接百度就行;

    在VS里添加 Messaging引用,就可以使用MessageQueue这个类了;接下来就要思考清楚你的数据(消息)的流向问题,之前因为自己对队列的错误认识,对到底在哪创建队列,队列的消息又由谁去发送和接收没有弄清除,还有参考的一些写得不是太清晰地博文,绕了好大一圈,所以今天在这里以我自己的项目需求为例子,说明 1、如何创建队列 2、如何向队列发送消息 3、 如何获取队列中的消息

首先、创建队列:根据我的需求,我要通过Socket通信将信息发送至数据转发服务器,因此为了避免并发访问问题的产生,消息队列应当建立在数据转发服务器上;

复制代码

       MessageQueue myqueue = null; string queuepath = @”.\private$\queuedemo”; if (!MessageQueue.Exists(queuepath))
{
myqueue = MessageQueue.Create(queuepath);
}
myqueue = new MessageQueue(queuepath);

复制代码

    这样就在数据转发服务器端创建了一个名为queuedemo的消息队列;从客户端要发送的消息就保存在这个队列里,你可以通过计算机管理->服务和应用下的消息队列中看到你创建的queuedemo队列,private$关键字是说明队列为专用队列,如果没有这个关键字还要配置域服务器,还是挺麻烦,这个还是借助百度吧,前面的“.”代表创建的队列目录是本机,这个队列一旦创建成功,就是系统的事了,接下来要做的就是你怎么去把消息写进这个队列,或者读取队列的值 这里要特别注意不要将queuepath路径字符串写成

string queuepath = @”FormatName:Direct=TCP:192.168.1.153\private$\queuedemo”;

这样写的话是用于远程计算机对这个队列进行访问的,因为MessageQueue的Create()和Exisit()方法是没办法去识别上述FormatName格式的,还有要确保Create()函数要被执行了之后再用MessageQueue实例去引用;这样服务器端队列的创建就完成了;

     在客户端中,向队列发送信息;

复制代码

string s = “客户端往队列里发送的信息”);
System.Messaging.Message msg = new System.Messaging.Message();
msg.Body = s;
msg.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
MessageQueue mq = new MessageQueue(@”FormatName:Direct=TCP:192.168.1.153\private$\queuedemo”);
mq.Send(msg);

复制代码

在客户端中,用一个MessageQueue实例指向服务器本机上创建的队列路径,这时,MessageQueue实例的构造函数里的路径就一定要用FormatName格式,指明是TCP通信还是HTTP还是Machine如我上面代码所示,然后调用Send()方法,将消息写进队列,这个要求发送的对象要以序列化的方式写进去,所以要设置formatter,这里用的是XmlMessageFormatter 还有BinaryMessageFormatter等等 注意保存你消息的 消息体Body是Object类型的 因此可以将你写的任何一个类的对象发送至消息队列

    在服务器中接收消息队列

复制代码

MessageQueue mq = new MessageQueue(@”.\private$\queuedemo”);

        mq.Formatter \= new XmlMessageFormatter(new Type\[\] { typeof(string) });
        Thread th \= new Thread(() => { while (true)
                {
                    System.Messaging.Message msg \= mq.Receive(); if (msg != null)
                    {
                        MessageBox.Show(msg.Body.ToString());
                    }

                }
            });
        th.IsBackground \= true;
        th.Start();

复制代码

在本机上可以新创建一个队列实例指向本机的队列,然后按照之前约定的序列化格式反序列化消息体所以将新的队列实例的foarmatter属性赋值为发送时的formatter属性如代码所示,这个时候就直接用Receive()得到消息体,然后对消息体里的信息做处理,我这里是开启一个线程显示队列的消息,只要有新的消息写入,我就在消息框中输出

    这个时候可能客户端无法向远程服务器成功发送消息,原因基本权限问题 服务器的消息队列的权限没有对未验证的客户端开放  你要在服务器队列里分配对应权限 如果你想读取队列的内容 还需要加系统变量

 问题解决办法

1. 服务器端

  • 服务器上消息队列权限设置:给ANONYMOUS LOGON赋予所有权限;

  • 修改服务器的注册表,允许非验证客户端访问

这样客户端就可以读取服务器里的队列信息了 当然一般业务逻辑上不这么做 因为他只负责发送消息 ,综上,就是使用消息队列 跨服务器读写的 最基本的用法

本文是利用SharpPcap实现网络包的捕获的小例子,实现了端口监控,数据包捕获等功能,主要用于学习分享。

什么是SharpPcap?

SharpPcap 是一个.NET 环境下的网络包捕获框架,基于著名的 pcap/WinPcap 库开发。提供了捕获、注入、分析和构建的功能,适用于 C# 和 VB NET 开发语言。

SharpPcap有两部分组成:1> SharpPcap.dll 负责数据的捕获  2> PacketDotNet.dll负责数据包的解析

思路:

  1. 通过进程名字获取对应的端口号。
  2. SharpPcap获取对应的数据包,通过解析数据包过滤相关的端口。

涉及知识点:

  • Process 获取相关进程信息。
  • netstat命令:netstat -ano|find “3844” 获取进程对应的端口
  • SharpPcap相关信息:
    • 通过CaptureDeviceList的静态方法获取设备列表。
    • 通过OnPacketArrival事件接收数据包。
    • 通过PacketDotNet来解析数据包

效果图下:

SharpPcap核心代码:

复制代码

1 ///


2 /// 开始捕捉 3 ///

4 ///
5 ///
6 private void btnStart_Click(object sender, EventArgs e) 7 {
8 if (this.combDevice.SelectedIndex > -1)
9 {
10 StartCapture(this.combDevice.SelectedIndex);
11 this.btnStart.Enabled = false;
12 this.btnStop.Enabled = true;
13 }
14 else { 15 MessageBox.Show(this,”请选择一个设备”,”提示”,MessageBoxButtons.OK);
16 }
17 }
18
19 ///
20 /// 停止捕捉 21 ///

22 ///
23 ///
24 private void btnStop_Click(object sender, EventArgs e) 25 {
26 Shutdown();
27 this.btnStop.Enabled = false;
28 this.btnStart.Enabled = true;
29 }
30
31 private void StartCapture(int itemIndex) 32 {
33 packetCount = 0;
34 device = CaptureDeviceList.Instance[itemIndex]; 35 packetStrings = new Queue();
36 bs = new BindingSource(); 37 dgvData.DataSource = bs; 38 LastStatisticsOutput = DateTime.Now; 39
40 // start the background thread
41 backgroundThreadStop = false;
42 backgroundThread = new Thread(BackgroundThread); 43 backgroundThread.Start();
44
45
46 // setup background capture
47 device.OnPacketArrival += new PacketArrivalEventHandler(device_OnPacketArrival); 48 device.OnCaptureStopped += new CaptureStoppedEventHandler(device_OnCaptureStopped); 49 device.Open();
50
51 // tcpdump filter to capture only TCP/IP packets
52 string filter = “ip and tcp”;
53 device.Filter = filter; 54
55 // force an initial statistics update
56 captureStatistics = device.Statistics; 57 UpdateCaptureStatistics();
58
59 // start the background capture
60 device.StartCapture();
61
62 btnStop.Enabled = true;
63 }
64
65 ///
66 /// 设备接收事件 67 ///

68 ///
69 ///
70 private void device_OnPacketArrival(object sender, CaptureEventArgs e) 71 {
72 // print out periodic statistics about this device
73 var Now = DateTime.Now; 74 var interval = Now - LastStatisticsOutput; 75 if (interval > new TimeSpan(0, 0, 2))
76 {
77 Console.WriteLine(“device_OnPacketArrival: “ + e.Device.Statistics); 78 captureStatistics = e.Device.Statistics; 79 statisticsUiNeedsUpdate = true;
80 LastStatisticsOutput = Now; 81 }
82
83 lock (QueueLock) 84 {
85 PacketQueue.Add(e.Packet);
86 }
87 }
88
89 ///
90 /// 设备停止事件 91 ///

92 ///
93 ///
94 private void device_OnCaptureStopped(object sender, CaptureStoppedEventStatus status) 95 {
96 if (status != CaptureStoppedEventStatus.CompletedWithoutError) 97 {
98 MessageBox.Show(“Error stopping capture”, “Error”, MessageBoxButtons.OK, MessageBoxIcon.Error);
99 } 100 } 101
102 private void UpdateCaptureStatistics() 103 { 104 tlblStatistic.Text = string.Format(“接收包: {0}, 丢弃包: {1}, 接口丢弃包: {2}”, captureStatistics.ReceivedPackets,captureStatistics.DroppedPackets, captureStatistics.InterfaceDroppedPackets); 105 }

复制代码

关于SharpPcap手册

源码下载

posted on 2017-10-19 23:42  Alan.hsiang  阅读(1871)  评论()  编辑  收藏