关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

云南大王-学习C#.NET、使用sqlserver数据库的总结

发布时间:2020-04-13 00:00:00
介绍:之前没有学习过C#这个语言,我是刚刚从大学毕业的学生,我的专业是嵌入式系统工程(底层驱动ARM、Linux、单片机),本人很喜欢计算机,更喜欢软件开发(应用层)的这种工作,只是当时考大学时没有过多的了解选错了专业,毕业后开始找工作一直在找我喜欢的应用层行业,找了30多家公司终于有一个愿意培养我的了(主要是我不会应用层的技术,没有公司要我,所以找的公司很多,但我没放弃),在公司的三个月里我一边工作一边学习,现在把我所学到的都记录下来,下面有的代码我是复制其他人的,也别介意我是研究了好久了学会了,方便记录我就黏贴在我这里。大家可以学习,参考,指点错误,我也当个笔记方便以后复习。 有个提示:我是有过C语言底子的,学习C#时那些简单基础的语法很快就能懂得使用,我相信学过类似的语言(Java、C++)都能一看就会那些简单基础,假如不会那请先学习编程的简单语法知识(我博客里也有简单语法知识),所以那些基础性东西我就不写了。我用的软件是vs2010,sqlserver2008。基础部分大多数是”控制台应用程序“,后面一点点延伸winform应用程序,web程序内的(HTML、css、javascript、jpuery、div+css)、MVC,ASP.NET。重点练习项目:超市管理系统、音乐播放器、局域网聊天(tcp/udp)、外网聊天(我相信很多程序员不会写) 想成功,多动脑!不要做个“手重眼轻”,能看会 未必能会写,很多都是用代码来讲解,全是“干货”。 接下来就是我这三个月的历程: 目录 一、基础 1.多态、继承   1)、virtual虚方法多态   2)、abstract抽象方法 2.泛型   1)、泛型方法   2)、泛型类   3)、泛型接口   4)、泛型约束 3.集合   1)、泛型集合(list)   2)、ArrayList集合   3)、Dictionary键值对集合   4)、Hashtable键值对集合   感觉少了什么,仔细想想我少写一个“枚举”,我就把枚举插在这个位置吧,临时在网上找的(不要夸我太懒,用到枚举的地方真的少,我就懒得写了)   5)、枚举 4.拆装箱 5.逆变和协变 6.工厂模式 7.部分类partial 8.密封类sealed 9.重写ToString方法 10.属性 11.里氏转换is、as 12.接口 13.接口与抽象类的区别 14.流操作 15.序列化、反序列化(XML和二进制) 16.PropertyInfo 反射 17.GUID编号 18.StringBuilder 19.委托、事件 20.编写DLL文件、调用DLL文件、传参、返回值 21.字符串大小写转换 22.字符串大小写转换 23.单例模式 24.文件、文件夹操作(path、File、Directory) 项目:----对基础知识的巩固复习 25.超市管理系统(控制台应用程序) 26.小游戏(石头剪刀布) 27.英汉词典 28.音乐播放器(winform)--------------这个软件会做了,才能代表C# winform开发 “基础”已经学会了 --------------------基础基本差不多了,等到全部结束后,我们再来更详细练习基础部分----------------------   二、线程 1.创建线程_前台_后台_挂起_唤醒_阻塞_终止_优先级 2.同步 3.线程池 4.异步 三、进程 1.进程简单操作 2.进程间通信 项目练习:映射综合   四、网络编程  大概了解tcp、udp、socket工作原理,代码是如何编写的,先不需要立刻学会,下面我还会很详细你讲解的 了解部分: 1.tcp 2.udp 3.标准的socket 详细讲解:---->>>>(我花了两块五买的教程,拿出来了给大家分享,代码已经全是我亲手写的了) 1.复习委托机制 2.复习回调机制 3.TCP服务端 4.同步TCP客户端 5.异步TCP客户端 6.UDP进程通信 7.UDP广播和组播 8.局域网聊天室 9.WebClient  10.打洞(可以不用学 ,高端技术很难得) 项目练习:     局域网聊天(简单)、     局域网聊天(仿QQ)、     广域网聊天(仿QQ,很难,学不会就不要学了,嘿嘿很多程序员都不会的)     串口通讯(详细讲解,我认为串口要好好学习,现在的可以都是软件和硬件搭配的好才最值钱,就不过多解释了)  --------------------------------------------------------------------------------------------------------------- 数据库了 第一部分: ac my sq  他们的工作原理和操作 第二部分: 和winform的搭配使用 三层架构 项目:管理系统之类的 第三部分(自选喽) ao很火 ----------------------------------------------------------------------------------------------------------------------------- WPF 把winform管理系统之类的项目改写成WPF ---------------------- ASP.NET html css js jq  MVC 把winform管理系统之类的项目改写成ASP.NET -------------------------------------------- 细致化的复习winform控件之类的知识 ----------------------------------------- 在来几个其他技术 --------------------------------------- 结束!发言               一、基础 1.多态、继承  1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 //实现多态的手段 7 //1)、虚方法 8 //步骤: 9 //1、将父类的方法标记为虚方法 ,使用关键字 virtual,这个函数可以被子类重新写一个遍。 10 //理解:通过继承实现的不同对象调用相同的方法,表现出不同的行为,称之为多态。 11 12 //2)、抽象类 13 //当父类中的方法不知道如何去实现的时候,可以考虑将父类写成抽象类,将方法写成抽象方法。 14 //派生类要实现抽象方法,派生抽象类可以不用重写父类抽象方法 15 //抽象类和接口 很像,我会在写接口时介绍他俩的区别 16 17 //3)、继承 18 //新类(即派生类)将获取基类的所有非私有数据和行为以及新类为自己定义的所有其他数据或行为。 19 //子类实现父类的属性和方法,并在此基础上进行相关的扩展 20 21 //下面是虚方法的多态: 22 namespace _1.多态_虚方法_ 23 { 24 public class Animal 25 { 26 public virtual void Eat()//虚方法 virtual 27 { 28 Console.WriteLine("动物会吃东西"); 29 } 30 } 31 32 public class Dog : Animal//继承Animal Animal为父类 33 { 34 public override void Eat()//重写虚方法 override 35 { 36 Console.WriteLine("狗也会吃东西"); 37 } 38 } 39 40 public class Cat : Animal 41 { 42 public override void Eat() 43 { 44 Console.WriteLine("猫也会吃东西"); 45 } 46 } 47 48 class Program 49 { 50 static void Main(string[] args) 51 { 52 //各种实现 体会吧 53 /*********************/ 54 Animal animal = new Animal(); 55 animal.Eat(); 56 Dog dog = new Dog(); 57 dog.Eat(); 58 Cat cat = new Cat(); 59 cat.Eat(); 60 /*******************/ 61 Console.WriteLine(); 62 /********************/ 63 Animal[] al = new Animal[3]; 64 al[0] = new Animal(); 65 al[0].Eat(); 66 al[1] = new Dog(); 67 al[1].Eat(); 68 al[2] = new Cat(); 69 al[2].Eat(); 70 /*********************/ 71 Console.WriteLine(); 72 /**********************/ 73 Animal a1 = new Animal(); 74 a1.Eat(); 75 Animal a2 = new Dog(); 76 a2.Eat(); 77 Animal a3 = new Cat(); 78 a3.Eat(); 79 80 Console.ReadKey(); 81 } 82 } 83 } virtual虚方法多态 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 //下面是抽象类的多态: 7 namespace _2.多态_抽象_ 8 { 9 public abstract class Animal 10 { 11 public abstract void Eat();//请注意抽象方法没有方法体 12 } 13 14 public abstract class Person:Animal 15 { 16 public abstract void 喝拉撒睡(); 17 } 18 19 public class Me : Person 20 { 21 public override void Eat() 22 { 23 Console.WriteLine("我会吃"); 24 } 25 //两个抽象方法缺一不可,要全部实现 26 public override void 喝拉撒睡() 27 { 28 Console.WriteLine("我还会喝拉撒睡"); 29 } 30 } 31 32 public class You : Animal //You继承Animal 继承用: 33 { 34 public override void Eat() 35 { 36 Console.WriteLine("你就是个吃货"); 37 } 38 } 39 40 class Program 41 { 42 static void Main(string[] args) 43 { 44 //Animal animal = new Animal();//抽象类不能被继承!!!! 45 Me m = new Me(); 46 m.Eat(); 47 m.喝拉撒睡(); 48 You y = new You(); 49 y.Eat(); 50 //////////// 51 Console.WriteLine(); 52 //////////// 53 Person[] p = new Person[1]; 54 Animal[] a=new Animal[2]; 55 a[0] = new Me(); 56 p[0]= new Me(); 57 p[0].Eat(); 58 p[0].喝拉撒睡(); 59 60 a[1] = new You(); 61 a[1].Eat(); 62 63 Console.ReadKey(); 64 } 65 } 66 } abstract抽象方法   2.泛型 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 泛型方法 7 { 8 class Program 9 { 10 static void fx(ref T l, ref T r) 11 { 12 //l和r值交换 13 T temp; 14 temp = l; 15 l = r; 16 r = temp; 17 } 18 19 static void Main(string[] args) 20 { 21 int a = 1; 22 int b = 2; 23 24 //fx(ref a, ref b); 25 fx(ref a, ref b);//这两种方式是一样的 26 27 Console.WriteLine(a+" "+b); 28 Console.ReadKey(); 29 } 30 } 31 } 泛型方法 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 泛型类 7 { 8 //我在这把通常大家爱写的T写成A,是为了让大家知道这知道代表一个类型不是固定的T 9 class FxClass 10 { 11 public void Fx(ref A l, ref A r) 12 { 13 A temp; 14 temp = l; 15 l = r; 16 r = temp; 17 } 18 } 19 20 class Program 21 { 22 static void Main(string[] args) 23 { 24 int a = 1; 25 int b = 2; 26 27 FxClass fxl = new FxClass(); 28 fxl.Fx(ref a, ref b); 29 Console.WriteLine(a + " " + b); 30 Console.ReadKey(); 31 } 32 } 33 } 泛型类 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 泛型接口 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 Student stu = new Student(); 13 Console.WriteLine(stu.Jia(2, 3)); 14 Console.WriteLine(stu.Jian(3, 1)); 15 Console.Read(); 16 } 17 } 18 19 //定义泛型接口 20 interface Icomuter 21 { 22 //定义泛型方法 23 T Jia(T item1, T item2); 24 T Jian(T item1, T item2); 25 26 } 27 28 //继承泛型接口 29 class Student : Icomuter 30 { 31 //继承实现泛型方法 32 public int Jia(int item1, int item2) 33 { 34 return item1 + item2; 35 } 36 public int Jian(int item1, int item2) 37 { 38 return item1 - item2; 39 } 40 } 41 } 泛型接口 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 #region .net泛型约束: 7 //所谓泛型,即通过参数化类型来实现在同一份代码上操作多种数据类型。 8 //泛型编程是一种编程范式,它利用“参数化类型”将类型抽象化,从而实现更为灵活的复用。 9 10 //在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制。 11 //如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误。 12 //这些限制称为约束。约束是使用 where 上下文关键字指定的。 13 #endregion 14 15 #region 约束 说明 16 //T:struct 17 //类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。 18 //T:class 19 //类型参数必须是引用类型,包括任何类、接口、委托或数组类型。 20 //T:new() 21 //类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。 22 //T:<基类名> 23 //类型参数必须是指定的基类或派生自指定的基类。 24 //T:<接口名称> 25 //类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。 26 //T:U 27 //为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束. 28 #endregion 29 30 namespace 泛型约束 31 { 32 class Program 33 { 34 static void fx(ref T l, ref T r) where T : IComparable //where是约束T类型在IComparable范围内 35 { 36 T temp; 37 temp = l; 38 l = r; 39 r = temp; 40 } 41 42 static void Main(string[] args) 43 { 44 int a = 1; 45 int b = 2; 46 47 //fx(ref a, ref b); 48 fx(ref a, ref b);//这两种方式是一样的 49 Console.WriteLine(a + " " + b); 50 Console.ReadKey(); 51 } 52 } 53 } 泛型约束   3.集合 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 //泛型集合 很简单 我就介绍一下几个部分 7 namespace _1.泛型集合_list_ 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 List list = new List(); 14 list.Add(1); 15 list.Add(2); 16 list.Add(3); 17 18 list.AddRange(new int[]{4,5,6});//添加集合 19 20 list.AddRange(list);//添加自己 21 22 int[] ii = list.ToArray();//转为数组 23 24 //怕误会以为泛型集合只可以操作整数 25 List lt = new List(); 26 //其实都是一样的,我就不多写了 27 lt.Add("请叫我:毕小帅");//大家都叫我“小帅”嘿嘿 28 lt.Add("我叫:小毕"); 29 30 //输出 31 for (int i = 0; i < list.Count; i++) 32 { 33 Console.Write(list[i].ToString()); 34 } 35 Console.WriteLine(); 36 for (int i = 0; i < lt.Count; i++) 37 { 38 Console.WriteLine(lt[i].ToString()); 39 } 40 Console.WriteLine(); 41 //也可以用foreach 42 //foreach在数据多的时候能比for的速度快 43 foreach (var item in list) 44 { 45 Console.Write(item.ToString()); 46 } 47 48 Console.ReadKey(); 49 } 50 } 51 } 泛型集合(list) 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Collections; 6 7 namespace ArrayList集合 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 ArrayList al = new ArrayList(); 14 //我喜欢它的最重要的一点就是:他什么类型都可以储存 15 //还不用像数组一样定义长度 16 //和list集合不同的就是不用定义类型 17 //缺点 读取时需要拆装箱 18 al.Add(1); //添加数字 19 al.Add("abc"); //添加字符串 20 al.Add('d'); //添加字符 21 22 al.AddRange(new char[] { 'e', 'f' });//添加数组 23 al.AddRange(al); //添加集合 自己 24 25 for (int i = 0; i < al.Count; i++) 26 { 27 Console.WriteLine(al[i].ToString()); 28 } 29 30 Console.Read(); 31 } 32 } 33 } ArrayList集合 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Dictionary键值对集合 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 //他和list集合性质差不多,先确定了类型 13 Dictionary dy = new Dictionary(); 14 15 dy.Add("key1", "value1");//添加 16 17 dy["key2"] = "value2";//如果键不存在也可以通过此方法来添加键/值对 18 19 dy.ContainsKey("key1"); //判断该键是否存在 20 21 foreach (Object key in dy.Keys) //遍历key 22 { } 23 foreach (Object value in dy.Values)//遍历value 24 { } 25 26 foreach (var de in dy) //同时遍历键/值对 27 { 28 Console.WriteLine(de.Key); 29 Console.WriteLine(de.Value); 30 } 31 dy.Clear();//清除所有 32 33 Console.Read(); 34 } 35 } 36 } Dictionary键值对集合 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Collections; 6 7 namespace Hashtable键值对集合 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 //我的感觉他和Arrylist集合性质差不多,不分类型的存储 14 //键值是唯一的 15 Hashtable ht = new Hashtable(); 16 17 ht.Add("key1", "value1"); 18 19 ht.Add("key2", "value2"); 20 21 ht.Contains("key1");//判断key1键是否存在 22 23 ht.ContainsKey("key1");//同上 24 25 foreach (Object key in ht.Keys) //遍历key 26 { } 27 foreach (Object value in ht.Values)//遍历value 28 { } 29 30 foreach (DictionaryEntry de in ht) //同时遍历键/值对 31 { 32 Console.WriteLine(de.Key); 33 Console.WriteLine(de.Value); 34 } 35 36 37 /****************************/ 38 ht.Remove("key1");//移除key1键元素 39 40 //前后做个判断 41 Console.WriteLine(); 42 foreach (DictionaryEntry de in ht) //同时遍历键/值对 43 { 44 Console.WriteLine(de.Key); 45 Console.WriteLine(de.Value); 46 } 47 /********************************/ 48 49 50 51 /****************************/ 52 ht.Clear();//清除所有元素 53 54 //前后做个判断 55 Console.WriteLine(); 56 foreach (DictionaryEntry de in ht) //同时遍历键/值对 57 { 58 Console.WriteLine(de.Key); 59 Console.WriteLine(de.Value); 60 Console.WriteLine("这句话没输出就是键值对被清空了"); 61 } 62 63 64 Console.Read(); 65 } 66 } 67 } Hashtable键值对集合 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 枚举 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 Console.WriteLine(Enum.GetName(typeof(Man), 1)); //还是 刘备 (由值获取名字) 13 14 string[] array1 = Enum.GetNames(typeof(Man)); 15 Console.WriteLine(array1[1]); //关羽 16 17 Array array2 = Enum.GetValues(typeof(Man)); 18 Console.WriteLine(array2.GetValue(1)); //还是关羽 19 20 Type t = Enum.GetUnderlyingType(typeof(Man)); 21 Console.WriteLine(t); //输出 Int32 22 23 //由值获取内容 24 int i = 1; 25 string Name = Enum.Parse(typeof(Man), i.ToString()).ToString(); //此时 Name="刘备" 26 Console.WriteLine(Name); 27 28 //由值获取内容 29 string Name2 = "关羽"; 30 int j = Convert.ToInt32(Enum.Parse(typeof(Man), Name2)); //此时 j=2 31 Console.WriteLine(j); 32 33 //这段是我自己写的。。。。(不是全都复制的) 34 //判断输入的字符串是否在枚举内 35 Console.WriteLine("请输入"); 36 string str = Console.ReadLine(); 37 bool b = Enum.IsDefined(typeof(Week), str); 38 39 Console.WriteLine(b.ToString()); 40 41 Console.ReadKey(); 42 } 43 } 44 45 enum Man 46 { 47 刘备 = 1, 48 关羽 = 2, 49 张飞 = 3 50 } 51 enum Week 52 { 53 Monday = 1, 54 Tuesday = 2, 55 Wednesday = 3, 56 Sunday = 0, 57 Everyday = 1  //成员的值可以设置成一样的,但是成员不行 58 } 59 enum sex : byte  //显示指定枚举的底层数据类型 60 { 61 male, 62 female,  //此逗号可以省略 63 };       //此分号可以省略 64 65 } 枚举 4.拆装箱 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Collections; 6 using System.Diagnostics; 7 8 //装箱操作和拆箱操作是要额外耗费cpu和内存资源的, 9 //所以用泛型来减少装箱操作和拆箱操作消耗。 10 namespace 拆箱_装箱 11 { 12 class Program 13 { 14 static void Main(string[] args) 15 { 16 int n1 = 10; 17 object o = n1;//装箱 18 int nn = (int)o;//拆箱 19 20 21 ArrayList lista = new ArrayList(); 22 List list = new List(); 23 //这个循环发生了100万次装箱操作 24 Stopwatch sw = new Stopwatch(); 25 sw.Start(); 26 for (int i = 0; i < 10000000; i++) 27 { 28 list.Add(i); 29 } 30 sw.Stop();//计时 31 Console.WriteLine(sw.Elapsed); 32 Console.ReadKey(); 33 34 35 //这个地方没有发生任意类型的装箱或者拆箱 36 string str = "123"; 37 int n2 = Convert.ToInt32(str); 38 39 40 int n3 = 10; 41 IComparable ic = n3;//装箱 42 43 } 44 } 45 } 拆装箱  5.逆变和协变 1 在网上收集的,平时感觉不常用 2 我们都知道.Net里或者说在OO的世界里,可以安全地把子类的引用赋给父类引用,例如: 3 4 //父类 = 子类 5 string str = "string"; 6 object obj = str;//变了 7 8 out 支持协变 9 in 支持逆变 10 11 协变(Foo<父类> = Foo<子类> ): (个人理解:object(范围大)=string(范围小)) 12 13 //泛型委托: 14 public delegate T MyFuncA();//不支持逆变与协变 15 public delegate T MyFuncB();//支持协变 16 17 MyFuncA funcAObject = null; 18 MyFuncA funcAString = null; 19 MyFuncB funcBObject = null; 20 MyFuncB funcBString = null; 21 MyFuncB funcBInt = null; 22 23 funcAObject = funcAString;//编译失败,MyFuncA不支持逆变与协变 24 funcBObject = funcBString;//变了,协变 25 funcBObject = funcBInt;//编译失败,值类型不参与协变或逆变 26 27 //泛型接口 28 public interface IFlyA { }//不支持逆变与协变 29 public interface IFlyB { }//支持协变 30 31 IFlyA flyAObject = null; 32 IFlyA flyAString = null; 33 IFlyB flyBObject = null; 34 IFlyB flyBString = null; 35 IFlyB flyBInt = null; 36 37 flyAObject = flyAString;//编译失败,IFlyA不支持逆变与协变 38 flyBObject = flyBString;//变了,协变 39 flyBObject = flyBInt;//编译失败,值类型不参与协变或逆变 40 41 //数组: 42 string[] strings = new string[] { "string" }; 43 object[] objects = strings; 44 45 ---------------------------------------------------------------------------------- 46 47 逆变(Foo<子类> = Foo<父类>)(个人理解:string(小范围)=object(大范围)) 48 49 public delegate void MyActionA(T param);//不支持逆变与协变 50 public delegate void MyActionB(T param);//支持逆变 51 52 public interface IPlayA { }//不支持逆变与协变 53 public interface IPlayB { }//支持逆变 54 55 MyActionA actionAObject = null; 56 MyActionA actionAString = null; 57 MyActionB actionBObject = null; 58 MyActionB actionBString = null; 59 actionAString = actionAObject;//MyActionA不支持逆变与协变,编译失败 60 actionBString = actionBObject;//变了,逆变 61 62 IPlayA playAObject = null; 63 IPlayA playAString = null; 64 IPlayB playBObject = null; 65 IPlayB playBString = null; 66 playAString = playAObject;//IPlayA不支持逆变与协变,编译失败 67 playBString = playBObject;//变了,逆变 逆变和协变  6.工厂模式 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 //个人理解:工厂模式就是switch选择为核心,让用户自己选择要执行的方法(但是方法他是看不见的) 7 namespace 工厂模式 8 { 9 //第一步:先写抽象的父类 10 public abstract class All 11 { 12 public abstract void 上菜(); 13 } 14 //第二步:子类 重写父类方法 15 public class Gdfjdc : All 16 { 17 public override void 上菜() 18 { 19 Console.WriteLine("小二,上干豆腐卷大葱");//我是东北人 20 } 21 } 22 public class xcbdf : All 23 { 24 public override void 上菜() 25 { 26 Console.WriteLine("小二上小葱拌豆腐"); 27 } 28 } 29 public class xjdmg : All 30 { 31 public override void 上菜() 32 { 33 Console.WriteLine("小二上小鸡炖蘑菇"); 34 } 35 } 36 public class scy : All 37 { 38 public override void 上菜() 39 { 40 Console.WriteLine("小二上酸菜鱼"); 41 } 42 } 43 44 class Class1 45 { 46 //第三步:核心地方到了 switch选择 47 public static All GetUserStr(string str_user) 48 { 49 All db = null; 50 switch (str_user) 51 { 52 case "干豆腐卷大葱": db = new Gdfjdc(); 53 break; 54 case "小葱拌豆腐": db = new xcbdf(); 55 break; 56 case "小鸡炖蘑菇": db = new xjdmg(); 57 break; 58 case "酸菜鱼": db = new scy(); 59 break; 60 } 61 return db; 62 } 63 static void Main() 64 { 65 //开始用户选菜了 66 string str_user = Console.ReadLine(); 67 All db = GetUserStr(str_user); 68 db.上菜(); 69 70 Console.Read(); 71 } 72 } 73 } 工厂模式  7.部分类partial 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 部分类partial 7 { 8 partial class Program 9 { 10 static void Main(string[] args) 11 { 12 Print(); 13 Console.ReadKey(); 14 } 15 } 16 partial class Program 17 { 18 static string str = "我是大帅哥"; 19 } 20 21 partial class Program 22 { 23 public static void Print() 24 { 25 Console.WriteLine(str); 26 } 27 } 28 } 部分类partial  8.密封类sealed 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 密封类 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 Person.person(); 13 Test.test(); 14 15 Console.Read(); 16 } 17 } 18 //密封类用sealed关键字 19 // public static sealed class Person : Test//这是错误的!!类不能即是密封的又是静态static的 20 public sealed class Person : Test 21 { 22 public static void person() 23 { 24 Console.WriteLine("我是密封类"); 25 } 26 } 27 //public static class Test //这是错误的!!密封类不能继承于静态类 28 public class Test 29 { 30 public static void test() 31 { 32 Console.WriteLine("我是父类"); 33 } 34 } 35 //public class abc : Person { }//这是错误的!!密封类不能被继承 36 } 密封类sealed  9.重写ToString方法 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 //首先要知道ToString是系统内部自有的方法 7 //系统内定义:public override string ToString(); 是个可被重写的方法 8 //这个例子不是简单的说明重写ToString,说明有很多系统定义的方法,我们都可以拿来重写的 9 namespace 重写ToString方法 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 Person p = new Person(); 16 Console.WriteLine(p.ToString()); 17 Console.ReadKey(); 18 } 19 } 20 21 public class Person 22 { 23 public override string ToString() 24 { 25 return "Hello World"; 26 } 27 } 28 } 重写ToString方法 10.属性 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 //看网上对属性的讲解: 7 //c#中为什么要用属性,1、在给一个对象附值时,可以通过属性SET方法, 8 //对这个值进行一个过滤,看这个值服不服合属性的要求。2、有的对象的属性, 9 //需要只读的的,就可以省去SET方法,有的是只写的,就可以省去GET方法 10 11 //属性就是为了访问私有字段,也就是为了对私有字段进行封装 12 13 //公有属性是在任何外部类都可访问到的,如果有人不怀好意就会随意的修改的public字段, 14 //很危险的,但是如果改成private,就不能擅自修改,只能通过属性,而在属性里是可以 15 //加代码来判断别人付的值是否符合你的要求,不符合的可以直接拒绝赋值,这样就增加了安全性 16 namespace 属性 17 { 18 class Program 19 { 20 /// 21 /// 自动属性 22 /// 23 public int Age 24 { 25 get; 26 set; 27 } 28 29 //普通属性 30 private string _name; 31 32 public string Name 33 { 34 get { return _name; } 35 set { _name = value; } 36 } 37 38 static void Main(string[] args) 39 { 40 } 41 } 42 } 属性 11.里氏转换is、as 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 //里氏转换 6 //1、子类可以复制给父类:如果有一个地方需要一个父类做参数,我们可以给一个子类代替 7 //2、如果父类中装的是子类对象,那么我们是可以将这个父类转换为子类对象的 8 9 namespace is_as 10 { 11 public class User 12 { 13 14 } 15 class Program 16 { 17 static void Main(string[] args) 18 { 19 Object obj1 = new User(); 20 //is:表示类型转换----如果转换成功,返回一个true,否则返回false 21 if (obj1 is User)//这里一定要知道object是一切类的父类,父类转子类判断是否成功 22 { 23 User user1 = (User)obj1;//将父类对象强制转换成子类对象 24 Console.WriteLine("转换成功"); 25 } 26 else 27 { 28 Console.WriteLine("转换失败"); 29 } 30 31 /****************************/ 32 33 Object obj2 = new User(); 34 //as:表示类型转换-----如果能够转换则返回一个对应的对象,否则返回一个null 35 User user2 = obj2 as User;//将父类对象强制转换成子类对象 36 if (user2 == null) 37 { 38 Console.WriteLine("转换失败"); 39 } 40 else 41 { 42 Console.WriteLine("转换成功"); 43 } 44 Console.ReadKey(); 45 } 46 } 47 } 里氏转换is、as  12.接口 别人写的,注释写的很好,简单易懂 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 #region 介绍接口 7 //如要实现接口类中对应的成员必须是公共的、非静态的, 8 //并且与接口成员具有相同的名称和签名。 9 //并且接口的实例化不能像类那样Program program = new Program(); 10 //其中上述程序中红色部分即为接口的实例化(使用派生类对象实例化接口)。 11 //1、接口的概念及声明 12 13 //接口是一种用来定义程序的协议,它描述可属于任何类或结构的一组相关行为。接口可有方法、属性、事件和索引器或这四种成员的任何组合类型,但不能包含字段。 14 15 //那么接口具有哪些特点呢? 16 17 //·接口类似于抽象基类:继承接口的任何非抽象类型都必须实现接口的所有成员(说明:如类A继承接口B,那么A中必须实现B中定义的属性,方法等)。 18 19 //·不能直接实例化接口 20 21 //·接口可以包含事件、索引器、方法和属性 22 23 //·接口不包含方法的实现 24 25 //·类和接口可以从多个接口继承 26 27 //·接口自身可以继承多个接口 28 29 //在声明接口时除了Interface和接口名称是必须的,其他都是可选项。另可使用new、public、protected、intenal和private等修饰符实现接口,但接口成员必须是公共的。 30 31 //2、接口的实现与继承 32 33 //声明实现接口的类时,需要在基类列表中包含类所实现的接口的名称。 34 #endregion 35 namespace 接口_单继承_ 36 { 37 interface ImyInterface 38 { 39 /// 40 /// 编号(可读可写) 41 /// 42 string ID 43 { 44 get; 45 set; 46 } 47 /// 48 /// 姓名(可读可写) 49 /// 50 string Name 51 { 52 get; 53 set; 54 } 55 /// 56 /// 显示定义的编号和姓名 57 /// 58 void ShowInfo(); 59 } 60 class Program : ImyInterface//继承自接口 61 { 62 string id = ""; 63 string name = ""; 64 /// 65 /// 编号 66 /// 67 public string ID 68 { 69 get 70 { 71 return id; 72 } 73 set 74 { 75 id = value; 76 } 77 } 78 /// 79 /// 姓名 80 /// 81 public string Name 82 { 83 get 84 { 85 return name; 86 } 87 set 88 { 89 name = value; 90 } 91 } 92 /// 93 /// 显示定义的编号和姓名 94 /// 95 public void ShowInfo() 96 { 97 Console.WriteLine("编号\t 姓名"); 98 Console.WriteLine(ID + "\t " + Name); 99 } 100 static void Main(string[] args) 101 { 102 Program program = new Program(); //实例化Program类对象 103 ImyInterface imyinterface = program; //使用派生类对象实例化接口ImyInterface 104 imyinterface.ID = "TM"; //为派生类中的ID属性赋值 105 imyinterface.Name = "C# 2.0从入门到应用开发"; //为派生类中的Name属性赋值 106 imyinterface.ShowInfo(); //调用派生类中方法显示定义的属性值 107 Console.ReadKey(); 108 } 109 } 110 } 接口-单继承 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 多继承 7 { 8 interface IPeople 9 { 10 /// 11 /// 姓名 12 /// 13 string Name 14 { 15 get; 16 set; 17 } 18 /// 19 /// 性别 20 /// 21 string Sex 22 { 23 get; 24 set; 25 } 26 } 27 interface ITeacher : IPeople //继承公共接口 28 { 29 /// 30 /// 教学方法 31 /// 32 void teach(); 33 } 34 interface IStudent : IPeople //继承公共接口 35 { 36 /// 37 /// 学习方法 38 /// 39 void study(); 40 } 41 class Program : IPeople, ITeacher, IStudent//多接口继承 42 { 43 string name = ""; 44 string sex = ""; 45 /// 46 /// 姓名 47 /// 48 public string Name 49 { 50 get 51 { 52 return name; 53 } 54 set 55 { 56 name = value; 57 } 58 } 59 /// 60 /// 性别 61 /// 62 public string Sex 63 { 64 get 65 { 66 return sex; 67 } 68 set 69 { 70 sex = value; 71 } 72 } 73 /// 74 /// 教学方法 75 /// 76 public void teach() 77 { 78 Console.WriteLine(Name + " " + Sex + " 教师"); 79 } 80 /// 81 /// 学习方法 82 /// 83 public void study() 84 { 85 Console.WriteLine(Name + " " + Sex + " 学生"); 86 } 87 static void Main(string[] args) 88 { 89 Program program = new Program(); //实例化类对象 90 ITeacher iteacher = program; //使用派生类对象实例化接口ITeacher 91 iteacher.Name = "TM"; 92 iteacher.Sex = "男"; 93 iteacher.teach(); 94 IStudent istudent = program; //使用派生类对象实例化接口IStudent 95 istudent.Name = "C#"; 96 istudent.Sex = "男"; 97 istudent.study(); 98 Console.ReadKey(); 99 } 100 } 101 } 接口-多继承 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 #region 7 //试想一下,如果在一个类A继承自接口B和C, 8 //并且在B和C中包含具有相同签名的成员, 9 //那么在类中实现该成员将导致两个接口都使用该成员作为他们的实现, 10 //然而,如果两个接口成员实现不同的功能, 11 //那么将会导致一个接口的成员实现不正确或两个接口的成员实现都不正确, 12 //这个时候我们应该如何处理呢?我们可以显示的实现接口成员, 13 //即创建一个仅通过接口调用并且特定于该接口的类成员。 14 #endregion 15 namespace 显示实现接口成员 16 { 17 interface ImyInterface1 18 { 19 /// 20 /// 求和方法 21 /// 22 /// 加法运算的和 23 int Add(); 24 } 25 interface ImyInterface2 26 { 27 /// 28 /// 求和方法 29 /// 30 /// 加法运算的和 31 int Add(); 32 } 33 class myClass : ImyInterface1, ImyInterface2 //继承接口 34 { 35 /// 36 /// 求和方法 37 /// 38 /// 加法运算的和 39 int ImyInterface1.Add() //显式接口成员实现 40 { 41 int x = 3; 42 int y = 5; 43 return x + y; 44 } 45 /// 46 /// 求和方法 47 /// 48 /// 加法运算的和 49 int ImyInterface2.Add() //显式接口成员实现 50 { 51 int x = 3; 52 int y = 5; 53 int z = 7; 54 return x + y + z; 55 } 56 } 57 class Program 58 { 59 static void Main(string[] args) 60 { 61 myClass myclass = new myClass(); //实例化接口继承类的对象 62 ImyInterface1 imyinterface1 = myclass; //使用接口继承类的对象实例化接口 63 Console.WriteLine(imyinterface1.Add()); //使用接口对象调用接口中方法 64 ImyInterface2 imyinterface2 = myclass; //使用接口继承类的对象实例化接口 65 Console.WriteLine(imyinterface2.Add()); //使用接口对象调用接口中方法 66 Console.ReadKey(); 67 } 68 } 69 } 70 //上面的实例中在Myclass类中,通过两个显示接口成员的方法分别实现了两个接口中的Add方法,在实例化不同的接口后,调用相应的方法实现输出结果。 显示实现接口成员 13.接口与抽象类的区别 1 c#接口和抽象类的区别 2 3 大家都容易把这两者搞混,我也一样,在听李建忠老师的设计模式时,他也老把抽象类说成接口,弄的我就更糊涂了,所以找了些网上的资料. 4 一、抽象类: 5 抽象类是特殊的类,只是不能被实例化;除此以外,具有类的其他特性;重要的是抽象类可以包括抽象方法,这是普通类所不能的。抽象方法只能声明于抽象类中,且不包含任何实现,派生类必须覆盖它们。另外,抽象类可以派生自一个抽象类,可以覆盖基类的抽象方法也可以不覆盖,如果不覆盖,则其派生类必须覆盖它们。 6 7 8 二、接口: 9 接口是引用类型的,类似于类,和抽象类的相似之处有三点: 10 1、不能实例化; 11 2、包含未实现的方法声明; 12 3、派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员); 13 14 另外,接口有如下特性: 15 接口除了可以包含方法之外,还可以包含属性、索引器、事件,而且这些成员都被定义为公有的。除此之外,不能包含任何其他的成员,例如:常量、域、构造函数、析构函数、静态成员。一个类可以直接继承多个接口,但只能直接继承一个类(包括抽象类)。 16 17 三、抽象类和接口的区别: 18 1.类是对对象的抽象,可以把抽象类理解为把类当作对象,抽象成的类叫做抽象类.而接口只是一个行为的规范或规定,微软的自定义接口总是后带able字段,证明其是表述一类类“我能做。。。”.抽象类更多的是定义在一系列紧密相关的类间,而接口大多数是关系疏松但都实现某一功能的类中. 19 2.接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法; 20 3.一个类一次可以实现若干个接口,但是只能扩展一个父类 21 4.接口可以用于支持回调,而继承并不具备这个特点. 22 5.抽象类不能被密封。 23 6.抽象类实现的具体方法默认为虚的,但实现接口的类中的接口方法却默认为非虚的,当然您也可以声明为虚的. 24 7.(接口)与非抽象类类似,抽象类也必须为在该类的基类列表中列出的接口的所有成员提供它自己的实现。但是,允许抽象类将接口方法映射到抽象方法上。 25 8.抽象类实现了oop中的一个原则,把可变的与不可变的分离。抽象类和接口就是定义为不可变的,而把可变的座位子类去实现。 26 9.好的接口定义应该是具有专一功能性的,而不是多功能的,否则造成接口污染。如果一个类只是实现了这个接口的中一个功能,而不得不去实现接口中的其他方法,就叫接口污染。 27 10.尽量避免使用继承来实现组建功能,而是使用黑箱复用,即对象组合。因为继承的层次增多,造成最直接的后果就是当你调用这个类群中某一类,就必须把他们全部加载到栈中!后果可想而知.(结合堆栈原理理解)。同时,有心的朋友可以留意到微软在构建一个类时,很多时候用到了对象组合的方法。比如asp.net中,Page类,有Server Request等属性,但其实他们都是某个类的对象。使用Page类的这个对象来调用另外的类的方法和属性,这个是非常基本的一个设计原则。 28 11.如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法. 29 30 四、抽象类和接口的使用: 31 1. 如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单的方法来控制组件版本。 32 2.如果创建的功能将在大范围的全异对象间使用,则使用接口。如果要设计小而简练的功能块,则使用接口。 33 3.如果要设计大的功能单元,则使用抽象类.如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。 34 4.抽象类主要用于关系密切的对象;而接口适合为不相关的类提供通用功能。 35 36 以下是我在网上看到的几个形象比喻,真的非常不错,呵呵: 37 1.飞机会飞,鸟会飞,他们都继承了同一个接口“飞”;但是F22属于飞机抽象类,鸽子属于鸟抽象类。 38 2. 就像铁门木门都是门(抽象类),你想要个门我给不了(不能实例化),但我可以给你个具体的铁门或木门(多态);而且只能是门,你不能说它是窗(单继承);一个门可以有锁(接口)也可以有门铃(多实现)。 门(抽象类)定义了你是什么,接口(锁)规定了你能做什么(一个接口最好只能做一件事,你不能要求锁也能发出声音吧(接口污染))。 接口与抽象类的区别  14.流操作 1 个人简单理解: 2 FileStream 操作字节的 3 StreamReader和StreamWriter 操作字符的 4 5 网络上的解释: 6 1. FileStream是一个较为底层的类,只能简单地读文件到缓存区,而StreamReader封装了一些高级方法如ReadLine() 7 2. FileStream可读可写,而StreamReader只能读不能写 8 3. FileStream不能指定编码(因为它看到的只是文件的二进制形式,当然无所谓编码),而StreamReader可以指定编码,一旦指定就不允许再更改,因此编码指定是放在它的构造方法里的。默认编码为System.Text.UTF8Encoding,实际上,在StreamReader的构造方法里,它会对文件进行编码检查,当然也可以不让它检查。 9 4. 若对是对二进制文件进行操作,最好使用BinaryReader,若要进行写操作,则用StreamWriter,同样StreamWriter也能按指定的编码进行读写 10 5. StreamReader的构造方法不一定需要FileStream,只需要文件名即可 11 6. StreamReader关闭后,与之相关的FileStream没有关闭,(通过FileStream的CanRead的测试) 12 13 FileStream对象表示在磁盘或网络路径上指向文件的流。这个类提供了在文件中读写字节的方法, 14 但经常使用StreamReader或StreamWriter执行这些功能。这是因为FileStream类操作的是字节和 15 字节数组,而Stream类操作的是字符数据。这是这两种类的一个重要区别,如果你是准备读取byte 16 数据的话,用StreamReader读取然后用System.Text.Encoding.Default.GetBytes转化的话,如下, 17 则可能出现数据丢失的情况,如byte数据的个数不对等。因此操作byte数据时要用FileStream。 18 19 string textContent = fileStream.ReadToEnd();byte[] bytes = System.Text.Encoding.Default.GetBytes(textContent); 20 字符数据易于使用,但是有些操作,比如随机文件访问(访问文件中间某点的数据),就必须由FileStream对象执行. 21 其中创建FileStream对象最简单的构造函数如下: 22 1FileStream file = newFileStream(fileName,FileMode.Member); 23 2FileStream file = newFileStream(fileName, FileMode.Member, FileAccess.Member); 理解Filestream与StreamReader和StreamWriter的区别 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO; 6 7 namespace _StreamReader 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 //使用StreamReader来读取一个文本文件 14 //首先在\bin\Debug目录里(也就是程序运行的当前目录里要有new.txt文件),否则报错 15 using (StreamReader sr = new StreamReader(@"new.txt", Encoding.UTF8))//UTF8是一种读取的格式,还有其他很多种格式 16 { 17 while (!sr.EndOfStream) 18 { 19 Console.WriteLine(sr.ReadLine()); 20 } 21 } 22 Console.ReadKey(); 23 } 24 } 25 } StreamReader读取文件内容 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO; 6 //FileStream 操作字节的 7 //StreamReader和StreamWriter 操作字符的 8 namespace _StreamWriter 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 //使用StreamWriter来写入一个文本文件 15 //首先在\bin\Debug目录里(也就是程序运行的当前目录里要有new.txt文件),否则报错 16 using (StreamWriter sw = new StreamWriter(@"new.txt", true)) 17 { 18 sw.Write("看我有木有把你覆盖掉"); 19 } 20 Console.WriteLine("OK"); 21 Console.ReadKey(); 22 } 23 } 24 } StreamWriter向文件内写入内容 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO; 6 7 namespace 文件流 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 //使用FileStream来读取数据 14 FileStream fsRead = new FileStream(@"bxs.txt", FileMode.OpenOrCreate, FileAccess.Read); 15 byte[] buffer = new byte[1024 * 1024 * 5]; 16 //2.84K 5M 17 //返回本次实际读取到的有效字节数 18 int r = fsRead.Read(buffer, 0, buffer.Length); 19 //将字节数组中每一个元素按照指定的编码格式解码成字符串 20 string s = Encoding.Default.GetString(buffer, 0, r); 21 //关闭流 22 fsRead.Close(); 23 //释放流所占用的资源 可以使用using替代它 24 fsRead.Dispose(); 25 Console.WriteLine(s); 26 Console.ReadKey(); 27 } 28 } 29 } FileStream 文件流读取文件 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO; 6 7 namespace 写入文件 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 //使用FileStream来写入数据 14 using (FileStream fsWrite = new FileStream(@"new.txt", FileMode.OpenOrCreate, FileAccess.Write)) 15 { 16 string str = "看我有没有把你覆盖掉"; 17 byte[] buffer = Encoding.UTF8.GetBytes(str); 18 fsWrite.Write(buffer, 0, buffer.Length); 19 } 20 Console.WriteLine("写入OK"); 21 Console.ReadKey(); 22 } 23 } 24 } FileStream 文件流写入文件内容 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO; 6 7 namespace 文件流拷贝多媒体文件 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 //思路:就是先将要复制的多媒体文件读取出来,然后再写入到你指定的位置 14 string source = @"abc.wmv"; 15 string target = @"new.wmv"; 16 CopyFile(source, target); 17 Console.WriteLine("复制成功"); 18 Console.ReadKey(); 19 } 20 21 public static void CopyFile(string soucre, string target) 22 { 23 //1、我们创建一个负责读取的流 24 using (FileStream fsRead = new FileStream(soucre, FileMode.Open, FileAccess.Read)) 25 { 26 //2、创建一个负责写入的流 27 using (FileStream fsWrite = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Write)) 28 { 29 byte[] buffer = new byte[1024 * 1024 * 5]; 30 //因为文件可能会比较大,所以我们在读取的时候 应该通过一个循环去读取 31 while (true) 32 { 33 //返回本次读取实际读取到的字节数 34 int r = fsRead.Read(buffer, 0, buffer.Length); 35 //如果返回一个0,也就意味什么都没有读取到,读取完了 36 if (r == 0) 37 { 38 break; 39 } 40 fsWrite.Write(buffer, 0, r); 41 } 42 } 43 } 44 } 45 } 46 } FileStream 文件流拷贝多媒体文件  15.序列化、反序列化(XML和二进制) 首先看一下我生成的XML文件,这里我就不讲解XML文件格式是怎么写的,很简单 1 2 3 - 4 5 6 - 7 8 毕毕 9 10 23 11 12 13 14 15 - 16 17 丹丹 18 19 22 20 21 22 23 生成的XML文件内容   1 序列化又称串行化,是.NET运行时环境用来支持用户定义类型的流化的机制。 2 其目的是以某种存储形成使自定义对象持久化,或者将这种对象从一个地方传输到另一个地方。 3 4 .NET框架提供了两种种串行化的方式: 5 1、是使用BinaryFormatter进行串行化; 6 2、使用XmlSerializer进行串行化。 7 第一种方式提供了一个简单的二进制数据流以及某些附加的类型信息, 8 而第二种将数据流格式化为XML存储。 9 可以使用[Serializable]属性将类标志为可序列化的。如果某个类的元素不想被序列化, 10 1、可以使用[NonSerialized]属性来标志, 11 2、可以使用[XmlIgnore]来标志。 12 13 序列化意思指的是把对象的当前状态进行持久化,一个对象的状态在面向对象的程序中是由属性表示的, 14 所以序列化类的时候是从属性读取值以某种格式保存下来,而类的成员函数不会被序列化, 15 .net存在几种默认提供的序列化,二进制序列化,xml和json序列化会序列化所有的实例共有属性。 讲解XML和二进制序列化 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO; 6 using System.Xml.Serialization; 7 8 namespace xml序列化_反序列化 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 List listpers = new List(); 15 Person p1 = new Person("毕毕", 23); 16 Person p2 = new Person("丹丹", 22); 17 18 listpers.Add(p1); 19 listpers.Add(p2);//把类的实例添加到listpers泛型列表中 20 21 XmlSerializeMethod(listpers);//序列化成xml 22 // ReXmlSerializeMethod();//反序列化 23 24 Console.Read(); 25 } 26 27 //序列化一个泛型列表到xml 28 public static void XmlSerializeMethod(List listPers) 29 { 30 using (FileStream fs = new FileStream("序列化.xml", FileMode.OpenOrCreate)) 31 { 32 XmlSerializer xs = new XmlSerializer(typeof(List)); 33 xs.Serialize(fs, listPers); 34 35 Console.WriteLine("序列化.xml" + "序列化文件成功"); 36 } 37 } 38 //反序列化一个xml到泛型列表 39 public static void ReXmlSerializeMethod() 40 { 41 using (FileStream fs = new FileStream("序列化.xml", FileMode.Open)) 42 { 43 XmlSerializer xs = new XmlSerializer(typeof(List)); 44 List listpers = xs.Deserialize(fs) as List; 45 if (listpers != null) 46 { 47 for (int i = 0; i < listpers.Count; i++) 48 { 49 listpers[i].SayHi(); 50 } 51 } 52 53 } 54 } 55 } 56 57 [Serializable]// 表示要进行序列化 58 public class Person 59 { 60 public Person() { } 61 public Person(string name, int age) 62 { 63 this.name = name; 64 this.age = age; 65 } 66 private string name; 67 public string Name 68 { 69 get { return name; } 70 set { name = value; } 71 } 72 [NonSerialized]//表示下面的age不进行序列化 73 private int age; 74 75 public int MyProperty 76 { 77 get { return age; } 78 set { age = value; } 79 } 80 81 public void SayHi() 82 { 83 Console.WriteLine("大家好,我是{0},今年{1}岁", name, age); 84 } 85 86 } 87 } xml序列化、反序列化 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO; 6 using System.Runtime.Serialization.Formatters.Binary; 7 8 //会生成.bin文件(二进制文件) 9 namespace 二进制序列化_反序列化 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 List listPers = new List(); 16 Person per1 = new Person("毕毕", 18); 17 Person per2 = new Person("丹丹", 20); 18 listPers.Add(per1); 19 listPers.Add(per2); 20 21 SerializeMethod(listPers);//序列化 22 Console.WriteLine("序列化完成"); 23 24 //ReserializeMethod();//反序列化,使用反序列化时把序列化部分注释,反序列化打开 25 26 #region 输出 27 //输出: 因为age不被序列化,所以为0 28 //序列化完成 29 //大家好,我是毕毕,今年0岁 30 //大家好,我是丹丹,今年0岁 31 #endregion 32 33 Console.ReadKey(); 34 } 35 36 static void ReserializeMethod() 37 { 38 //反序列化 39 using (FileStream fs = new FileStream("二进制.bin", FileMode.Open)) 40 { 41 BinaryFormatter bf = new BinaryFormatter(); 42 List list = bf.Deserialize(fs) as List; 43 if (list != null) 44 { 45 for (int i = 0; i < list.Count; i++) 46 { 47 list[i].SayHi(); 48 } 49 } 50 } 51 } 52 53 static void SerializeMethod(List listPers) 54 { 55 //序列化 56 using (FileStream fs = new FileStream("二进制.bin", FileMode.OpenOrCreate)) 57 { 58 BinaryFormatter bf = new BinaryFormatter(); 59 bf.Serialize(fs, listPers); 60 } 61 } 62 63 } 64 65 [Serializable]// 表示要进行序列化 66 class Person 67 { 68 public Person(){} 69 public Person(string name, int age) 70 { 71 this.name = name; 72 this.age = age; 73 } 74 private string name; 75 public string Name 76 { 77 get { return name; } 78 set { name = value; } 79 } 80 [NonSerialized]//表示下面的age不进行序列化 81 private int age; 82 83 public int MyProperty 84 { 85 get { return age; } 86 set { age = value; } 87 } 88 89 public void SayHi() 90 { 91 Console.WriteLine("大家好,我是{0},今年{1}岁",name,age); 92 } 93 } 94 } BinaryFormatter 二进制序列化、反序列化 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 //少量属性的自动化操作手动添加几下当然是没有问题的,但是属性数量较多的时候敲起 7 //这些繁锁的代码可以困了,再说对扩展和维护性造成很多的不便,这时,就需要使用反射来实现了。 8 9 //为了方便查看,建议使用计时窗口。也可以节点查看 10 11 //了解了类的属性反射使用后,那么方法也是可以这样做的, 12 //即t.GetProperties()改为t.GetMethods(),操作方法同上。 13 namespace 反射 14 { 15 public class A 16 { 17 public int Property1 { get; set; } 18 } 19 class Program 20 { 21 static void Main() 22 { 23 A aa = new A(); 24 Type type = aa.GetType();//获取类型 25 // string aaname = aa.Name; //获取类型名 26 // string aafullname = aa.FullName; //获取命名空间 27 // Type stu1 = typeof(Student); 28 // Type stu = Type.GetType("反射.Student"); 29 System.Reflection.PropertyInfo propertyInfo = type.GetProperty("Property1"); 30 propertyInfo.SetValue(aa, 5, null);//给对应属性赋值.可以改成循环赋值 31 int value = (int)propertyInfo.GetValue(aa, null); 32 Console.WriteLine(value); 33 34 Console.Read(); 35 } 36 } 37 38 public class Student 39 { 40 public void Abc() 41 { 42 Console.WriteLine("无所谓啦,反正不用输出的"); 43 } 44 } 45 } 16.PropertyInfo反射 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 //少量属性的自动化操作手动添加几下当然是没有问题的,但是属性数量较多的时候敲起 7 //这些繁锁的代码可以困了,再说对扩展和维护性造成很多的不便,这时,就需要使用反射来实现了。 8 9 //为了方便查看,建议使用计时窗口。也可以节点查看 10 11 //了解了类的属性反射使用后,那么方法也是可以这样做的, 12 //即t.GetProperties()改为t.GetMethods(),操作方法同上。 13 namespace 反射 14 { 15 public class A 16 { 17 public int Property1 { get; set; } 18 } 19 class Program 20 { 21 static void Main() 22 { 23 A aa = new A(); 24 Type type = aa.GetType();//获取类型 25 // string aaname = aa.Name; //获取类型名 26 // string aafullname = aa.FullName; //获取命名空间 27 // Type stu1 = typeof(Student); 28 // Type stu = Type.GetType("反射.Student"); 29 System.Reflection.PropertyInfo propertyInfo = type.GetProperty("Property1"); 30 propertyInfo.SetValue(aa, 5, null);//给对应属性赋值.可以改成循环赋值 31 int value = (int)propertyInfo.GetValue(aa, null); 32 Console.WriteLine(value); 33 34 Console.Read(); 35 } 36 } 37 38 public class Student 39 { 40 public void Abc() 41 { 42 Console.WriteLine("无所谓啦,反正不用输出的"); 43 } 44 } 45 } PropertyInfo 反射  17.GUID编号 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 //GUID代码是不重复的编号 7 //有时可以使用GUID编号作为数据库的id值 8 //买商品时收款机打印的编号就是利用的GUID编号 9 namespace _13.GUID代码 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 //产生一个不会重复的编号 16 Console.WriteLine(Guid.NewGuid().ToString()); 17 Console.WriteLine(Guid.NewGuid().ToString()); 18 Console.WriteLine(Guid.NewGuid().ToString()); 19 Console.WriteLine(Guid.NewGuid().ToString()); 20 Console.WriteLine(Guid.NewGuid().ToString()); 21 Console.ReadKey(); 22 23 //这只是一次结果,每次都不一样的 24 //c2ad2105-28c3-48cc-a550-ec6ecce7deba 25 //18881ad6-0cb9-49a5-b1c2-4747ccb3111c 26 //26c63df2-63f9-4d5b-8648-c6fd19d08c19 27 //a6b02a41-af88-4fd9-8b80-d14b4ef423d9 28 //3d8e93cb-5982-4b6f-bf63-33489929963a 29 } 30 } 31 } GUID编号 18.StringBuilder 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 //String 在进行运算时(如赋值、拼接等)会产生一个新的实例, 8 //而 StringBuilder 则不会,StringBuilder会在内存中开辟一块 9 //连续的内存,当增加字符串实际上是针对同一块内存的修改,所以效率更高。 10 //一字符串进行操作时最好使用 StringBuilder,不要使用 String 11 12 namespace _14_高效的StringBuilder 13 { 14 class Program 15 { 16 static void Main(string[] args) 17 { 18 StringBuilder sb = new StringBuilder(); 19 sb.Append("张三"); 20 sb.Append("李四"); 21 sb.Append("王五"); 22 sb.Append("李四"); 23 sb.Insert(1, 123); 24 sb.Replace("李四", "赵六");//把sb集合里的所有“李四”替换成赵六 25 sb.Replace("赵六", "七七"); 26 Console.WriteLine(sb.ToString()); 27 Console.ReadKey(); 28 } 29 } 30 } StringBuilder的使用 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 11 //winform类型 12 namespace 使用StringBuilder来拼接网页 13 { 14 public partial class Form1 : Form 15 { 16 public Form1() 17 { 18 InitializeComponent(); 19 } 20 21 private void button1_Click(object sender, EventArgs e) 22 { 23 24 StringBuilder sb = new StringBuilder(); 25 sb.Append(""); 26 sb.Append(""); 27 sb.Append(""); 28 sb.Append(""); 29 sb.Append(""); 30 sb.Append(""); 31 sb.Append(""); 32 sb.Append(""); 33 sb.Append(""); 34 sb.Append(""); 35 sb.Append(""); 36 sb.Append(""); 37 sb.Append(""); 38 sb.Append(""); 39 sb.Append(""); 40 sb.Append(""); 41 sb.Append(""); 42 sb.Append(""); 43 sb.Append(""); 44 sb.Append(""); 45 sb.Append(""); 46 sb.Append(""); 47 sb.Append(""); 48 sb.Append(""); 49 sb.Append(""); 50 sb.Append(""); 51 sb.Append(""); 52 sb.Append(""); 53 sb.Append(""); 54 sb.Append("
星期一星期一星期一星期一
星期一星期一星期一星期一
星期一星期一星期一星期一
星期一星期一星期一星期一
"); 55 sb.Append(""); 56 sb.Append(""); 57 webBrowser1.DocumentText = sb.ToString();//这是添加了一个窗体空间,显示网页数据的 58 } 59 } 60 } 使用StringBuilder来拼接网页  体现一下StringBuilder和String的差距,网上直接复制的一份对比程序 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 7 //string类型的特别之处在于我们可以像使用值类型那样使用string类型, 8 //而实际上string是引用类型。既然是引用类型,CLR就会把string类型保 9 //存在托管堆上。当我们使用str1 = str1 + i.ToString();进行拼接,由 10 //于string类型的恒定性,不会改变str1在内存中的地址,而是在托管堆上 11 //创建了另外一个字符串对象。如此,拼接10000次,就创建了10000个string 12 //类型对象,效率难免低下。 13 14 //而StringBuilder会在内存中开辟一块连续的内存,当增加字符串实际上是 15 //针对同一块内存的修改,所以效率更高。 16 17 //当然,到底使用硬拼接字符串,还是使用StringBuilder,不是绝对的,要 18 //看情况。当拼接字符串很少的情况下,当然直接硬拼接字符串就行了。 19 20 namespace StringBuilder和String比速度 21 { 22 class Program 23 { 24 static void Main(string[] args) 25 { 26 string str1 = string.Empty; 27 Stopwatch sw1 = new Stopwatch();//计时运行的时间 28 sw1.Start(); 29 for (int i = 0; i < 10000; i++) 30 { 31 str1 = str1 + i.ToString(); 32 } 33 sw1.Stop(); 34 Console.WriteLine("拼接字符串所耗费时间为:" + sw1.ElapsedMilliseconds + "毫秒"); 35 StringBuilder str2 = new StringBuilder(10000); 36 Stopwatch sw2 = new Stopwatch(); 37 sw2.Start(); 38 for (int i = 0; i < 10000; i++) 39 { 40 str2.Append(i.ToString()); 41 } 42 sw2.Stop(); 43 Console.WriteLine("使用StringBuilder所耗费时间为:" + sw2.ElapsedMilliseconds + "毫秒"); 44 Console.ReadKey(); 45 } 46 } 47 } 48 49 //运行结果: 50 //拼接字符串所耗费时间为:148毫秒 51 //使用StringBuilder所耗费时间为:1毫秒 StringBuilder和String速度对比  19.委托 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 //个人理解概述: 7 //委托是C#内重重重点,都知道启动程序时一定会启动主线程,主线程按着顺序向下执行。 8 //在当点击一个控件需要执行一个以上的任务时,主线程就蒙了,还是会继续工作但是界面“假死”。 9 //这是就要用到多线程,这个之后讲线程,c#是不允许跨线程的,这是委托就起到了作用。 10 //委托传值很方便,在之后就会发现委托和线程是个好基友。 11 //委托是有个好东西,他的好处我就不在这多说了,上网查查。 12 namespace delegate委托简单例子 13 { 14 class Program 15 { 16 public delegate void NAME(string nm);//定义委托,定义委托时传参类型要与对应的方法参数类型一致 17 public static NAME nm; //实例委托 18 19 //给委托传递需要的方法 20 public static void Name(string nm) 21 { 22 Console.WriteLine("My name is {0}", nm); 23 Console.ReadKey(); 24 } 25 26 static void Main(string[] args) 27 { 28 nm = new NAME(Name); 29 nm("毕小帅");//启动委托 30 } 31 } 32 } delegate委托简单例子 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 泛型委托 7 { 8 class Program 9 { 10 public delegate void Del(T item); 11 public static void Notify(int i) 12 { 13 Console.WriteLine("我是整数泛型委托"); 14 } 15 public static void Notify(string i) 16 { 17 Console.WriteLine("我是字符串泛型委托"); 18 } 19 20 static void Show(string str) 21 { 22 Console.WriteLine(str); 23 } 24 static string show(string str) 25 { 26 return str; 27 } 28 29 static void Main(string[] args) 30 { 31 Del m1 = new Del(Notify); 32 m1(1); 33 Del m2 = new Del(Notify); 34 m2(""); 35 36 Console.WriteLine(); 37 38 Action str = Show;//Action 是系统提供的不具有返回值 39 str("我是毕毕"); 40 41 Console.WriteLine(); 42 43 Func Str = show;//Func泛型具有返回值,Func内的第二个string是返回值类型 44 string ss = Str("我是小毕"); 45 Console.WriteLine(ss); 46 47 Console.ReadKey(); 48 } 49 } 50 } 三种泛型委托(普通泛型、Action、Func) 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 匿名委托 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 List names = new List(); 13 names.Add("毕毕love丹丹"); 14 names.Add("毕毕"); 15 names.Add("丹丹"); 16 17 List found = names.FindAll( //从前往后匹配相同字段 18 delegate(string name) 19 { 20 return name.StartsWith("毕毕", 21 StringComparison.OrdinalIgnoreCase); 22 }); 23 24 if (found != null) 25 { 26 foreach (string str in found) 27 Console.WriteLine(str); 28 } 29 Console.ReadKey(); 30 } 31 } 32 } 匿名委托 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace lamda 7 { 8 public delegate void Del1(); 9 public delegate void Del2(string name); 10 public delegate string Del3(string name); 11 12 class Program 13 { 14 static string n = "bb"; 15 16 static void Main(string[] args) 17 { 18 Del1 d1 = () => { Console.WriteLine("无参"+" "+"b1"); }; 19 20 Del2 d2 = (name) => { Console.WriteLine("无参"+" "+name); }; 21 22 Del3 d3 = (name) => { Console.WriteLine("return 返回的是"+name); return name; }; 23 24 d1(); d2(n); d3(n); 25 26 Console.ReadKey(); 27 } 28 } 29 } lamda委托 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 多播委托 7 { 8 public delegate void Del(); 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 Del del = T1; 14 15 del += T2; 16 del += T3; 17 del += T4; 18 del(); 19 20 Console.ReadKey(); 21 } 22 23 public static void T1() 24 { 25 Console.WriteLine("我是T1"); 26 } 27 public static void T2() 28 { 29 Console.WriteLine("我是T2"); 30 } 31 public static void T3() 32 { 33 Console.WriteLine("我是T3"); 34 } 35 public static void T4() 36 { 37 Console.WriteLine("我是T4"); 38 } 39 } 40 } 多播委托 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace class3 7 { 8 class Program 9 { 10 public delegate void Del(string name); 11 static void Main(string[] args) 12 { 13 ////Del del = new Del(Eat); 14 Del del = Eat1;//这俩种一样 15 del("毕小帅"); 16 17 Test("毕小帅", Eat1); 18 Test("丹丹", Eat2); 19 20 //匿名委托 21 Del d = delegate(string name) 22 { 23 Console.WriteLine("你好"+name); 24 }; 25 d("毕毕"); 26 27 //lamda表达式 => 28 Del dd = (string name) => { Console.WriteLine("hello" + name); }; 29 dd("小毕"); 30 31 Console.ReadKey(); 32 } 33 34 public static void Test(string name, Del del) 35 { 36 del(name); 37 } 38 39 public static void Eat1(string name) 40 { 41 Console.WriteLine(name+"吃了吗"); 42 } 43 public static void Eat2(string name) 44 { 45 Console.WriteLine(name + "吃了吗?"); 46 } 47 } 48 } 匿名委托和lamda综合 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 委托事件 7 { 8 class Program 9 { 10 public delegate void SalaryCompute(); //声明一个代理类 11 12 public class Employee 13 { 14 public event SalaryCompute OnSalaryCompute; //定义事件,将其与代理绑定 15 16 public virtual void FireEvent() //触发事件的方法 17 { 18 if (OnSalaryCompute != null) 19 { 20 OnSalaryCompute(); //触发事件 21 } 22 } 23 } 24 25 public class HumanResource 26 { 27 public void SalaryHandler() //事件处理函数 28 { 29 Console.WriteLine("我可终于被打印出来了"); //只是打印一行字而已 30 } 31 32 public static void Main() 33 { 34 Employee ep = new Employee(); 35 HumanResource hr = new HumanResource(); 36 ep.OnSalaryCompute += new SalaryCompute(hr.SalaryHandler); //注册,委托连接到事件处理函数 37 ep.FireEvent(); //触发事件 38 Console.Read(); 39 } 40 } 41 } 42 } 委托事件 收集一个类型不错的委托事件 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 事件练习 7 { 8 class Program 9 { 10 11 static void Main() 12 { 13 God.Start(); 14 } 15 /// 16 /// 类EatEventArgs 必须继承自类EventArgs,用来引发事件时封装数据 17 /// 18 public class EatEventArgs : EventArgs 19 { 20 public String restrauntName; //饭店名称 21 public decimal moneyOut; //准备消费金额 22 } 23 24 /// 25 /// 这个委托用来说明处理吃饭事件的方法的方法头(模式) 26 /// 27 public delegate void EatEventHandler(object sender, EatEventArgs e); 28 29 /// 30 /// 引发吃饭事件(EateEvent)的类Master(主人),这个类必须 31 /// 1.声明一个名为EatEvent的事件: public event EatEventHandler EatEvent; 32 /// 2.通过一个名为OnEatEvent的方法来引发吃饭事件,给那些处理此事件的方法传数据; 33 /// 3.说明在某种情形下引发事件呢?在饿的时候。用方法Hungrg来模拟。 34 /// 35 public class Master 36 { 37 //声明事件 38 public event EatEventHandler EatEvent; 39 40 //引发事件的方法 41 public void OnEatEvent(EatEventArgs e) 42 { 43 if (EatEvent != null) 44 { 45 EatEvent(this, e); 46 } 47 } 48 49 //当主人饿的时候,他会指定吃饭地点和消费金额。 50 public void Hungry(String restrauntName, decimal moneyOut) 51 { 52 EatEventArgs e = new EatEventArgs(); 53 e.restrauntName = restrauntName; 54 e.moneyOut = moneyOut; 55 56 Console.WriteLine("主人说:"); 57 Console.WriteLine("我饿了,要去{0}吃饭,消费{1}元", e.restrauntName, e.moneyOut); 58 59 //引发事件 60 OnEatEvent(e); 61 } 62 } 63 64 /// 65 /// 类Servant(仆人)有一个方法ArrangeFood(安排食物)来处理主人的吃饭事件 66 /// 67 public class Servant 68 { 69 public void ArrangeFood(object sender, EatEventArgs e) 70 { 71 Console.WriteLine(); 72 Console.WriteLine("仆人说:"); 73 Console.WriteLine("我的主人, 您的命令是 : "); 74 Console.WriteLine("吃饭地点 -- {0}", e.restrauntName); 75 Console.WriteLine("准备消费 -- {0}元 ", e.moneyOut); 76 Console.WriteLine("好的,正给您安排。。。。。。。。\n"); 77 System.Threading.Thread.Sleep(5000); 78 Console.WriteLine("主人,您的食物在这儿,请慢用"); 79 Console.Read(); 80 } 81 } 82 83 /// 84 /// 类God安排qinshihuang(秦始皇)的仆人是lisi(李斯),并让李斯的ArrangeFood 85 /// 方法来处理qinshihuang的吃饭事件:qinshihuang.EatEvent += new EatEventHandler(lishi.ArrangeFood); 86 /// 87 public class God 88 { 89 public static void Start() 90 { 91 Master qinshihuang = new Master(); 92 Servant lishi = new Servant(); 93 94 qinshihuang.EatEvent += new EatEventHandler(lishi.ArrangeFood); 95 96 //秦始皇饿了,想去希尔顿大酒店,消费5000元 97 qinshihuang.Hungry("希尔顿大酒店", 5000.0m); 98 } 99 } 100 } 101 } 委托事件  下面的是两个窗体简单传值(winform) 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace 窗体传值 11 { 12 public partial class Form1 : Form 13 { 14 public Form1() 15 { 16 InitializeComponent(); 17 } 18 19 private void button1_Click(object sender, EventArgs e) 20 { 21 Form2 f2 = new Form2(Show); 22 f2.Show(); 23 } 24 25 void Show(string str) 26 { 27 label1.Text = str; 28 } 29 } 30 } Form1代码 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace 窗体传值 11 { 12 public delegate void Del(string str); //定义委托 13 14 public partial class Form2 : Form 15 { 16 public Del _del; 17 public Form2(Del del)//是将Form1里的Show()方法传过来了 18 { 19 this._del = del; //这个del可以想象成Form1窗体里的show(....)方法 20 InitializeComponent(); 21 } 22 //时钟 也可以写成while 都是一个效果 23 private void timer1_Tick(object sender, EventArgs e) 24 { 25 _del(textBox1.Text);//Form2窗体开启时Form1了的Show()方法就成为了 委托调用的方法 26 } 27 } 28 } Form2代码   1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 7 namespace 调用exe 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 //Process 是进程类 14 #region 这俩是一样的 15 Process.Start("notePad");//可以不加.exe =(notepad.exe) 16 //如果不是系统关联的程序指明路径就行了。 17 Process.Start(@"D:\Program Files (x86)\Tencent\QQ\Bin\QQ.exe");//这是我的qq安装路径 18 //我比较喜欢这个 19 Process p = Process.Start("notepad.exe"); 20 p.WaitForExit();//等待外部程序退出后才能往下执行 21 #endregion 22 23 #region 下面这俩是一样的 24 System.Diagnostics.Process exep = new System.Diagnostics.Process(); 25 exep.StartInfo.FileName = "notePad"; 26 exep.StartInfo.Arguments = @"C:\Users\Administrator\Desktop\字符串的知识"; 27 exep.StartInfo.CreateNoWindow = true; 28 exep.StartInfo.UseShellExecute = false; 29 exep.Start(); 30 exep.WaitForExit();//关键,等待外部程序退出后才能往下执行 31 //我喜欢这个 32 ProcessStartInfo startInfo = new ProcessStartInfo("IExplore.exe"); 33 startInfo.Arguments = "www.baidu.com";//启动参数 34 startInfo.WindowStyle = ProcessWindowStyle.Minimized; 35 Process.Start(startInfo); 36 #endregion 37 } 38 } 39 } Process 启动进程  20.编写DLL文件、调用DLL文件、传参、返回值   1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 //一.创建dll文件 6 //  1.在vs2010中创建Class Library 项目;(就是建个类) 7 // 2.在类中写上下面代码 8 // 3.3.按F6编译生成Dll文件 不要按别的!!按F6!!! 9 //二.调用dll文件(这步在新建的项目中) 10 namespace ClassLibrary1 11 { 12 public static class Class1 13 { 14 public static string Messages() 15 { 16 //定义了一个方法,此方法的作用就是返回下面字符串。 17 return "欢迎使用Visual Studio 2010 做的DLL文件!"; 18 } 19 static int s = 0; 20 public static int INT(int ii) 21 { 22 for (int i = 0; i < 10; i++) 23 { 24 s += ii; 25 } 26 return s; 27 } 28 } 29 } 编写DLL文件   1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Runtime.InteropServices; 10 using ClassLibrary1; 11 //二.调用dll文件 12 //将第一步生成的Dll文件(\bin\Debug\myFirstDll.dll)拷贝到新建的现在的项目文件夹下 13 //添加引用ClassLibrary1 14 //编辑代码(下面代码) 15 //编译运行 16 namespace 调用外部dll 17 { 18 public partial class Form1 : Form 19 { 20 public Form1() 21 { 22 InitializeComponent(); 23 } 24 25 [DllImport("user32.dll", EntryPoint = "MessageBoxA")] 26 27 static extern int MsgBox(int hWnd, string msg, string caption, int type); 28 private void button1_Click(object sender, EventArgs e) 29 { 30 MsgBox(0, " 这就是用 DllImport 调用 DLL 弹出的提示框哦! ", " 小毕毕 ", 0x30); 31 } 32 33 //调用自己写的dll文件 34 [DllImport("ClassLibrary1.dll")] 35 36 static extern string Messages();// 37 private void button2_Click(object sender, EventArgs e) 38 { 39 string str = Class1.Messages(); 40 MessageBox.Show(str); 41 } 42 43 [DllImport("ClassLibrary1.dll")] 44 45 static extern int INT(); 46 private void button3_Click(object sender, EventArgs e) 47 { 48 int s = Class1.INT(10); 49 MessageBox.Show(s.ToString()); 50 } 51 } 52 } 调用Dll文件、传参、返回值 21.C# 操作XML  1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Xml; 7 namespace _03创建XML 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 //通过代码来创建XML文档 14 //1、引用命名空间 15 //2、创建XML文档对象 16 XmlDocument doc = new XmlDocument(); 17 //3、创建第一个行描述信息,并且添加到doc文档中 18 XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", "utf-8", null); 19 doc.AppendChild(dec); 20 //4、创建根节点 21 XmlElement books = doc.CreateElement("Books"); 22 //将根节点添加到文档中 23 doc.AppendChild(books); 24 25 //5、给根节点Books创建子节点 26 XmlElement book1 = doc.CreateElement("Book"); 27 //将book添加到根节点 28 books.AppendChild(book1); 29 30 31 //6、给Book1添加子节点 32 XmlElement name1 = doc.CreateElement("Name"); 33 name1.InnerText = "毕毕"; 34 book1.AppendChild(name1); 35 36 XmlElement price1 = doc.CreateElement("Price"); 37 price1.InnerText = "10"; 38 book1.AppendChild(price1); 39 40 XmlElement des1 = doc.CreateElement("Des"); 41 des1.InnerText = "好看"; 42 book1.AppendChild(des1); 43 44 XmlElement book2 = doc.CreateElement("Book"); 45 books.AppendChild(book2); 46 47 48 XmlElement name2 = doc.CreateElement("Name"); 49 name2.InnerText = "毕毕"; 50 book2.AppendChild(name2); 51 52 XmlElement price2= doc.CreateElement("Price"); 53 price2.InnerText = "10"; 54 book2.AppendChild(price2); 55 56 XmlElement des2 = doc.CreateElement("Des"); 57 des2.InnerText = "好看"; 58 book2.AppendChild(des2); 59 60 doc.Save("Books.xml"); 61 Console.WriteLine("保存成功"); 62 Console.ReadKey(); 63 } 64 } 65 } 创建XML文件 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Xml; 7 namespace _4_创建带属性的XML文档 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 XmlDocument doc = new XmlDocument(); 14 XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", "utf-8","yes"); 15 doc.AppendChild(dec); 16 17 XmlElement order = doc.CreateElement("Order"); 18 doc.AppendChild(order); 19 20 XmlElement customerName = doc.CreateElement("CustomerName"); 21 customerName.InnerXml = "

我是一个p标签

"; 22 order.AppendChild(customerName); 23 24 XmlElement customerNumber = doc.CreateElement("CustomerNumber"); 25 customerNumber.InnerText = "

我是一个p标签

"; 26 order.AppendChild(customerNumber); 27 28 29 XmlElement items = doc.CreateElement("Items"); 30 order.AppendChild(items); 31 32 XmlElement orderItem1 = doc.CreateElement("OrderItem"); 33 //给节点添加属性 34 orderItem1.SetAttribute("Name", "充气**");//嘿嘿 35 orderItem1.SetAttribute("Count", "10"); 36 items.AppendChild(orderItem1); 37 38 XmlElement orderItem2 = doc.CreateElement("OrderItem"); 39 //给节点添加属性 40 orderItem2.SetAttribute("Name", "充气**"); 41 orderItem2.SetAttribute("Count", "10"); 42 items.AppendChild(orderItem2); 43 44 XmlElement orderItem3 = doc.CreateElement("OrderItem"); 45 //给节点添加属性 46 orderItem3.SetAttribute("Name", "充气**"); 47 orderItem3.SetAttribute("Count", "10"); 48 items.AppendChild(orderItem3); 49 50 doc.Save("XML.xml"); 51 Console.WriteLine("保存成功"); 52 Console.ReadKey(); 53 54 55 } 56 } 57 } 创建带属性的XML文档 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Xml; 7 using System.IO; 8 namespace _5_追击XML 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 //追加XML文档 15 XmlDocument doc = new XmlDocument(); 16 XmlElement books; 17 if (File.Exists("Books.xml")) 18 { 19 //如果文件存在 加载XML 20 doc.Load("Books.xml"); 21 //获得文件的根节点 22 books = doc.DocumentElement; 23 } 24 else 25 { 26 //如果文件不存在 27 //创建第一行 28 XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", "utf-8", null); 29 doc.AppendChild(dec); 30 //创建跟节点 31 books = doc.CreateElement("Books"); 32 doc.AppendChild(books); 33 } 34 //5、给根节点Books创建子节点 35 XmlElement book1 = doc.CreateElement("Book"); 36 //将book添加到根节点 37 books.AppendChild(book1); 38 39 40 //6、给Book1添加子节点 41 XmlElement name1 = doc.CreateElement("Name"); 42 name1.InnerText = "c#开发大全"; 43 book1.AppendChild(name1); 44 45 XmlElement price1 = doc.CreateElement("Price"); 46 price1.InnerText = "110"; 47 book1.AppendChild(price1); 48 49 XmlElement des1 = doc.CreateElement("Des"); 50 des1.InnerText = "看不懂"; 51 book1.AppendChild(des1); 52 53 54 doc.Save("Books.xml"); 55 Console.WriteLine("保存成功"); 56 Console.ReadKey(); 57 58 } 59 } 60 } 追加内容 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Xml; 7 8 namespace _6_读取XML文档 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 //比较综合点 15 XmlDocument doc = new XmlDocument(); 16 doc.Load("Books.xml"); 17 XmlNode root = doc.DocumentElement; 18 XmlNodeList nodeList = root.ChildNodes; 19 foreach (XmlNode node in nodeList) 20 { 21 Console.WriteLine(node.Name); 22 if (node.Name == "Book") 23 { 24 //找到name节点,进行操作 25 XmlNodeList xnl = doc.SelectNodes("/Books/Book/Name"); 26 foreach (XmlNode nd in xnl) 27 { 28 Console.WriteLine(nd.InnerText); 29 } 30 } 31 } 32 Console.ReadKey(); 33 } 34 } 35 } 36 #region 在网上一位博客那里收集过来了,我看他的资料我才会xml操作。我全部复制下来了 37 //C#操作XML时,要引入命名空间using System.Xml 38 39 //获取根节点的方法: 40 //1、知道根节点名称: 41 //XmlNode root = xmlDoc.SelectSingleNode("根节点名称"); 42 //2、不知道根节点名称: 43 //XmlElement root = xmlDoc.DocumentElement; 44 //xml中node(节点)和element(元素)的区别(还是不太明白) 45 //1、element是一个小范围的定义,必须含有完整信息的结点才能叫做元素。例如:
内容
,一个元素一定是一个节点,一个节点不一定是一个元素。 46 //2、node是基本对象,attribute,element,text等都是node的子对象。 47 //创建节点 48 //1、CreateElement() 方法 49 //有一个参数,两个参数,三个参数三种重载,参数类型均为string。 50 //一个参数:CreateElement("元素名称") 51 //两个参数:CreateElement("元素名称","元素的命名空间") 52 //三个参数:CreateElement("元素的前缀","元素的名称","元素的命名空间") 53 //2、CreateNode() 方法 54 //三个参数 都为string类型 55 //CreateNode("节点类型","节点名称","节点命名空间") 56 //四个参数 都为string类型 57 //CreateNode("节点类型","节点的前缀","节点名称","节点命名空间") 58 //问题:节点前缀有什么作用?结点命名空间有什么作用? 59 //增加节点: 60 //1、AppendChild() 方法 61 //2、InsertAfter(要插入的节点,参考节点) 方法 62 //3、InsertBefore(要插入的节点,参考节点) 方法 63 //增加节点属性: 64 //SetAttribute("属性名","属性值")方法 65 66 //删除节点属性: 67 //RemoveAttribute("属性名称") 68 //给节点添加数据: 69 //1、给节点的innerText赋值 70 //例子:XmlElement eName = doc.CreateElement("name"); 71 // eName.InnerText = aaaaa; 72 //2、添加XmlText节点,为其添加值 73 // 添加节点元素 74 // 将XmlText以子节点的方式添加给节点元素 75 //例子: XmlElement eName = xmlDoc.CreateElement("name"); 76 // XmlText tName = xmlDoc.CreateTextNode(aaaaa); 77 // eName.AppendChild(tName); 78 //寻找某个节点(寻找name节点): 79 //先找到根节点,找出根节点下的节点列表(XmlNodeList),遍历每个节点。 80 //再找每个节点下的节点列表,进行遍历,指导找到所需要的节点。 81 // 82 // 83 // 84 // 85 //方法: 86 //XmlNode root = xmlDoc.SelectSingleNode("msg"); 87 //XmlNodeList nodeList = root.ChildNodes; 88 //foreach (XmlNode node in nodeList) 89 //{ 90 // if(node.Name=="name") 91 // { 92 // 找到name节点,进行操作 93 // } 94 //} 95 //删除节点 96 //RemoveAll(无参数) 97 //RemoveChild(要移除的节点) 98 //更新节点 99 //1、ReplaceChild(新节点,老节点) 100 //建立一个新节点,替换老节点 101 //2、找到要更新的节点,重新设置其属性和数据 102 //用GridView显示xml文件中的数据 103 //view plaincopy to clipboardprint? 104 //DataSet ds = new DataSet(); 105 //ds.ReadXml(Server.MapPath("BooksInfo.xml")); 106 //GridView1.DataSource = ds.Tables[0]; 107 //GridView1.DataBind(); 108 //DataSet ds = new DataSet(); 109 //ds.ReadXml(Server.MapPath("BooksInfo.xml")); 110 //GridView1.DataSource = ds.Tables[0]; 111 //GridView1.DataBind(); 112 //xml文件 113 //view plaincopy to clipboardprint? 114 // 115 // 116 // 117 // 三国演义 118 // lfdfd 119 // 55.95 120 // 121 // 122 // CS从入门到精通 123 // 涨红 124 // 58.3 125 // 126 // 127 // CS从入门到精通 128 // 盖茨 129 // 58.3 130 // 131 // 132 // CS从入门到精通 133 // 盖茨 134 // 58.3 135 // 136 // 137 // 138 // 139 // 140 // 三国演义 141 // lfdfd 142 // 55.95 143 // 144 // 145 // CS从入门到精通 146 // 涨红 147 // 58.3 148 // 149 // 150 // CS从入门到精通 151 // 盖茨 152 // 58.3 153 // 154 // 155 // CS从入门到精通 156 // 盖茨 157 // 58.3 158 // 159 // 160 //显示效果: 161 162 //删除属性为计算机的节点 163 //view plaincopy to clipboardprint? 164 //XmlDocument xmlDoc = new XmlDocument(); 165 //xmlDoc.Load(Server.MapPath("xml/BooksInfo.xml")); 166 //XmlNodeList xnl = xmlDoc.SelectSingleNode("bookstore").ChildNodes; 167 //foreach (XmlNode xn in xnl) 168 // { 169 // XmlElement xe = (XmlElement)xn; 170 171 // if (xe.GetAttribute("genre") == "计算机") 172 // { 173 // xe.RemoveAll();//删除属性=计算机的该节点的全部内容 174 // //xe.ParentNode.RemoveChild(xe); 175 // } 176 177 178 // } 179 //xmlDoc.Save(Server.MapPath("xml/BooksInfo.xml")); 180 //XmlDocument xmlDoc = new XmlDocument(); 181 //xmlDoc.Load(Server.MapPath("xml/BooksInfo.xml")); 182 //XmlNodeList xnl = xmlDoc.SelectSingleNode("bookstore").ChildNodes; 183 //foreach (XmlNode xn in xnl) 184 // { 185 // XmlElement xe = (XmlElement)xn; 186 // if (xe.GetAttribute("genre") == "计算机") 187 // { 188 // xe.RemoveAll();//删除属性=计算机的该节点的全部内容 189 // //xe.ParentNode.RemoveChild(xe); 190 // } 191 192 // } 193 //xmlDoc.Save(Server.MapPath("xml/BooksInfo.xml")); 194 //xe.ParentNode.RemoveChild(xe) 195 //寻找到xe节点的父节点,再删除他的子节点。那么此节点(包括节点标记)将不存在。显示中将没有空行 196 //xe.RemoveAll() 197 //移除节点内容,但是节点标签还在。显示中有空行 198 199 #endregion 200 201 #region 202 //读取xml节点内的数据内容 203 //XmlDocument doc = new XmlDocument(); 204 ////加载要读取的XML 205 //doc.Load("Order.xml"); 206 ////获得根节点 207 //XmlElement books = doc.DocumentElement; 208 ////获得子节点 返回节点的集合 209 //XmlNodeList xnl = books.ChildNodes; 210 //foreach (XmlNode item in xnl) 211 //{ 212 // Console.WriteLine(item.InnerText); 213 //} 214 //Console.ReadKey(); 215 216 217 ////读取带属性的XML文档 218 //XmlDocument doc = new XmlDocument(); 219 //doc.Load("Order.xml"); 220 //XmlNodeList xnl = doc.SelectNodes("/Order/Items/OrderItem"); 221 222 //foreach (XmlNode node in xnl) 223 //{ 224 // Console.WriteLine(node.Attributes["Name"].Value); 225 // Console.WriteLine(node.Attributes["Count"].Value); 226 //} 227 //Console.ReadKey(); 228 229 230 //改变属性的值 231 //XmlDocument doc = new XmlDocument(); 232 //doc.Load("Order.xml"); 233 //XmlNode xn = doc.SelectSingleNode("/Order/Items/OrderItem[@Name='190']"); 234 //xn.Attributes["Count"].Value = "200"; 235 //xn.Attributes["Name"].Value = "颜世伟"; 236 //doc.Save("Order.xml"); 237 //Console.WriteLine("保存成功"); 238 239 240 //删除相应的的范围 241 //XmlDocument doc = new XmlDocument(); 242 //doc.Load("Order.xml"); 243 //XmlNode xn = doc.SelectSingleNode("/Order/Items"); 244 //xn.RemoveAll(); 245 //doc.Save("Order.xml"); 246 //Console.WriteLine("删除成功"); 247 //Console.ReadKey(); 248 249 250 ////获得文档的根节点 251 //XmlDocument doc = new XmlDocument(); 252 //doc.Load("Books.xml"); 253 //XmlNode root = doc.DocumentElement; 254 //XmlNodeList nodeList = root.ChildNodes; 255 //foreach (XmlNode node in nodeList) 256 //{ 257 // Console.WriteLine(node.Name); 258 // //if(node.Name=="name") 259 // //{ 260 // // //找到name节点,进行操作 261 // //} 262 //} 263 264 265 ////比较综合点 266 //XmlDocument doc = new XmlDocument(); 267 //doc.Load("Order.xml"); 268 //XmlNode root = doc.DocumentElement; 269 //XmlNodeList nodeList = root.ChildNodes; 270 //foreach (XmlNode node in nodeList) 271 //{ 272 // Console.WriteLine(node.Name); 273 // if (node.Name == "Items") 274 // { 275 // //找到name节点,进行操作 276 // XmlNodeList xnl = doc.SelectNodes("/Order/Items/OrderItem"); 277 // foreach (XmlNode nd in xnl) 278 // { 279 // Console.WriteLine(nd.Attributes["Name"].Value); 280 // Console.WriteLine(nd.Attributes["Count"].Value); 281 // } 282 // } 283 //} 284 #endregion 读取XML文件 所需要的XML文件,放在运行程序的当前目录下 1 2 3 - 4 5 6 - 7 8 c#开发大全 9 10 110 11 12 看不懂 13 14 15 16 17 - 18 19 c#开发大全 20 21 110 22 23 看不懂 24 25 26 27 28 - 29 30 c#开发大全 31 32 110 33 34 看不懂 35 36 37 38 39 - 40 41 c#开发大全 42 43 110 44 45 看不懂 46 47 48 49 Books.xml(追加) 1 2 3 - 4 5 6 - 7 8 c#开发大全 9 10 110 11 12 看不懂 13 14 15 16 17 - 18 19 c#开发大全 20 21 110 22 23 看不懂 24 25 26 27 Books.xml(读取)  22.字符串大小写转换 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace class2 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 string[] names = { "abDC", "BYl" }; 13 Prot(names); 14 prot(names); 15 } 16 17 public static void Prot(string[] name) 18 { 19 for (int i = 0; i < name.Length; i++) 20 { 21 name[i] = name[i].ToUpper();//转大写 22 Console.WriteLine(name[i]); 23 } 24 // Console.ReadKey(); 25 } 26 27 public static void prot(string[] name) 28 { 29 for (int i = 0; i < name.Length; i++) 30 { 31 name[i] = name[i].ToLower(); 32 Console.WriteLine(name[i]); 33 } 34 Console.ReadKey(); 35 } 36 } 37 } 字符串大小写转换 23.单例模式简单的: 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace Csharp_特色_单体模式 11 { 12 public partial class Form1 : Form 13 { 14 public Form1() 15 { 16 InitializeComponent(); 17 } 18 19 private void button1_Click(object sender, EventArgs e) 20 { 21 Form2 f2 = Form2.instance; 22 23 f2.Show(); 24 } 25 } 26 } 单例模式(简单)-Form1 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace Csharp_特色_单体模式 11 { 12 public partial class Form2 : Form 13 { 14 private Form2() 15 { 16 InitializeComponent(); 17 } 18 //不要说C#太懒了,这叫智慧,嘿嘿 19 public static readonly Form2 instance = new Form2(); 20 } 21 22 //核心在这里 23 //public class Singleton 24 //{ 25 // private Singleton(){} 26 // public static readonly Singleton instance = new Singleton(); 27 //} 28 } 单例模式(简单)-Form2 相比之下复杂一点点的: 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 11 namespace _2_单例模式 12 { 13 public partial class Form1 : Form 14 { 15 public Form1() 16 { 17 InitializeComponent(); 18 } 19 20 private void Form1_Load(object sender, EventArgs e) 21 { 22 this.IsMdiContainer = true;//指定当前窗体为父窗体 23 this.WindowState = FormWindowState.Maximized; //窗体加载时,窗体为最大化 24 } 25 26 private void button1_Click(object sender, EventArgs e) 27 { 28 Form2 frm2 = Form2.GetSingle(); 29 frm2.MdiParent = this;//指定父窗体 30 31 frm2.Show(); 32 } 33 } 34 } 单例模式-From1 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 11 namespace _2_单例模式 12 { 13 public partial class Form2 : Form 14 { 15 //全局唯一的单例 16 public static Form2 FrmSingle=null; 17 18 public Form2() 19 { 20 InitializeComponent(); 21 } 22 23 public static Form2 GetSingle() 24 { 25 if (FrmSingle == null) 26 { 27 FrmSingle = new Form2(); 28 } 29 return FrmSingle; 30 } 31 } 32 //核心就是 这个 33 //public class Singleton 34 // { 35 // private static Singleton _instance = null; 36 // private Singleton() { } 37 // public static Singleton CreateInstance() 38 // { 39 // if (_instance == null) 40 // { 41 // _instance = new Singleton(); 42 // } 43 // return _instance; 44 // } 45 // } 46 } 单例模式-Form2  线程安全的单例模式 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace _2.线程安全的单例模式Singleton 11 { 12 public partial class Form1 : Form 13 { 14 public Form1() 15 { 16 InitializeComponent(); 17 } 18 19 private void button1_Click(object sender, EventArgs e) 20 { 21 Form2 f2 = Form2.CI(); 22 f2.Show(); 23 } 24 } 25 } 线程安全的单例模式-Form1 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace _2.线程安全的单例模式Singleton 11 { 12 public partial class Form2 : Form 13 { 14 private volatile static Form2 ins = null; 15 private static readonly object lockHelper = new object(); 16 private Form2() 17 { 18 InitializeComponent(); 19 } 20 21 public static Form2 CI() 22 { 23 if (ins == null) 24 { 25 lock (lockHelper) 26 { 27 if (ins == null) 28 ins = new Form2(); 29 } 30 } 31 return ins; 32 } 33 } 34 //第二种考虑了线程安全,不过有点烦,但绝对是正规写法,经典的一叉 35 //核心就是这个 36 //public class Singleton 37 //{ 38 // private volatile static Singleton _instance = null; 39 // private static readonly object lockHelper = new object(); 40 // private Singleton() { } 41 // public static Singleton CreateInstance() 42 // { 43 // if (_instance == null) 44 // { 45 // lock (lockHelper) 46 // { 47 // if (_instance == null) 48 // _instance = new Singleton(); 49 // } 50 // } 51 // return _instance; 52 // } 53 //} 54 } 线程安全的单例模式-Form2  24.文件、文件夹操作(path、File、Directory)   1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.IO; 10 11 namespace 文件操作 12 { 13 public partial class Form1 : Form 14 { 15 public Form1() 16 { 17 InitializeComponent(); 18 } 19 20 private void Form1_Load(object sender, EventArgs e) 21 { 22 //这是控制台程序,我就写在这里方便理解 23 ////注意:这样是没有在桌面创建任何文件的 24 //string path = @"C:\Users\Administrator\Desktop\new.txt";//我的桌面 目录 25 //Console.WriteLine(Path.GetDirectoryName(path));//得到 文件目录 26 //Console.WriteLine(Path.ChangeExtension(path, "jpg"));//等到文件目录,改变文件格式 27 //Console.ReadKey(); 28 } 29 30 private void btnCreate_Click(object sender, EventArgs e) 31 { 32 //using System.IO; 33 File.Create(@"C:\Users\Administrator\Desktop\new.txt"); 34 MessageBox.Show("创建成功"); 35 } 36 37 private void btnCopy_Click(object sender, EventArgs e) 38 { 39 File.Copy(@"C:\Users\Administrator\Desktop\new.txt", @"C:\Users\Administrator\Desktop\abc.txt", true);//加上true可以覆盖源文件,不加 不可覆盖 40 MessageBox.Show("拷贝成功"); 41 } 42 43 private void btnMove_Click(object sender, EventArgs e) 44 { 45 //也可以用这种方式重命名 46 File.Move(@"C:\Users\Administrator\Desktop\new.txt", @"C:\Users\Administrator\Desktop\new_old.txt"); 47 MessageBox.Show("移动成功"); 48 } 49 50 private void btnDelete_Click(object sender, EventArgs e) 51 { 52 File.Delete(@"C:\Users\Administrator\Desktop\new_old.txt"); 53 MessageBox.Show("删除成功"); 54 } 55 56 //下面的是读取和写入操作,首先要确定有目标文件就是需要使用的文件(在指定目录建个txt文件) 57 //我使用的是C:\Users\Administrator\Desktop\毕毕love丹丹.txt 58 //定义个全局变量 方便使用 59 public static string strTxt = @"C:\Users\Administrator\Desktop\毕毕love丹丹.txt"; 60 61 private void btnWriter_Click(object sender, EventArgs e) 62 { 63 //File类写入 64 //其实写入任何东西都是没用处的,都被下面的Write...给覆盖了 65 //想要看见自己输入的内容把WriteAllLines和WriteAllText两行注释掉就行了 66 string str = txtWR.Text; 67 byte[] buffer = Encoding.Default.GetBytes(str); 68 File.WriteAllBytes(strTxt, buffer); 69 70 File.WriteAllLines(strTxt, new string[] { "张三", "李四", "王五", "赵六" }); 71 72 File.WriteAllText(strTxt, "大家请叫我‘毕小帅’");//覆盖 73 74 File.AppendAllText(strTxt, "没有覆盖哟"); 75 76 MessageBox.Show("成功"); 77 } 78 79 private void btnReader_Click(object sender, EventArgs e) 80 { 81 txtWR.Text = ""; 82 //使用File类来读取数据 83 byte[] buffer = File.ReadAllBytes(strTxt); 84 string str = Encoding.UTF8.GetString(buffer, 0, buffer.Length); 85 txtWR.Text = str + '\n'; 86 87 //编码:把字符串以怎样形式存储为二进制 ASCII GBK GB2312 UTF-8 88 89 string[] str1 = File.ReadAllLines(strTxt, Encoding.Default); 90 for (int i = 0; i < str1.Length; i++) 91 { 92 txtWR.AppendText(str1[i]); 93 } 94 95 ////ReadAllLines和ReadAllText都是读取文件的所有行 96 ////不同的是 第一个读取所有行的集合,第二个是读取文件内的整个的 全部的数据 97 //string str2 = File.ReadAllText(strTxt, Encoding.Default); 98 //txtWR.AppendText(str2); 99 } 100 } 101 } 文件操作 25.超市管理系统(控制台应用程序) 先看一下输出结果:    需要的文件: 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 //我仿照一个培训老师写的教程、 7 namespace 超市管理系统 8 { 9 class Prm_main 10 { 11 static void Main(string[] args) 12 { 13 //为了方便理解,我把类名和方法名都写成了汉字,工作中不可以这样,建议写成简写英文 14 15 //无论做什么,首先把流程搞清楚 16 // 看要求: 17 // 四种商品--酱油、香蕉、手机、电脑 18 // 每种商品都在单独的货架上 19 // 每种商品都是有id、价格、名字 20 // 用户购买商品的优惠,如何打折。得出总价钱 21 //知道这些就够了,简单的管理系统就有思路了。(我感觉我比那位培训老师都讲的仔细,嘿嘿) 22 23 //我的思路: 24 //1、用户要买东西首先需要有商品 25 //2、每个商品要有id、价格、名字 26 //3、商品不可能在超市里是乱放的,要有分类 27 //4、然后就是用户购买了,按照买的个数*价格*打折 28 29 //思路很清晰了,开始写了 30 //1.建个商品的父类 封装商品的(id、价格、名字)---商品Father.cs 31 //2.写出四种商品的类 32 //3.商品确定了,把商品放在规定的货架上--超市.cs 33 //4.商品全部搞定后,接下来就是客户购买了,先写个打折的父类(因为好几种打折的方式,来重写父类) 34 //5.实现打折的方式---打折_满减.cs 35 //6.实现打折的方式---打折_折扣率.cs 36 //7.还需要个不打折的付款---不打折.cs 37 //8.准备工作全部结束了,最后一步就是用户购物了 38 //好了全部搞定 39 /*******************************************************/ 40 41 //接下来就是用户购物了 42 43 用户购物 sm = new 用户购物(); 44 //展示货物 45 sm.展示货物(); 46 //跟用户交互 47 sm.用户交互(); 48 Console.ReadKey(); 49 } 50 } 51 } Prm_main 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 超市管理系统 7 { 8 class 商品Father 9 { 10 public double Price 11 { 12 get; 13 set; 14 } 15 16 public string Name 17 { 18 get; 19 set; 20 } 21 22 public string ID 23 { 24 get; 25 set; 26 } 27 28 public 商品Father(string id, double price, string Name) 29 { 30 this.ID = id; 31 this.Price = price; 32 this.Name = Name; 33 } 34 } 35 } 商品Father 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 超市管理系统 7 { 8 class 电脑 : 商品Father 9 { 10 public 电脑(string id, double price, string Name) 11 : base(id, price, Name) 12 { 13 14 } 15 } 16 } 电脑 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 超市管理系统 7 { 8 class 手机 : 商品Father 9 { 10 public 手机(string id, double price, string Name) 11 : base(id, price, Name) 12 { 13 14 } 15 } 16 } 手机 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 超市管理系统 7 { 8 class 香蕉 : 商品Father 9 { 10 public 香蕉(string id, double price, string Name) 11 : base(id, price, Name) 12 { 13 14 } 15 } 16 } 香蕉 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 超市管理系统 7 { 8 class 酱油 : 商品Father 9 { 10 public 酱油(string id, double price, string Name) 11 : base(id, price, Name) 12 { 13 14 } 15 } 16 } 酱油 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 超市管理系统 7 { 8 class 超市 9 { 10 List> list = new List>(); 11 12 /// 13 ///向用户展示货物 14 /// 15 public void 展示货物() 16 { 17 foreach (var item in list) 18 { 19 Console.WriteLine("我们超市有:" + item[0].Name + "," + "\t" + "有" + item.Count + "个," + "\t" + "每个" + item[0].Price + "元"); 20 } 21 } 22 //list[0]存储Acer电脑 23 //list[1]存储三星手机 24 //list[2]存储酱油 25 //list[3]存储香蕉 26 /// 27 /// 在创建仓库对象的时候 像仓库中添加货架 28 /// 29 public 超市() 30 { 31 list.Add(new List<商品Father>()); 32 list.Add(new List<商品Father>()); 33 list.Add(new List<商品Father>()); 34 list.Add(new List<商品Father>()); 35 } 36 /// 37 /// 进货 38 /// 39 /// 货物的类型 40 /// 货物的数量 41 public void JinPros(string strType, int count) 42 { 43 for (int i = 0; i < count; i++) 44 { 45 switch (strType) 46 { 47 case "手机": list[0].Add(new 香蕉(Guid.NewGuid().ToString(), 1000, "手机")); 48 break; 49 case "电脑": list[1].Add(new 手机(Guid.NewGuid().ToString(), 2000, "电脑")); 50 break; 51 case "酱油": list[2].Add(new 酱油(Guid.NewGuid().ToString(), 10, "酱油")); 52 break; 53 case "香蕉": list[3].Add(new 电脑(Guid.NewGuid().ToString(), 50, "香蕉")); 54 break; 55 } 56 } 57 } 58 /// 59 /// 从仓库中提取货物 60 /// 61 /// 62 /// 63 /// 64 public 商品Father[] QuPros(string strType, int count) 65 { 66 商品Father[] pros = new 商品Father[count]; 67 for (int i = 0; i < pros.Length; i++) 68 { 69 switch (strType) 70 { 71 case "手机": 72 pros[i] = list[0][0]; 73 list[0].RemoveAt(0); 74 break; 75 case "电脑": pros[i] = list[1][0]; 76 list[1].RemoveAt(0); 77 break; 78 case "酱油": pros[i] = list[2][0]; 79 list[2].RemoveAt(0); 80 break; 81 case "香蕉": pros[i] = list[3][0]; 82 list[3].RemoveAt(0); 83 break; 84 } 85 } 86 return pros; 87 } 88 } 89 } 超市 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 超市管理系统 7 { 8 /// 9 /// 打折的父类 抽象类---需要重写父类 10 /// 11 abstract class 打折Father 12 { 13 /// 14 /// 计算打折后应付多少钱 15 /// 16 /// 打折前应付的价钱 17 /// 打折后应付的钱 18 public abstract double 付钱(double realMoney); 19 } 20 } 打折Father 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 超市管理系统 7 { 8 /// 9 /// 买M元 送N元 10 /// 11 class 打折_满减 : 打折Father 12 { 13 //买500送100 14 public double M 15 { 16 get; 17 set; 18 } 19 20 public double N 21 { 22 get; 23 set; 24 } 25 26 public 打折_满减(double m, double n) 27 { 28 this.M = m; 29 this.N = n; 30 } 31 public override double 付钱(double realMoney) 32 { 33 //600 -100 34 //1000-200 35 //1200 36 if (realMoney >= this.M) 37 { 38 return realMoney - (int)(realMoney / this.M) * this.N; 39 } 40 else 41 { 42 return realMoney; 43 } 44 } 45 } 46 } 打折_满减 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 超市管理系统 7 { 8 /// 9 /// 按折扣率打折 10 /// 11 class 打折_折扣率 : 打折Father 12 { 13 /// 14 /// 折扣率 15 /// 16 public double Rate 17 { 18 get; 19 set; 20 } 21 22 public 打折_折扣率(double rate) 23 { 24 this.Rate = rate; 25 } 26 public override double 付钱(double realMoney) 27 { 28 return realMoney * this.Rate; 29 } 30 31 } 32 } 打折_折扣率 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 超市管理系统 7 { 8 /// 9 /// 不打折 该多少钱就多少钱 10 /// 11 class 不打折 : 打折Father 12 { 13 public override double 付钱(double realMoney) 14 { 15 return realMoney; 16 } 17 } 18 } 不打折 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 超市管理系统 7 { 8 class 用户购物 9 { 10 //创建仓库对象 11 超市 ck = new 超市(); 12 /// 13 /// 创建超市对象的时候,给仓库的货架上导入货物 14 /// 15 public 用户购物() 16 { 17 ck.JinPros("酱油", 1000); 18 ck.JinPros("香蕉", 1000); 19 ck.JinPros("手机", 1000); 20 ck.JinPros("电脑", 1000); 21 } 22 23 /// 24 /// 跟用户交互的过程 25 /// 26 public void 用户交互() 27 { 28 Console.WriteLine("欢迎观临,请问您需要些什么?"); 29 Console.WriteLine("我们有 酱油、香蕉、手机、电脑"); 30 string strType = Console.ReadLine(); 31 Console.WriteLine("您需要多少?"); 32 int count = Convert.ToInt32(Console.ReadLine()); 33 //去仓库取货物 34 商品Father[] pros = ck.QuPros(strType, count); 35 //下面该计算价钱了 36 double realMoney = 总价钱(pros); 37 Console.WriteLine("您总共应付{0}元", realMoney); 38 Console.WriteLine("请选择您的打折方式 1--不打折 2--打九折 3--打85 折 4--买300送50 5--买500送100"); 39 string input = Console.ReadLine(); 40 //通过简单工厂的设计模式根据用户的舒服获得一个打折对象 41 打折Father cal = 打折_工厂(input); 42 double totalMoney = cal.付钱(realMoney); 43 Console.WriteLine("打完折后,您应付{0}元", totalMoney); 44 Console.WriteLine("以下是您的购物信息"); 45 foreach (var item in pros) 46 { 47 Console.WriteLine("货物名称:" + item.Name + "," + "\t" + "货物单价:" + item.Price + "," + "\t" + "货物编号:" + item.ID); 48 } 49 } 50 51 /// 52 /// 根据用户的选择打折方式返回一个打折对象 53 /// 54 /// 用户的选择 55 /// 返回的父类对象 但是里面装的是子类对象 56 public 打折Father 打折_工厂(string input) 57 { 58 打折Father cal = null; 59 switch (input) 60 { 61 case "1": cal = new 不打折(); 62 break; 63 case "2": cal = new 打折_折扣率(0.9); 64 break; 65 case "3": cal = new 打折_折扣率(0.85); 66 break; 67 case "4": cal = new 打折_满减(300, 50); 68 break; 69 case "5": cal = new 打折_满减(500, 100); 70 break; 71 } 72 return cal; 73 } 74 75 /// 76 /// 根据用户买的货物计算总价钱 77 /// 78 /// 79 /// 80 public double 总价钱(商品Father[] pros) 81 { 82 double Money = 0; 83 //realMoney = pros[0].Price * pros.Length; 84 85 for (int i = 0; i < pros.Length; i++) 86 { 87 Money += pros[i].Price; 88 89 // realMoney = pros[i] * pros.Length; 90 } 91 return Money; 92 } 93 94 public void 展示货物() 95 { 96 ck.展示货物(); 97 } 98 } 99 } 用户购物  26.小游戏(石头剪刀布) 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace 石头剪刀布 11 { 12 public partial class Form1 : Form 13 { 14 public Form1() 15 { 16 InitializeComponent(); 17 // button1.Click += (s, e) => { shitou(); }; 18 button2.Click += (s, e) => { jiandao(); };//别忘了,事件只能出现在+=、-=左边 19 button3.Click += (d, e) => { bu(); }; 20 } 21 //其实button1.Click += (s, e) => { shitou(); };和下面的这写法是一样的 22 private void button1_Click(object sender, EventArgs e) 23 { 24 shitou(); 25 } 26 //石头 27 public void shitou() 28 { 29 label4.Text = button1.Text; 30 label3.Text = bj(toint(), pc()); 31 } 32 //剪刀 33 public void jiandao() 34 { 35 label4.Text = button2.Text; 36 label3.Text = bj(toint(), pc()); 37 } 38 39 //布 40 public void bu() 41 { 42 label4.Text = button3.Text; 43 label3.Text = bj(toint(), pc()); 44 } 45 46 /// 47 /// 电脑随机显示,还有相应的值替代它 48 /// 49 /// 50 public int pc() 51 { 52 Random ran = new Random(); 53 int vpc = ran.Next(1, 4); 54 // string strpc;//这样写会浪费资源,在堆中要开辟很多空间 55 //这样,每次调用pc方法时strpc都是初始为空的。 56 //重要的是string类型是引用类型。 57 string strpc = string.Empty;//初始为空 58 switch (vpc) 59 { 60 case 1: 61 strpc = "石头"; 62 break; 63 case 2: 64 strpc = "剪刀"; 65 break; 66 case 3: 67 strpc = "布"; 68 break; 69 default: 70 throw new Exception("未知错误"); 71 } 72 label5.Text = strpc; 73 return vpc; 74 } 75 /// 76 /// 把剪刀石头布用数字表示 77 /// 78 /// 79 public int toint() 80 { 81 int n; 82 switch (label4.Text) 83 { 84 case "石头": 85 n = 1; 86 break; 87 case "剪刀": 88 n = 2; 89 break; 90 case "布": 91 n = 3; 92 break; 93 default: 94 throw new Exception("出错了"); 95 } 96 return n; 97 } 98 //比较 99 public string bj(int user, int pc) 100 { 101 int tmp = user - pc; 102 string bj = string.Empty; 103 if (tmp == 1 || tmp == -2) 104 { 105 bj = "你输了"; 106 } 107 else if (tmp == 0) 108 { 109 bj = "平局"; 110 } 111 else 112 { 113 bj = "你赢了"; 114 } 115 return bj; 116 } 117 } 118 } 石头剪刀布 27.英汉词典       1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.IO; 10 11 namespace 英汉词典 12 { 13 public partial class Form1 : Form 14 { 15 public Form1() 16 { 17 InitializeComponent(); 18 } 19 //第一步,我是先把英汉词典.txt数据源的内容储存起来,方便使用 20 //首先用一个泛型字典存储英汉词典.TXT里的内容 21 //反省字典是(Dictionary<,>)这样的,里面是键值对 22 //每行数据必须要有一个唯一的键不可以重复,尾随的数据可以重复 23 24 //new 一个泛型字典 25 Dictionary dic = new Dictionary(); 26 //new 一个泛型list 27 List list = new List(); 28 29 //读取英汉词典.TXT文件,这就要知道它的路径了 30 //我个人建议是把英汉词典.txt文件放在相对路径下,因为打包之后方便使用 31 32 //绝对路径下读取文件 33 //加上@,便于后面的符号转换 34 //Encoding.Default是选择当前系统默认的字体编码 35 //string[] strarr = File.ReadAllLines(@"C:\Users\Administrator\Desktop\英汉词典.txt",Encoding.Default); 36 //相对路径下读取文件 37 //我选择的是相对路径 38 string[] strarr = File.ReadAllLines(@"英汉词典.txt", Encoding.Default); 39 40 //窗体加载时自动运行 41 private void Form1_Load(object sender, EventArgs e) 42 { 43 Stime(); 44 label2.Text = "您查询的结果:"; 45 label1.Text = ""; 46 47 //遍历每一个行,每行都是两个元素,英文和中文 48 for (int i = 0; i < strarr.Length; i++) 49 { 50 //使用split方法移除单个空字符 51 string[] strarr1 = strarr[i].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); 52 //避免重复添加 53 //contains是包含的意思 54 if (!dic.Keys.Contains(strarr1[0])) 55 { 56 //其实这样也就可以了,但是作为一个严谨的程序员,我还是给这一段加个判断 57 //将数组里的英文和中文填到泛型字典里 58 dic.Add(strarr1[0], strarr1[1]); 59 //将英文添加到泛型list里 60 //这样list内的数据都是dic内的键值 61 list.Add(strarr1[0]); 62 } 63 } 64 //为了让程序运行起来想过能高大上一些,就填了这一下的代码 65 AutoCompleteStringCollection strings = new AutoCompleteStringCollection(); 66 // 所有list泛型的英文单词转换成数组 添加到 strings里 67 strings.AddRange(list.ToArray()); 68 textBox1.AutoCompleteCustomSource = strings; //然后赋给文本框的 自动补全 所需的资源 属性 69 textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource; //指定 CustomSource 为数据源 70 textBox1.AutoCompleteMode = AutoCompleteMode.Suggest; //启动自动补全模式 71 } 72 //以上读取英汉字典.txt的操作,已经搞定 73 //接下来就开始实现了 74 75 76 private void textBox1_TextChanged(object sender, EventArgs e) 77 { 78 //文本框内若是没有数据,就不显示label1 79 if (textBox1.Text == "") 80 { 81 label1.Text = ""; 82 } 83 84 //开始查找,文本框内与泛型字典键相同就把数据显示出来 85 //trim()是把空白的字符去掉 86 if (dic.Keys.Contains(textBox1.Text.Trim())) 87 { 88 //用键值找到数据,显示在textBox2中 89 textBox2.Text = dic[textBox1.Text.Trim()]; 90 91 //因为搜索到了结果,所以在线搜索不显示 92 linkLabel1.Visible = false; 93 label1.Text = ""; 94 label2.Text = "您查询的结果:"; 95 timer.Stop(); 96 Ltime = 0; 97 } 98 else if (textBox1.Text == "") 99 { 100 textBox2.Text = "请输入要查询单词"; 101 linkLabel1.Visible = false; 102 timer.Stop(); 103 Ltime = 0; 104 } 105 else 106 { 107 textBox2.Text = "正在搜索"; 108 //计时开始 109 timer.Start(); 110 label2.Text = "您查询的结果:"; 111 112 } 113 114 } 115 //以上显示部分也基本搞定 116 //对了,把在线查询实现出来 117 private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) 118 { 119 //因为我这有360浏览器,经常被终结,我就添加了try catch 120 try 121 { 122 System.Diagnostics.Process.Start("explorer.exe", "http://www.youdao.com/w/" + textBox1.Text.Trim()); 123 } 124 catch 125 { 126 MessageBox.Show("通过其他方式已将查询关闭"); 127 } 128 } 129 130 private void label2_Click(object sender, EventArgs e) 131 { 132 133 } 134 135 //为了让程序能高大上,我设置在20秒内若是没有查到结果就显示在线查找 136 //也可以按键盘回车键直接进行查询结果 137 138 //定义个查找所用时间 139 public int Ltime = 0; 140 //定义个计时器 141 public Timer timer; 142 143 public void Stime() 144 { 145 timer = new Timer(); 146 //一秒间隔 147 timer.Interval = 1000; 148 timer.Tick += (s, e) => 149 { 150 Ltime++; 151 label1.Text = Ltime.ToString();//显示查询几秒 152 153 if (Ltime >= 20) 154 { 155 label1.Text = "收索时间大于20秒已超时"; 156 label2.Text = "对不起,系统不包含您输入的单词"; 157 textBox2.Text = ""; 158 //显示网站链接 159 linkLabel1.Visible = true; 160 linkLabel1.Text = "对不起请尝试使用(有道youdao)在线翻译:" + "\r\n\n\t" + textBox1.Text.Trim(); 161 timer.Stop(); 162 Ltime = 0; 163 164 //使linkWebSearch控件显示的网址在textbox控件上面 165 linkLabel1.BringToFront(); 166 } 167 else//那就是20秒内显示出结果了 168 { 169 linkLabel1.Visible = false; 170 label1.Text = Ltime.ToString(); 171 } 172 }; 173 } 174 175 /// 176 /// 在textBox1文本框内点击回车的事件 177 /// 178 /// 179 /// 180 private void textBox1_KeyDown(object sender, KeyEventArgs e) 181 { 182 //判断是否点击了回车按钮 183 if (e.KeyCode == Keys.Enter) 184 { 185 //我这是把上面的复制下来了,直接查出结果 186 if (dic.Keys.Contains(textBox1.Text.Trim())) 187 { 188 textBox2.Text = dic[textBox1.Text.Trim()]; 189 linkLabel1.Visible = false; 190 Ltime = 0; 191 label2.Text = "您查询的结果:"; 192 } 193 else 194 { 195 label2.Text = "对不起,系统不包含您输入的单词"; 196 label1.Text = ""; 197 textBox2.Text = ""; 198 199 linkLabel1.Visible = true; 200 201 linkLabel1.Text = "对不起请尝试使用(有道youdao)在线翻译:" + "\r\n\n\t" + textBox1.Text.Trim(); 202 203 timer.Stop(); 204 Ltime = 0; 205 linkLabel1.BringToFront(); 206 } 207 208 } 209 } 210 } 211 } 英汉词典 28.音乐播放器(winform)--------------这个软件会做了,才能代表C# winform开发 “基础”已经学会了 首先先上图片,了解一下软件的构造             ListBox控件属性要修改的地方   那些控件都好说,只要是播放控件vs上面没有: 这时,我们就要手动添加,相当于调用了Windows的Dll文件了 1、在vs工具箱里右键点击“组件”(这是我的习惯,点击别的也可以)  2、找到“选择项”,点击。电脑一般配置的要等待一会,有点慢      3、看图           Form1主要部分: Form2主要部分:                 上代码了,接下来就自己对号入座吧 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.IO; 10 11 namespace 音乐播放器 12 { 13 public partial class Frm_main : Form 14 { 15 public Frm_main() 16 { 17 InitializeComponent(); 18 } 19 20 #region 加载窗体 21 private void Frm_main_Load(object sender, EventArgs e) 22 { 23 trackBar1.SendToBack();//置于底层 24 label1.Image = Image.FromFile("放音.jpg"); 25 label6.Text = "25"; 26 27 //trackBar1.BringToFront();//置于顶层 28 //trackBar2.BringToFront();//置于顶层 29 //程序加载时,取消播放器的自动播放 30 musicPlayer.settings.autoStart = false; 31 label1.Image = Image.FromFile("放音.jpg"); 32 //AutoEllipsis 属性可以改变 label控件的大小 33 toolTip1.SetToolTip(this.btn_PlayMode, "播放模式");//当鼠标放在控件上会显示 这是toolTip的功能 34 toolTip1.SetToolTip(this.label1, "声音开关");//当鼠标放在控件上会显示 这是toolTip的功能 35 } 36 #endregion 37 38 #region 窗体移动 39 bool formMoveFlag = false;//窗体是否移动标志位位 40 Point formPoint;//记录窗体的位置 41 /// 42 /// 鼠标在窗体上按下 43 /// 44 /// 45 /// 46 private void MainForm_MouseDown(object sender, MouseEventArgs e) 47 { 48 formPoint = new Point(); 49 // int xOffset; 50 // int yOffset; 51 if (e.Button == MouseButtons.Left)//如果鼠标左键按下 52 { 53 formPoint = new Point(-e.X, -e.Y); //得到变量的值 54 formMoveFlag = true;//开始移动 55 } 56 } 57 /// 58 /// 鼠标在窗体上移动 59 /// 60 /// 61 /// 62 private void MainForm_MouseMove(object sender, MouseEventArgs e) 63 { 64 if (formMoveFlag == true)//如果窗体正在移动 65 { 66 Point mousePos = Control.MousePosition;//获取鼠标当前位置 67 mousePos.Offset(formPoint.X, formPoint.Y);//设置鼠标移动后的位置 68 Location = mousePos;//将鼠标的坐标 传递给窗体 69 } 70 } 71 /// 72 /// 鼠标在窗体上释放 73 /// 74 /// 75 /// 76 private void MainForm_MouseUp(object sender, MouseEventArgs e) 77 { 78 if (e.Button == MouseButtons.Left)//按下的是鼠标左键 79 { 80 formMoveFlag = false;//停止移动 81 } 82 } 83 #endregion 84 85 #region 1、主要的播放功能 86 87 //存储音乐文件的全路径 88 List listpath = new List(); 89 90 #region 右键 打开添加音乐文件 91 int addi = 0; 92 private void btn_Open_Click(object sender, EventArgs e) 93 { 94 OpenFileDialog ofd = new OpenFileDialog(); 95 ofd.InitialDirectory = @"音乐加歌词"; 96 ofd.Filter = "MP3文件|*.mp3|MP4文件|*.mp4|音乐文件|*.wav|所有文件|*.*"; 97 ofd.Title = "请选择音乐文件"; 98 ofd.Multiselect = true;//允许多选 99 ofd.ShowDialog(); 100 101 //获得文本框中选择文件的全路径 102 string[] path = ofd.FileNames; 103 for (int i = 0; i < path.Length; i++) 104 { 105 //将音乐文件的全路径加载到泛型集合中 106 addi++; 107 listpath.Add(path[i]); 108 //将音乐文件的文件名存储到ListBox中 109 lst_Playerfile.Items.Add(addi + "、" + Path.GetFileName(path[i])); 110 } 111 } 112 #endregion 113 #region 右键删除 114 private void 删除ToolStripMenuItem_Click(object sender, EventArgs e) 115 { 116 //删除的是列表中的选择项 117 //还要记住list集合也要删 118 119 //获取要删除的歌曲的数量 120 int count = lst_Playerfile.SelectedItems.Count; 121 for (int i = 0; i < count; i++) 122 { 123 //先删除集合 124 listpath.RemoveAt(lst_Playerfile.SelectedIndex); 125 //再删除表 126 lst_Playerfile.Items.RemoveAt(lst_Playerfile.SelectedIndex); 127 } 128 } 129 #endregion 130 #region 右键刷新 131 private void 刷新ToolStripMenuItem_Click(object sender, EventArgs e) 132 { 133 int add = 0; 134 List lst = new List(); 135 136 for (int i = 0; i < lst_Playerfile.Items.Count; i++) 137 { 138 add++; 139 //lst_Playerfile.Items.Add(add + "、" + lst_Playerfile.Items[i]); 140 string[] ss = lst_Playerfile.Items[i].ToString().Split('、'); 141 lst.Add(add + "、" + ss[1]); 142 } 143 lst_Playerfile.Items.Clear(); 144 foreach (var item in lst) 145 { 146 lst_Playerfile.Items.Add(item); 147 } 148 addi = add;//使刷新后的总行数等于addi,添加文件时可以正确排序 149 } 150 #endregion 151 152 #region ListBox的双击事件--选择播放的音乐 153 private void lst_Playerfile_DoubleClick(object sender, EventArgs e) 154 { 155 //timer3.Enabled = false; 156 //timer4.Enabled = false; 157 try 158 { 159 //索引找到点击播放的音乐是第几个 160 musicPlayer.URL = listpath[lst_Playerfile.SelectedIndex]; 161 musicPlayer.Ctlcontrols.play(); 162 IsExistLrc(listpath[lst_Playerfile.SelectedIndex]); 163 btn_Player.Text = "暂停"; 164 index_dq = lst_Playerfile.SelectedIndex; 165 } 166 catch { } 167 } 168 #endregion 169 170 #region 播放按钮 171 bool bl = true; 172 private void btn_Player_Click(object sender, EventArgs e) 173 { 174 try 175 { 176 if (bl) 177 { 178 musicPlayer.URL = listpath[lst_Playerfile.SelectedIndex]; 179 bl = false; 180 } 181 182 if (lst_Playerfile.SelectedIndex < 0) 183 { 184 MessageBox.Show("请选择播放的音乐文件"); 185 return; 186 } 187 if (btn_Player.Text == "播放") 188 { 189 //索引找到点击播放的音乐是第几个 190 //musicPlayer.URL = listpath[lst_Playerfile.SelectedIndex]; 191 musicPlayer.Ctlcontrols.play(); 192 //IsExistLrc(listpath[lst_Playerfile.SelectedIndex]); 193 IsExistLrc(musicPlayer.URL); 194 for (int i = 0; i < listpath.Count; i++) 195 { 196 if (musicPlayer.URL == listpath[i]) 197 { 198 index_dq = i; 199 } 200 } 201 //index_dq = lst_Playerfile.SelectedIndex; 202 //timer2.Enabled = true; 203 204 btn_Player.Text = "暂停"; 205 } 206 else if (btn_Player.Text == "暂停") 207 { 208 musicPlayer.Ctlcontrols.pause(); 209 btn_Player.Text = "播放"; 210 } 211 } 212 catch { } 213 } 214 #endregion 215 #region 终止播放 不是停止 216 private void btn_Stop_Click(object sender, EventArgs e) 217 { 218 musicPlayer.Ctlcontrols.stop(); 219 } 220 #endregion 221 #region 上一曲按钮 222 private void btn_Shang_Click(object sender, EventArgs e) 223 { 224 //try 225 // { 226 int index = lst_Playerfile.SelectedIndex; 227 lst_Playerfile.SelectedIndices.Clear(); 228 index--; 229 if (index < -1) 230 { 231 MessageBox.Show("选择播放文件"); 232 return; 233 } 234 else if (index < 0) 235 { 236 index = listpath.Count - 1; 237 musicPlayer.URL = listpath[index]; 238 } 239 musicPlayer.URL = listpath[index]; 240 lst_Playerfile.SelectedIndex = index; 241 musicPlayer.Ctlcontrols.play(); 242 IsExistLrc(listpath[lst_Playerfile.SelectedIndex]); 243 // } 244 // catch { MessageBox.Show("选择播放文件"); } 245 } 246 #endregion 247 #region 下一曲 248 private void btn_Xia_Click(object sender, EventArgs e) 249 { 250 //try 251 //{ 252 int index = lst_Playerfile.SelectedIndex; 253 lst_Playerfile.SelectedIndices.Clear(); 254 255 if (index < 0) 256 { 257 MessageBox.Show("选择播放文件"); 258 return; 259 } 260 index++; 261 if (index > listpath.Count - 1) 262 { 263 index = 0; 264 musicPlayer.URL = listpath[index]; 265 } 266 musicPlayer.URL = listpath[index]; 267 lst_Playerfile.SelectedIndex = index; 268 musicPlayer.Ctlcontrols.play(); 269 IsExistLrc(listpath[lst_Playerfile.SelectedIndex]); 270 //} 271 //catch { MessageBox.Show("选择播放文件"); } 272 } 273 #endregion 274 #region 声音开关按键 275 private void label1_Click(object sender, EventArgs e) 276 { 277 //将label的Tag属性设置为1 278 if (label1.Tag.ToString() == "1") 279 { 280 //目的让它静音 281 musicPlayer.settings.mute = true; 282 label1.Image = Image.FromFile("静音.jpg"); 283 label1.Tag = "2"; 284 label6.Text = "0";//显示音量大小 285 } 286 else if (label1.Tag.ToString() == "2") 287 { 288 //放音 289 musicPlayer.settings.mute = false; 290 label1.Image = Image.FromFile("放音.jpg"); 291 label1.Tag = "1"; 292 label6.Text = voice.ToString(); 293 } 294 } 295 #endregion 296 297 #region 模拟进度条--设置声音 298 int voice = 25; 299 //设置声音大小 300 public void setVoice(int voice) 301 { 302 if (voice >= 0 && voice <= 100) 303 { 304 musicPlayer.settings.volume = voice; 305 label6.Text = voice.ToString(); 306 } 307 } 308 private void panel5_MouseDown(object sender, MouseEventArgs e) 309 { 310 if (e.Location.X >= 0 && e.Location.X <= 100) 311 { 312 voice = e.Location.X; 313 panel5.Size = new Size(e.Location.X, 3); 314 setVoice(voice); 315 } 316 } 317 private void panel4_MouseDown(object sender, MouseEventArgs e) 318 { 319 if (e.Location.X >= 0 && e.Location.X <= 100) 320 { 321 voice = e.Location.X; 322 setVoice(voice); 323 panel5.Size = new Size(e.Location.X, 3); 324 } 325 } 326 #endregion 327 328 #region 计时器timer1--显示音乐时间、trackBar1显示播放进度 329 private void timer1_Tick(object sender, EventArgs e) 330 { 331 //如果播放器在播放中执行 332 if (musicPlayer.playState == WMPLib.WMPPlayState.wmppsPlaying) 333 { 334 label3.Text = musicPlayer.currentMedia.duration.ToString() + 335 "\r\n" + musicPlayer.currentMedia.durationString + 336 "\r\n" + musicPlayer.Ctlcontrols.currentPosition.ToString() + 337 "\r\n" + musicPlayer.Ctlcontrols.currentPositionString; 338 339 string[] sss1 = (musicPlayer.currentMedia.duration.ToString()).Split('.'); 340 string[] sss2 = (musicPlayer.Ctlcontrols.currentPosition.ToString()).Split('.'); 341 342 trackBar1.Maximum = int.Parse(sss1[0]); 343 trackBar1.Value = int.Parse(sss2[0]); 344 345 //if (Convert.ToInt32(label1.Tag) != 1)//当点击音乐开关时,label1.Tag=2; 346 //{ 347 // label6.Text = "0";//显示音量大小 348 //} 349 350 if (loob) 351 { 352 //自动下一曲 353 double d1 = double.Parse(musicPlayer.currentMedia.duration.ToString()); 354 355 double d2 = double.Parse(musicPlayer.Ctlcontrols.currentPosition.ToString()) + 1; 356 if (d1 <= d2) 357 { 358 //获得当前选中项的索引 359 int index = lst_Playerfile.SelectedIndex; 360 361 //清空所有选中项的索引 362 lst_Playerfile.SelectedIndices.Clear(); 363 index_dq++; 364 if (index_dq == lst_Playerfile.Items.Count) 365 { 366 index_dq = 0; 367 } 368 //将改变后的索引重新的赋值给当前选中项的索引 369 //if (index != index_dq) 370 //{ 371 // lst_Playerfile.SelectedIndex = index; 372 //} 373 //else 374 //{ 375 lst_Playerfile.SelectedIndex = index_dq; 376 //} 377 musicPlayer.URL = listpath[index_dq]; 378 musicPlayer.Ctlcontrols.play(); 379 IsExistLrc(listpath[index_dq]); 380 } 381 } 382 } 383 } 384 #endregion 385 386 #endregion 387 388 #region 2、歌词部分 389 390 //存储时间 391 List listTime = new List(); 392 //存储歌词 393 List listLrcText = new List(); 394 395 #region 添加歌词部分 396 //判断是否有歌词文件 397 void IsExistLrc(string songPath) 398 { 399 //清空两个集合的内容 400 listTime.Clear(); 401 listLrcText.Clear(); 402 403 songPath += ".lrc"; 404 if (File.Exists(songPath)) 405 { 406 //读取歌词文件 407 string[] lrcText = File.ReadAllLines(songPath, Encoding.Default); 408 //格式化歌词 409 FormatLrc(lrcText); 410 } 411 else//不存在歌词 412 { 413 label4.Text = "---------歌词未找到---------"; 414 415 Form2.StrLrc = label4.Text; 416 } 417 } 418 419 /// 420 /// 添加歌词 421 /// 422 /// 423 void FormatLrc(string[] lrcText) 424 { 425 for (int i = 0; i < lrcText.Length; i++) 426 { 427 //lrc格式歌词是这样的:[00:15.57]当我和世界不一样 428 string[] lrcTemp = lrcText[i].Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries); 429 //00:15.57 lrcTemp[0] 430 //当我和世界不一样 lrcTemp[1] 431 string[] lrcNewTemp = lrcTemp[0].Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries); 432 //00 lrcNewTemp[0] 433 //15.57 lrcNewTemp[1] 434 double time = double.Parse(lrcNewTemp[0]) * 60 + double.Parse(lrcNewTemp[1]); 435 //把截取出来的时间加到泛型集合中 436 listTime.Add(time); 437 //把这个时间所对应的歌词存储到泛型集合中 438 try 439 { 440 listLrcText.Add(lrcTemp[1]); 441 } 442 catch { listLrcText.Add(lrcTemp[0]); } 443 } 444 } 445 #endregion 446 #region 计时器timer2---播放歌词 447 private void timer2_Tick(object sender, EventArgs e) 448 { 449 for (int i = 0; i < listTime.Count; i++)//listTime listLrcText都行 450 { 451 try 452 { 453 if (musicPlayer.Ctlcontrols.currentPosition >= listTime[i] && musicPlayer.Ctlcontrols.currentPosition < listTime[i + 1]) 454 { 455 label4.Text = listLrcText[i]; 456 Form2.StrLrc = listLrcText[i]; 457 } 458 } 459 catch { label4.Text = listLrcText[i]; } 460 } 461 } 462 #endregion 463 464 #endregion 465 466 #region 3、播放模式 467 468 #region 之前写的按钮点击事件 音量加减、快进快退、快退不好使,查了资料还是不行,不知道是我写错了还是mp3格式不支持 469 //音量加 470 //private void btn_YL_jia_Click(object sender, EventArgs e) 471 //{ 472 // musicPlayer.settings.volume += 5; 473 // // MessageBox.Show(musicPlayer.settings.volume.ToString()); 474 //} 475 //int t1 = 1; 476 //音量减 477 //private void btn_YL_jian_Click(object sender, EventArgs e) 478 //{ 479 // musicPlayer.settings.volume -= 5; 480 //} 481 //快进 482 //private void btn_kj_Click(object sender, EventArgs e) 483 //{ 484 // if (t1 == 1) 485 // { 486 // musicPlayer.Ctlcontrols.fastForward(); 487 // t1 = 2; 488 // } 489 // else 490 // { 491 // musicPlayer.Ctlcontrols.play(); 492 // t1 = 1; 493 // } 494 //} 495 //int t2 = 1; 496 //快退 497 //private void btn_kt_Click(object sender, EventArgs e) 498 //{ 499 // if (t2 == 1) 500 // { 501 // musicPlayer.Ctlcontrols.fastReverse(); 502 // t2 = 2; 503 // } 504 // else 505 // { 506 // musicPlayer.Ctlcontrols.play(); 507 // t2 = 1; 508 // } 509 //} 510 #endregion 511 512 #region 计时器timer3--关于播放模式(单曲循环) 513 private void timer3_Tick(object sender, EventArgs e) 514 { 515 if (musicPlayer.playState == WMPLib.WMPPlayState.wmppsPlaying) 516 { 517 double d1 = double.Parse(musicPlayer.currentMedia.duration.ToString()); 518 double d2 = double.Parse(musicPlayer.Ctlcontrols.currentPosition.ToString()) + 1; 519 if (d1 <= d2) 520 { 521 //获得当前选中项的索引 522 int index = lst_Playerfile.SelectedIndex; 523 524 //清空所有选中项的索引 525 lst_Playerfile.SelectedIndices.Clear(); 526 527 //将改变后的索引重新的赋值给当前选中项的索引 528 if (index != index_dq) 529 { 530 lst_Playerfile.SelectedIndex = index;//这里我写成index是因为 人性化些,歌曲是在单曲的但是你可以把索引放在别的文件上,不用让索引跳来挑去的 531 532 } 533 else 534 { 535 lst_Playerfile.SelectedIndex = index_dq; 536 } 537 musicPlayer.URL = listpath[index_dq]; 538 musicPlayer.Ctlcontrols.play(); 539 IsExistLrc(listpath[index_dq]); 540 } 541 } 542 } 543 #endregion 544 545 #region 计时器timer4--关于播放模式(随机播放) 546 private void timer4_Tick(object sender, EventArgs e) 547 { 548 int i_lst = lst_Playerfile.Items.Count - 1; 549 Random ran = new Random(); 550 int index_t4 = lst_Playerfile.SelectedIndex; 551 552 if (musicPlayer.playState == WMPLib.WMPPlayState.wmppsPlaying) 553 { 554 int vpc = ran.Next(0, i_lst); 555 double d1 = double.Parse(musicPlayer.currentMedia.duration.ToString()); 556 double d2 = double.Parse(musicPlayer.Ctlcontrols.currentPosition.ToString()) + 1; 557 if (d1 <= d2) 558 { 559 //获得当前选中项的索引 560 int index = vpc; 561 //清空所有选中项的索引 562 lst_Playerfile.SelectedIndices.Clear(); 563 //将索引赋值给当前选中项的索引 564 //if (index_t4 != index_dq) 565 //{ 566 lst_Playerfile.SelectedIndex = index;//这里我写成index是因为 人性化些,歌曲是在单曲的但是你可以把索引放在别的文件上,不用让索引跳来挑去的 567 index_dq = index; 568 //} 569 570 musicPlayer.URL = listpath[index]; 571 musicPlayer.Ctlcontrols.play(); 572 IsExistLrc(listpath[index]); 573 } 574 } 575 } 576 #endregion 577 578 #region 滚动播放进度条 579 private void trackBar1_Scroll(object sender, EventArgs e) 580 { 581 musicPlayer.Ctlcontrols.currentPosition = trackBar1.Value; 582 } 583 #endregion 584 585 bool loob = true; 586 587 #region 播放模式(单曲循环、顺序播放、随机播放、列表循环) 588 string strMucisMode = ""; 589 private void ClearCheck() 590 { 591 单曲循环ToolStripMenuItem1.Checked = false; 592 顺序播放ToolStripMenuItem1.Checked = false; 593 随机播放ToolStripMenuItem1.Checked = false; 594 列表循环ToolStripMenuItem1.Checked = false; 595 } 596 597 //播放模式的按钮 598 private void btn_PlayMode_Click(object sender, EventArgs e) 599 { 600 Point p = new Point(20, 0); 601 this.contextMenuStrip2.Show(btn_PlayMode, p);//定义相对于按钮的位置 602 } 603 int index_dq = 0; 604 private void 单曲循环ToolStripMenuItem1_Click(object sender, EventArgs e) 605 { 606 loob = false; 607 strMucisMode = "单曲循环"; 608 ClearCheck(); 609 单曲循环ToolStripMenuItem1.Checked = true; 610 btn_PlayMode.Image = Image.FromFile(".//image/danqu.png"); 611 toolTip1.SetToolTip(this.btn_PlayMode, "单曲循环");//当鼠标放在控件上会显示 612 613 timer4.Enabled = false; 614 timer3.Enabled = true; 615 } 616 private void 顺序播放ToolStripMenuItem1_Click(object sender, EventArgs e) 617 { 618 loob = true; 619 strMucisMode = "顺序播放"; 620 ClearCheck(); 621 顺序播放ToolStripMenuItem1.Checked = true; 622 btn_PlayMode.Image = Image.FromFile(".//image/shunxu.png"); 623 toolTip1.SetToolTip(this.btn_PlayMode, "顺序播放"); 624 625 timer3.Enabled = false; 626 timer4.Enabled = false; 627 } 628 private void 随机播放ToolStripMenuItem1_Click(object sender, EventArgs e) 629 { 630 loob = false; 631 strMucisMode = "随机播放"; 632 ClearCheck(); 633 随机播放ToolStripMenuItem1.Checked = true; 634 btn_PlayMode.Image = Image.FromFile(".//image/suiji.png"); 635 toolTip1.SetToolTip(this.btn_PlayMode, "随机播放"); 636 637 btn_Player.Text = "暂停"; 638 timer3.Enabled = false; 639 timer4.Enabled = true; 640 } 641 642 //列表循环和顺序播放是一样的 我就写成一样的了 643 private void 列表循环ToolStripMenuItem1_Click(object sender, EventArgs e) 644 { 645 loob = true; 646 strMucisMode = "列表循环"; 647 ClearCheck(); 648 列表循环ToolStripMenuItem1.Checked = true; 649 btn_PlayMode.Image = Image.FromFile(".//image/xunhuan.png"); 650 toolTip1.SetToolTip(this.btn_PlayMode, "列表循环"); 651 652 timer3.Enabled = false; 653 timer4.Enabled = false; 654 } 655 #endregion 656 657 #endregion 658 659 #region 4、桌面效果(桌面歌词、最小化) 660 661 #region 新建窗口Form2 显示桌面歌词 662 Form2 frm2 = Form2.GetSingle(); 663 /// 664 /// 显示桌面歌词 665 /// 666 /// 667 /// 668 private void btn_Show_Click(object sender, EventArgs e) 669 { 670 if (btn_Show.Tag.ToString() == "show") 671 { 672 btn_Show.Tag = "hide"; 673 toolTip1.SetToolTip(this.btn_Show, "关闭桌面歌词"); 674 toolTip1.SetToolTip(this.Lb_show, "关闭桌面歌词"); 675 frm2.Show(); 676 } 677 else 678 { 679 btn_Show.Tag = "show"; 680 toolTip1.SetToolTip(this.btn_Show, "显示桌面歌词"); 681 toolTip1.SetToolTip(this.Lb_show, "显示桌面歌词"); 682 frm2.Hide();//隐藏窗口 此处不能关闭,如果关闭后 由于form2窗体是单例模式创建的 一次只能创建一个 关闭后将无法打开 683 } 684 } 685 #endregion 686 687 #region 最小化窗体 电脑右下角显示最小化图标 688 /// 689 /// 点击最小化按钮 690 /// 691 /// 692 /// 693 private void btnMin_Click(object sender, EventArgs e) 694 { 695 if (this.WindowState != FormWindowState.Minimized) 696 { 697 this.Hide();//隐藏窗口 698 this.notifyIcon1.Visible = true;//右下角最小化图标可见 699 this.notifyIcon1.ShowBalloonTip(1000); //提示球 显示时间长度 700 } 701 } 702 /// 703 /// 双击电脑右下角程序图标 704 /// 705 /// 706 /// 707 private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e) 708 { 709 this.Show();//显示窗体 710 } 711 //关闭窗口 712 private void btnExit_Click(object sender, EventArgs e) 713 { 714 this.Close(); 715 } 716 #endregion 717 718 #endregion 719 } 720 } 721 722 #region 网上对axWindowsMediaPlayer1的 概述 723 //利用axWindowsMediaPlayer(Windows Media Player)制作MP3播放器 2 724 //在制作mp3播放器之前,我们需要了解axWindowsMediaPlayer 媒体主要方法属性: 725 //属性/方法名: 说明: 726 //[基本属性]   727 //URL:String; 指定媒体位置,本机或网络地址 728 //uiMode:String; 播放器界面模式,可为Full, Mini, None, Invisible 729 //playState:integer; 播放状态,1=停止,2=暂停,3=播放,6=正在缓冲,9=正在连接,10=准备就绪 730 //enableContextMenu:Boolean; 启用/禁用右键菜单 731 //fullScreen:boolean; 是否全屏显示 732 ////播放器基本控制 733 //Ctlcontrols.play; 播放 734 //Ctlcontrols.pause; 暂停 735 //Ctlcontrols.stop; 停止 736 //Ctlcontrols.currentPosition:double; 当前进度 737 //Ctlcontrols.currentPositionString:string; 当前进度,字符串格式。如“00:23” 738 //Ctlcontrols.fastForward; 快进 739 //Ctlcontrols.fastReverse; 快退 740 //Ctlcontrols.next; 下一曲 741 //Ctlcontrols.previous; 上一曲 742 //[settings] wmp.settings //播放器基本设置 743 //settings.volume:integer; 音量,0-100 744 //settings.autoStart:Boolean; 是否自动播放 745 //settings.mute:Boolean; 是否静音 746 //settings.playCount:integer; 播放次数 747 //[currentMedia] wmp.currentMedia //当前媒体属性 748 //currentMedia.duration:double; 媒体总长度 749 //currentMedia.durationString:string; 媒体总长度,字符串格式。如“03:24” 750 //currentMedia.getItemInfo(const string); 获取当前媒体信息"Title"=媒体标题,"Author"=艺术家,"Copyright"=版权信息,"Description"=媒体内容描述, "Duration"=持续时间(秒),"FileSize"=文件大小,"FileType"=文件类型,"sourceURL"=原始地址 751 //currentMedia.setItemInfo(const string); 通过属性名设置媒体信息 752 //currentMedia.name:string; 同 currentMedia.getItemInfo("Title") 753 //[currentPlaylist] wmp.currentPlaylist //当前播放列表属性 754 //currentPlaylist.count:integer; 当前播放列表所包含媒体数 755 //currentPlaylist.Item[integer]; 获取或设置指定项目媒体信息,其子属性同wmp.currentMedia 756 //axWindowsMediaPlayer1.currentMedia.sourceURL; //获取正在播放的媒体文件的路径 757 //axWindowsMediaPlayer1.currentMedia.name; //获取正在播放的媒体文件的名称 758 //axWindowsMediaPlayer1.Ctlcontrols.Play          播放 759 //axWindowsMediaPlayer1.Ctlcontrols.Stop          停止 760 //axWindowsMediaPlayer1.Ctlcontrols.Pause          暂停 761 //axWindowsMediaPlayer1.Ctlcontrols.PlayCount        文件播放次数 762 //axWindowsMediaPlayer1.Ctlcontrols.AutoRewind       是否循环播放 763 //axWindowsMediaPlayer1.Ctlcontrols.Balance         声道 764 //axWindowsMediaPlayer1.Ctlcontrols.Volume         音量 765 //axWindowsMediaPlayer1.Ctlcontrols.Mute          静音 766 //axWindowsMediaPlayer1.Ctlcontrols.EnableContextMenu    是否允许在控件上点击鼠标右键时弹出快捷菜单 767 //axWindowsMediaPlayer1.Ctlcontrols.AnimationAtStart    是否在播放前先播放动画 768 //axWindowsMediaPlayer1.Ctlcontrols.ShowControls      是否显示控件工具栏 769 //axWindowsMediaPlayer1.Ctlcontrols.ShowAudioControls    是否显示声音控制按钮 770 //axWindowsMediaPlayer1.Ctlcontrols.ShowDisplay       是否显示数据文件的相关信息 771 //axWindowsMediaPlayer1.Ctlcontrols.ShowGotoBar       是否显示Goto栏 772 //axWindowsMediaPlayer1.Ctlcontrols.ShowPositionControls  是否显示位置调节按钮 773 //axWindowsMediaPlayer1.Ctlcontrols.ShowStatusBar      是否显示状态栏 774 //axWindowsMediaPlayer1.Ctlcontrols.ShowTracker       是否显示进度条 775 //axWindowsMediaPlayer1.Ctlcontrols.FastForward       快进 776 //axWindowsMediaPlayer1.Ctlcontrols.FastReverse       快退 777 //axWindowsMediaPlayer1.Ctlcontrols.Rate          快进/快退速率 778 //axWindowsMediaPlayer1.AllowChangeDisplaySize 是否允许自由设置播放图象大小 779 //axWindowsMediaPlayer1.DisplaySize       设置播放图象大小 780 // 1-MpDefaultSize         原始大小 781 // 2-MpHalfSize           原始大小的一半 782 // 3-MpDoubleSize          原始大小的两倍 783 // 4-MpFullScreen          全屏 784 // 5-MpOneSixteenthScreen      屏幕大小的1/16 785 // 6-MpOneFourthScreen       屏幕大小的1/4 786 // 7-MpOneHalfScreen        屏幕大小的1/2 787 //axWindowsMediaPlayer1.ClickToPlay       是否允许单击播放窗口启动Media Player 788 //在视频播放之后,可以通过如下方式读取源视频的宽度和高度,然后设置其还原为原始的大小. 789 // private void ResizeOriginal() 790 // { 791 // int intWidth = axWindowsMediaPlayer1.currentMedia.imageSourceWidth; 792 // int intHeight = axWindowsMediaPlayer1.currentMedia.imageSourceHeight; 793 // axWindowsMediaPlayer1.Width = intWidth + 2; 794 // axWindowsMediaPlayer1.Height = intHeight + 2; 795 // } 796 //打开媒体文件并播放: 797 // Dim filePath As String 798 // With Me.OpenFileDialog1 799 // .Title = "打开语音文件" 800 // .CheckPathExists = True 801 // .CheckFileExists = True 802 // .Multiselect = False 803 // .Filter = "mp3文件(*.mp3)|*.mp3|所有文件(*.*)|*.*" 804 // If .ShowDialog = DialogResult.Cancel Then 805 // Exit Sub 806 // End If 807 // filePath = .FileName 808 // End With 809 // Me.Text = "PC复读机-文件 " & filePath 810 // AxWindowsMediaPlayer1.URL = filePath 811 // Try 812 // Me.AxWindowsMediaPlayer1.Ctlcontrols.play() 813 // Catch ex As Exception 814 // MsgBox("对不起,不能播放此格式语音文件", MsgBoxStyle.OKOnly, "PC复读机") 815 // Exit Sub 816 // End Try 817 //注意: 818 //AxWindowsMediaPlayer1.URL 中URL是表示要播放的文件名,取消了原来的Name属性. 819 //AxWindowsMediaPlayer1.Ctlcontrols.play()播放,同样还有Pause,Stop等其他属性. 820 //AxWindowsMediaPlayer1.settings.balance表示媒体播放的声道设置,0表示均衡,-1和1表示左右声道. 821 //AxWindowsMediaPlayer1.currentMedia.duration 表示要播放的文件的时间长度.可用它获取文件长度. 822 //AxWindowsMediaPlayer1.Ctlcontrols.currentPosition表示正在播放的文件的当前播放位置,可用这个属性来对媒体文件进行前进后退等设置.如 823 //AxWindowsMediaPlayer1.Ctlcontrols.currentPosition+1 表示前进1个时间单位. 824 //AxWindowsMediaPlayer1.settings.rate播放速率,一般乘以16后再显示kbps单位. 825 //注意:在上面程序中,如果在后面加上一个: 826 //msgbox(AxWindowsMediaPlayer1.currentMedia.duration.ToString ) 827 //则显示结果很可能为0,因此,这时候很可能获取不到文件的播放时间长度,容易出错。所以在利用的时候可以加一个timer控件: 828 //Private Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick 829 // EndPoint = AxWindowsMediaPlayer1.currentMedia.duration 830 // If EndPoint = 0 Then Exit Sub '可能因为媒体文件的打开需要一定时间,这里等待媒体文件的打开 831 // msgbox(AxWindowsMediaPlayer1.currentMedia.duration.ToString ) 832 //End Sub 833 //此时msgbox便会显示文件播放长度。 834 //2. Ctlcontrols属性 835 //Ctlcontrols属性是AxWindowsMediaPlayer的一个重要属性, 此控件中有许多常用成员。 836 //(1) 方法play 837 //用于播放多媒体文件,其格式为: 838 //窗体名.控件名.Ctlcontrols.play() 839 //如: AxWindowsMediaPlayer1.Ctlcontrols.play() ‘此处缺省窗体名是Me 840 //(2) 方法pause 841 //用于暂停正在播放的多媒体文件,其格式为: 842 //窗体名.控件名.Ctlcontrols.pause() 843 //如: AxWindowsMediaPlayer1.Ctlcontrols.pause() 844 //(3) 方法stop 845 //用于停止正在播放的多媒体文件,其格式为: 846 //窗体名.控件名.Ctlcontrols.stop() 847 //如: AxWindowsMediaPlayer1.Ctlcontrols.stop() 848 //(4) 方法fastforward 849 //用于将正在播放的多媒体文件快进,其格式为: 850 //窗体名.控件名.Ctlcontrols.fastforward() 851 //如: AxWindowsMediaPlayer1.Ctlcontrols.forward() 852 //(5) 方法fastreverse 853 //窗体名.控件名.Ctlcontrols.fastreverse() 854 //如: AxWindowsMediaPlayer1.Ctlcontrols.fastreverse() 855 //6. 属性CurrentPosition 856 //用于获取多媒体文件当前的播放进度,其值是数值类型,使用格式为: 857 //窗体名.控件名.Ctlcontrols.currentPosition 858 //d1 =AxWindowsMediaPlayer1.Ctlcontrols.currentPosition 859 //其中d1 是一个整型变量。 860 //7. 属性Duration 861 //用于获取当前多媒体文件的播放的总时间,其值为数值类型,其使用格式为: 862 //窗体名.控件名.currentMedia.duration 863 //如:d2 =AxWindowsMediaPlayer1.currentMedia.duration 864 //其中d2是一个整型变量。 865 #endregion 音乐播放器(Form1) 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace 音乐播放器 11 { 12 public partial class Form2 : Form 13 { 14 public Form2() 15 { 16 InitializeComponent(); 17 } 18 19 #region 1、单例模式(只显示一个窗体) 20 public static string strMusicLrc = " "; 21 22 public static Form2 FrmSingle = null;//创建一个单例模式 23 24 //提供一个静态方法 如果窗体未被创建则返回一个新窗体 25 public static Form2 GetSingle() 26 { 27 if (FrmSingle == null) 28 { 29 FrmSingle = new Form2(); 30 } 31 return FrmSingle; 32 } 33 #endregion 34 35 #region 2、设置From2 的起始位置 36 private void Form2_Load(object sender, EventArgs e) 37 { 38 this.Location = new Point(200, 600);//设置窗口出现的位置 39 40 //以下两行代码可以将窗体设置为透明背景 41 this.BackColor = Color.Black; 42 this.TransparencyKey = this.BackColor; 43 } 44 #endregion 45 46 #region 3、创建一个静态方法 通过静态方法进行歌词传递 47 private static string _strLrc; 48 public static string StrLrc 49 { 50 get { return _strLrc; } 51 set { _strLrc = value; } 52 } 53 54 //定时刷新歌词 55 private void timer1_Tick(object sender, EventArgs e) 56 { 57 label1.Text = StrLrc; 58 } 59 #endregion 60 61 #region 窗体移动 62 bool formMoveFlag = false;//窗体是否移动标志位位 63 Point formPoint;//记录窗体的位置 64 /// 65 /// 鼠标在窗体上按下 66 /// 67 /// 68 /// 69 private void MainForm_MouseDown(object sender, MouseEventArgs e) 70 { 71 formPoint = new Point(); 72 73 if (e.Button == MouseButtons.Left)//如果鼠标左键按下 74 { 75 formPoint = new Point(-e.X, -e.Y); //得到变量的值 76 formMoveFlag = true;//开始移动 77 78 #region 还有种方法 79 // int x; 80 // int y; 81 //x = -e.X - SystemInformation.FrameBorderSize.Width;//获取当前X坐标 82 //y = -e.Y - SystemInformation.CaptionHeight - SystemInformation.FrameBorderSize.Height;//获取当前Y坐标 83 //formPoint = new Point(x, y); 84 //formMove = true;//开始移动 85 #endregion 86 } 87 } 88 /// 89 /// 鼠标在窗体上移动 90 /// 91 /// 92 /// 93 private void MainForm_MouseMove(object sender, MouseEventArgs e) 94 { 95 if (formMoveFlag == true)//如果窗体正在移动 96 { 97 Point mousePos = Control.MousePosition;//获取鼠标当前位置 98 mousePos.Offset(formPoint.X, formPoint.Y);//设置鼠标移动后的位置 99 Location = mousePos;//将鼠标的坐标 传递给窗体 100 } 101 } 102 /// 103 /// 鼠标在窗体上释放 104 /// 105 /// 106 /// 107 private void MainForm_MouseUp(object sender, MouseEventArgs e) 108 { 109 if (e.Button == MouseButtons.Left)//按下的是鼠标左键 110 { 111 formMoveFlag = false;//停止移动 112 } 113 } 114 #endregion 115 } 116 } 音乐播放器(Form2) 播放mp3的实体图:   播放mv的实体图:     二、线程  1.创建线程_前台_后台_挂起_唤醒_阻塞_终止_优先级 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 7 namespace 创建线程_前台_后台_挂起_唤醒_阻塞_终止_优先级 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 //创建线程 14 Thread t1 = new Thread(new ThreadStart(TestMethod)); 15 Thread t2 = new Thread(new ParameterizedThreadStart(TestMethod)); 16 Thread t3 = new Thread(new ThreadStart(TestMethod)); 17 Thread t4 = new Thread(new ThreadStart(s5000)); 18 19 //我把他们的排序从高到低----优先级 20 //Highest 在具有任何其他优先级的线程之前 21 //AboveNormal 可以将Thread安排在具有highest优先级线程之后,在Normal之前 22 //Normal 在AboveNormal之后,BelowNormal之前。默认值。 23 //BelowNormal 在Normal之后,Lowest之前 24 //Lowest 在具有其他任何优先级的线程之后 25 26 //Priority属性来获取和设置其优先级 27 t1.Priority = ThreadPriority.Highest; 28 //t2.Priority = ThreadPriority.AboveNormal; 29 //t3.Priority = ThreadPriority.AboveNormal;//可以不用写,这是默认的 30 //t4.Priority = ThreadPriority.AboveNormal; 31 32 #region 线程的前台、后台的区别 33 //创建线程默认是前台的 34 //当所有前台线程关闭时,所有的后台线程也会被直接终止,不会抛出异常。 35 //当程序主线程被关闭时前台线程是不会关闭的,当前台线程全部结束后,整个程序结束 36 t1.IsBackground = true;//手动将前台线程改成后台线程IsBackground 37 t2.IsBackground = true; 38 //这时,t3,t4还是前台线程 39 #endregion 40 41 #region t1线程被挂起,唤醒。t2线程正常启动 42 //!我学习这段时发现 挂起和唤醒已经过时了 不用怕,下面还有各种各样的线程锁 43 t1.Start(); 44 t1.Suspend();//线程t1被挂起 -->Suspend 45 t2.Start("hello"); 46 Thread.Sleep(1000);//使主线程休眠1s 47 Console.ReadKey(); 48 //在随便点击按钮后,线程t1被唤醒,恢复线程 -->Resume 49 t1.Resume(); 50 #endregion 51 52 #region 对中断、中止、终止的理解 53 //中断 :计算机执行某程序时,发生了紧急事件或有特殊请求,中央处理机暂停某程序的执行,而去处理上述事件或请求,处理完毕后再重新执行某程序的过程。 54 //中止 :中途停止。 55 //终止 :结束;停止。 56 #endregion 57 58 #region Abort和Join的搭配使用 59 //线程被终止,就停止运行,是无法恢复的,因为 Windows 会永久地删除被中止线程的所有数据。 60 //1.abort()的功能是用来终止调用此方法的线程的,只是在多数情况下, 61 //它需要一点时间,有些延迟(可能在短时间内此线程还在执行)... 62 //2.join()方法它的功能不是终止线程,而是在t线程终止之前,阻止正在结束 63 //(调用了abort()方法但还未结束)的t线程执行,同时使主线程等待, 64 //直到t线程终止(也就是abort()方法终止过程完毕)了再执行下面的代码, 65 //打印出来的结果,执行状态就为FALSE,线程状态也为停止了 66 t3.Start(); 67 t3.Abort(); //终止线程 使用Abort()方法 68 //t3.Start();//线程正在运行或被终止;它无法重新启动。 这样是错误的 69 Console.WriteLine("线程t3被终止,按任意键继续"); 70 t3.Join();//等待线程t3终止结束之后才能继续向下进行 71 Console.ReadKey(); 72 #endregion 73 74 //join和sleep的区别 75 t4.Start(); 76 Console.WriteLine("开始默念五个数"); 77 t4.Join(); //阻塞 等待线程t4执行结束在执行下面的代码 78 t4.Join(5000);//他俩一样都是阻塞,这个只是阻塞5秒钟,不管t4线程有没有结束5s之后就继续向下执行,不要误解 在5s内t4线程结束了就会立即向下执行 这就是join和sleep的区别 79 80 Console.WriteLine(t1.Name + "状态:" + t1.ThreadState + " " + t1.IsAlive); 81 Console.WriteLine(t2.Name + "状态:" + t2.ThreadState + " " + t2.IsAlive); 82 Console.WriteLine(t3.Name + "状态:" + t3.ThreadState + " " + t3.IsAlive); 83 Console.WriteLine(t4.Name + "状态:" + t4.ThreadState + " " + t4.IsAlive); 84 Console.ReadKey(); 85 } 86 87 public static void TestMethod() 88 { 89 Console.WriteLine("不带参数的线程"); 90 } 91 92 public static void TestMethod(object data) 93 { 94 string datastr = data as string; 95 Console.WriteLine("带参数的线程函数,参数为:{0}", datastr); 96 } 97 98 public static void s5000() 99 { 100 Thread.Sleep(5000);//线程停留5秒 101 } 102 } 103 } 创建线程_前台_后台_挂起_唤醒_阻塞_终止_优先级  2.同步 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 7 #region 同步和异步的区别 8 ////同步、异步和并行的讲解:(百度上找的) 9 ////假如5个菜分别是A,B,C,D,E 你有两个炉子 只能同时炒A跟B 10 ////所以剩下的CDE只能等AB炒完了才能开始 这个等待就是同步 我们叫做阻塞 即这个时候你只能做AB这两个菜 11 12 ////假如你还有一台咖啡机, 你在炒AB的时候 把咖啡豆跟水放到咖啡机里打开开关 你就可以不用去管它了 13 ////我们说 我们新开了一个线程煮咖啡 但是注咖啡这个动作不妨碍你炒菜 所以煮咖啡这个线程是异步的 我们叫非阻塞 14 15 ////等到咖啡机 叮--- 告诉你咖啡煮好了 这个时候你去把咖啡拿出来 叮的这一声 我们叫通知 通知主线程也就是你 16 ////我(咖啡机)的工作做完了, 你去把咖啡拿出来 我们叫回调 也就是 咖啡机线程完成之后 通知主线程要做的动作 17 18 ////简单来讲 会占用你的时间 让你无法做其它事情的任务 叫同步任务(炒菜要专注 不然可能会糊锅)。 19 ////那些不需要占用你的时间的任务 叫异步任务 咖啡机自己会把咖啡煮好 不需要你一直看着它 20 21 ////只有两个炉子,那就只能有2个人炒, 这个叫并行 22 #endregion 23 #region 注意这里ManualResetEvent和AutoResetEvent的一个重要区别: 24 // manual的话肯定会给线程1和线程2都发送一个信号,而auto只会随机给其中一个发送信号。 25 26 //为什么一个叫manual而一个叫auto呢?我想这是很多人的疑问,现在我们就来看这个问题。 27 // 刚才_manualResetEvent .Set();的这句话我想大家都明白了,可以看做将IsRelease的属性设置为true.线程1中 28 // _manualResetEvent.WaitOne();接收到信号后不再阻塞线程1。在此之后的整个过程中IsRelease的值都是true.如果 29 //想将IsRelease的值回复成false,就必须再调用_manualResetEvent.Reset()的方法。 30 31 // 如果是_autoResetEvent.set(),那么_autoResetEvent.WaitOne()后会自动将IsRelease的值自动设置为false. 32 //这就是为什么一个叫auto,一个叫manual. 33 #endregion 34 35 namespace 简单的同步 36 { 37 class Program 38 { 39 static AutoResetEvent myResetEvent = new AutoResetEvent(false);//默认阻塞当前线程 40 static AutoResetEvent ChangeEvent = new AutoResetEvent(false); 41 //static ManualResetEvent myResetEvent = new ManualResetEvent(false);//默认阻塞当前线程 42 //static ManualResetEvent ChangeEvent = new ManualResetEvent(false); 43 static int number; //这是关键资源 44 45 static void Main() 46 { 47 Thread payMoneyThread = new Thread(new ThreadStart(PayMoneyProc)); 48 //Thread payMoneyThread = new Thread(PayMoneyProc);//也可以写成这样 49 payMoneyThread.Name = "付钱线程"; 50 51 Thread getBookThread = new Thread(new ThreadStart(GetBookProc)); 52 getBookThread.Name = "取书线程"; 53 54 payMoneyThread.IsBackground = true; 55 getBookThread.IsBackground = true; 56 57 payMoneyThread.Start(); 58 getBookThread.Start(); 59 60 for (int i = 1; i <= 10; i++) 61 { 62 Console.WriteLine("买书线程:数量{0}", i);//这个主线程运行的 63 number = i; 64 myResetEvent.Set();//发送信号 65 //ChangeEvent.Set(); 66 Thread.Sleep(1000); 67 } 68 payMoneyThread.Abort(); 69 getBookThread.Abort(); 70 Console.ReadKey(); 71 } 72 73 static void PayMoneyProc() 74 { 75 while (true) 76 { 77 myResetEvent.WaitOne();//接收信号 78 //myResetEvent.Reset(); 79 Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number); 80 ChangeEvent.Set(); 81 } 82 } 83 static void GetBookProc() 84 { 85 while (true) 86 { 87 ChangeEvent.WaitOne(); 88 //ChangeEvent.Reset(); 89 Console.WriteLine("{0}:数量{1}", Thread.CurrentThread.Name, number); 90 Console.WriteLine("------------------------------------------"); 91 //Thread.Sleep(0); 92 } 93 } 94 } 95 } 简单的同步 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 7 //当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据, 8 //即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。 9 10 //调用GetAge()得到的是“主”内存区域的Age数值。 11 //用volatile修饰后的变量不允许有不同于“主”内存区域的变量拷贝。 12 //换句话说,一个变量经volatile修饰后在所有线程中必须是同步的; 13 //任何线程中改变了它的值,所有其他线程立即获取到了相同的值。 14 //理所当然的,volatile修饰的变量存取时比一般变量消耗的资源要多一点; 15 //因此线程有它自己的变量拷贝更为高效。 16 namespace volatile_同步 17 { 18 class Program 19 { 20 static volatile int Age = 10;//其实不加volatile输出也一样,有点抽象,内存内的值 21 22 public static int GetAge1() 23 { 24 Age = 20; 25 return Age; 26 } 27 28 public static int GetAge2() 29 { 30 Age = 30; 31 return Age; 32 } 33 34 public static void Age1() 35 { 36 for (int i = 0; i < 10; i++) 37 { 38 Thread.Sleep(1000); 39 Console.WriteLine("Age1=" + Age); 40 } 41 } 42 43 static void Main(string[] args) 44 { 45 Thread t1 = new Thread(Age1); 46 t1.IsBackground = true; 47 t1.Start(); 48 Thread.Sleep(3000); 49 GetAge1(); 50 Thread.Sleep(3000);//先运行一会GetAge1,在运行GetAge2 51 GetAge2();//运行GetAge2时观察Age1值变没?变了就是同步。 52 53 Console.Read(); 54 } 55 } 56 } 57 #region volatile的详解和与lock的不同 58 //volatile多用于多线程的环境,当一个变量定义为volatile时, 59 //读取这个变量的值时候每次都是从momery里面读取而不是从cache读。 60 //这样做是为了保证读取该变量的信息都是最新的,而无论其他线程如何更新这个变量。 61 62 // 更通俗的解释: 63 //Volatile 字面的意思时易变的,不稳定的。在C#中也差不多可以这样理解。 64 //编译器在优化代码时,可能会把经常用到的代码存在Cache里面,然后下一次调用就直接读取Cache而不是内存,这样就大大提高了效率。但是问题也随之而来了。 65 //在多线程程序中,如果把一个变量放入Cache后,又有其他线程改变了变量的值,那么本线程是无法知道这个变化的。它可能会直接读Cache里的数据。但是很不幸,Cache里的数据已经过期了,读出来的是不合时宜的脏数据。这时就会出现bug。 66 //用Volatile声明变量可以解决这个问题。用Volatile声明的变量就相当于告诉编译器,我不要把这个变量写Cache,因为这个变量是可能发生改变的。 67 68 //volatile解决问题1:CPU缓存 69 //volatile解决问题2:编译器优化(指令乱序) 70 71 //volatile 关键字表示字段可能被多个并发执行线程修改。声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制。这样可以确保该字段在任何时间呈现的都是最新的值。 72 //volatile 修饰符通常用于由多个线程访问而不使用 lock 语句(C# 参考) 语句对访问进行序列化的字段。有关在多线程方案中使用 volatile 的示例,请参见如何:创建和终止线程(C# 编程指南)。 73 //volatile 关键字可应用于以下类型的字段: 74 //引用类型。 75 //指针类型(在不安全的上下文中)。请注意,虽然指针本身可以是可变的,但是它指向的对象不能是可变的。换句话说,您无法声明“指向可变对象的指针”。 76 //整型,如 sbyte、byte、short、ushort、int、uint、char、float 和 bool。 77 //具有整数基类型的枚举类型。 78 //已知为引用类型的泛型类型参数。 79 //IntPtr 和 UIntPtr。 80 81 //volatile是最简单的一种同步方法,当然简单是要付出代价的。它只能在变量一级做同步, 82 //volatile的含义就是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我。 83 //因此,当多线程同时访问该变量时,都将直接操作主存,从本质上做到了变量共享。 84 85 86 //很有道理::: 87 //C# volatile 与 lock 88 //volatile的使用场景:多个线程同时访问一个变量,CLR为了效率,允许每个线程进行本地缓存, 89 //这就导致了变量的不一致性。volatile就是为了解决这个问题,volatile修饰的变量, 90 //不允许线程进行本地缓存,每个线程的读写都是直接操作在共享内存上, 91 //这就保证了变量始终具有一致性。缺点很明显:牺牲了效率。 92 93 //lock的使用场景:多个线程同时访问一个代码块,使用lock 修饰该代码块,强制多个线程进行排队, 94 //一个接一个的去访问。缺点很明显:排队进行必然导致效率低。 95 96 //系统中应该尽量减少lock的使用(也就是排队执行的情况),因为根据阿姆达尔定律: 97 //S=1/(a+(1-a)/n) ,其中S 为加速比,a 为串行计算部分,n为并行计算节点,该公式意味着: 98 //如果具备足够的并行计算节点,要想增加系统的速度,必须减少串行部分。因为串行意味着, 99 //一个人做的时候,其他人必须等着。Google 的MapReduce,也就是把串行计算转化为并行计算。 100 #endregion volatile_同步 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 7 //在这篇博客里学习的 http://www.cnblogs.com/zhycyq/articles/2679017.html 8 namespace _3._1Monitor_Wait___Pulse___PulseAll__ 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 //开启三个线程 15 new Thread(A).Start(); 16 new Thread(B).Start(); 17 new Thread(C).Start(); 18 Console.ReadLine(); 19 } 20 21 //1.必须要先知道:object这是 .NET Framework 中所有类的最终基类 22 //2.lock(object对象) 就是把.NET Framework 中所有类都加上锁了 23 //三个进程中A没有睡眠直接进入就绪队列,B、C在外面(等待队列中),此时A是lockObj的拥有者 24 //然后结果1s后A用Pulus发出信号,允许第一个等待队列中的线程B进入就绪队列,线程B也是用Pulus发出信号,使线程C进入了就绪队列。 25 //线程A用Wait放弃同步对象,进入了等待队列 26 //此时就绪队列中线程B是lockObj的拥有者开始执行了 27 //线程B结束后线程C拥有了lockObj使用权,开始执行 28 //线程C结束了,同步对象闲置,A就重新得到了同步对象 29 //线程A开始执行了 30 31 //最后提醒一下,线程是没有先后顺序的,A先进入就绪队列获得拥有权这是一定的 32 //接下来B和C不清楚是谁先进来的了,还有最后A和剩下的那个线程谁先得到拥有权也是未知的 33 //上面的 解释方法思考 34 35 static object lockObj = new object(); 36 static void A() 37 { 38 lock (lockObj) //线程A直接进入就绪队列 39 { 40 Thread.Sleep(1000); 41 Monitor.Pulse(lockObj); 42 Monitor.Wait(lockObj); //自我流放到等待队列 43 } 44 Console.WriteLine("A exit..."); 45 } 46 static void B() 47 { 48 Thread.Sleep(500); 49 lock (lockObj) //线程B500ms后进入就绪队列 50 { 51 Monitor.Pulse(lockObj); 52 } 53 Console.WriteLine("B exit..."); 54 } 55 static void C() 56 { 57 Thread.Sleep(800); 58 lock (lockObj) //线程C800ms后进入就绪队列 59 { } 60 Console.WriteLine("C exit..."); 61 } 62 } 63 64 //下面的这个 和上面的这个思路差不多,就不解释了,用的是线程池 65 //class MyManualEvent 66 //{ 67 // private object lockObj = new object(); 68 // private bool hasSet = false; 69 // public void Set() 70 // { 71 // lock (lockObj) 72 // { 73 // hasSet = true; 74 // Monitor.PulseAll(lockObj); 75 // } 76 // } 77 // public void WaitOne() 78 // { 79 // lock (lockObj) 80 // { 81 // while (!hasSet) 82 // { 83 // Monitor.Wait(lockObj); 84 // } 85 // } 86 // } 87 //} 88 //class Program 89 //{ 90 // static MyManualEvent myManualEvent = new MyManualEvent(); 91 // static void Main(string[] args) 92 // { 93 // ThreadPool.QueueUserWorkItem(WorkerThread, "A"); 94 // ThreadPool.QueueUserWorkItem(WorkerThread, "B"); 95 // Console.WriteLine("Press enter to signal the green light"); 96 // Console.ReadLine(); 97 // myManualEvent.Set(); 98 // ThreadPool.QueueUserWorkItem(WorkerThread, "C"); 99 // Console.ReadLine(); 100 // } 101 // static void WorkerThread(object state) 102 // { 103 // myManualEvent.WaitOne(); 104 // Console.WriteLine("Thread {0} got the green light...", state); 105 // } 106 //} 107 } Monitor、Wait、Pulse、PulseAll 1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.Threading; 5 6 //Monitor类提供了与lock类似的功能,不过与lock不同的是,它能更好的控制同步块, 7 //当调用了Monitor的Enter(Object o)方法时,会获取o的独占权,直到调用Exit(Object o)方法时, 8 //才会释放对o的独占权,可以多次调用Enter(Object o)方法,只需要调用同样次数的Exit(Object o)方法 9 //即可,Monitor类同时提供了TryEnter(Object o,[int])的一个重载方法,该方法尝试获取o对象的独占权, 10 //当获取独占权失败时,将返回false。 11 12 // 但使用 lock 通常比直接使用 Monitor 更可取,一方面是因为 lock 更简洁, 13 //另一方面是因为 lock 确保了即使受保护的代码引发异常,也可以释放基础监视器。 14 //这是通过 finally 中调用Exit来实现的。事实上,lock 就是用 Monitor 类来实现的。 15 //下面两段代码是等效的: 16 17 //1.Monitor.Enter(object)方法是获取锁,Monitor.Exit(object)方法是释放锁,这就是Monitor最常用的两个方法,当然在使用过程中为了避免获取锁之后因为异常,致锁无法释放,所以需要在try{} catch(){}之后的finally{}结构体中释放锁(Monitor.Exit())。 18 //2.Monitor的常用属性和方法: 19 // Enter(Object) 在指定对象上获取排他锁。 20 // Exit(Object) 释放指定对象上的排他锁。 21 // IsEntered 确定当前线程是否保留指定对象锁。 22 // Pulse 通知等待队列中的线程锁定对象状态的更改。 23 // PulseAll 通知所有的等待线程对象状态的更改。 24 // TryEnter(Object) 试图获取指定对象的排他锁。 25 // TryEnter(Object, Boolean) 尝试获取指定对象上的排他锁,并自动设置一个值,指示是否得到了该锁。 26 // Wait(Object) 释放对象上的锁并阻止当前线程,直到它重新获取该锁。 27 28 //两个线程之间如何协同工作。 29 //这个程序的思路是共同做一件事情(从一个ArrayList中删除元素),如果执行完成了,两个线程都停止执行。 30 31 public class ThreadDemo 32 { 33 private Thread threadOne; 34 private Thread threadTwo; 35 private ArrayList stringList; 36 private event EventHandler OnNumberClear;//数据删除完成引发的事件 37 public static void Main() 38 { 39 ThreadDemo demo = new ThreadDemo(1000); 40 demo.Action(); 41 Console.WriteLine(); 42 Thread.Sleep(10000); 43 Console.ReadKey(); 44 } 45 public ThreadDemo(int number) 46 { 47 Random random = new Random(1000000); 48 stringList = new ArrayList(number); 49 for (int i = 0; i < number; i++) 50 { 51 stringList.Add(random.Next().ToString()); 52 } 53 threadOne = new Thread(new ThreadStart(Run));//两个线程共同做一件事情 54 threadTwo = new Thread(new ThreadStart(Run));//两个线程共同做一件事情 55 threadOne.Name = "线程1"; 56 threadTwo.Name = "线程2"; 57 OnNumberClear += new EventHandler(ThreadDemo_OnNumberClear); 58 } 59 /// 60 /// 开始工作 61 /// 62 public void Action() 63 { 64 threadOne.Start(); 65 threadTwo.Start(); 66 } 67 /// 68 /// 共同做的工作 69 /// 70 private void Run() 71 { 72 string stringValue = null; 73 while (true) 74 { 75 Monitor.Enter(this);//锁定,保持同步 76 stringValue = (string)stringList[0]; 77 Console.WriteLine(Thread.CurrentThread.Name + "删除了" + stringValue); 78 stringList.RemoveAt(0);//删除ArrayList中的元素 79 if (stringList.Count == 0) 80 { 81 OnNumberClear(this, new EventArgs());//引发完成事件 82 } 83 Monitor.Exit(this);//取消锁定 84 Thread.Sleep(5); 85 } 86 } 87 88 //执行完成之后,停止所有线程 89 void ThreadDemo_OnNumberClear(object sender, EventArgs e) 90 { 91 Console.WriteLine("执行完了,停止了所有线程的执行。"); 92 threadTwo.Abort(); 93 threadOne.Abort(); 94 } 95 } Monitor同步互斥锁 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 7 #region 一看就能懂 lock使用范围 8 //lock: 9 //class A 10 // { 11 // } 12 //struct S 13 //{ 14 //} 15 //int i; 16 //object o; 17 //string str; 18 //A a=new A(); 19 //S s=new S(); 20 //lock(i){}//错误 21 //lock(o){}//正确 22 //lock(str){}//正确 23 //lock(a){}//正确 24 //lock(s){}//错误 25 #endregion 26 27 #region 重点: 28 //lock的参数必须是基于引用类型的对象,不要是基本类型像bool,int什么的, 29 //这样根本不能同步,原因是lock的参数要求是对象,如果传入int, 30 //势必要发生装箱操作,这样每次lock的都将是一个新的不同的对象。 31 //最好避免使用public类型或不受程序控制的对象实例,因为这样很可能导致死锁。 32 //特别是不要使用字符串作为lock的参数,因为字符串被CLR“暂留”, 33 //就是说整个应用程序中给定的字符串都只有一个实例,因此更容易造成死锁现象。 34 //建议使用不被“暂留”的私有或受保护成员作为参数。其实某些类已经提供了专门用于被锁的成员, 35 //比如Array类型提供SyncRoot,许多其它集合类型也都提供了SyncRoot。 36 // 所以,使用lock应该注意以下几点:  37 38 // 1、如果一个类的实例是public的,最好不要lock(this)。 39 //因为使用你的类的人也许不知道你用了lock,如果他new了一个实例, 40 //并且对这个实例上锁,就很容易造成死锁。 41 42 // 2、如果MyType是public的,不要lock(typeof(MyType)) 43 44 // 3、永远也不要lock一个字符串 45 #endregion 46 //说明: 47 // private static object ojb = new object(); 48 // lock(obj) 49 // { 50 // //锁定运行的代码段 51 // } 52 //假设线程A先执行,线程B稍微慢一点。线程A执行到lock语句,判断obj是否已申请了互斥锁, 53 //判断依据是逐个与已存在的锁进行object.ReferenceEquals比较(此处未加证实),如果不存在, 54 //则申请一个新的互斥锁,这时线程A进入lock里面了。 55 //这时假设线程B启动了,而线程A还未执行完lock里面的代码。线程B执行到lock语句, 56 //检查到obj已经申请了互斥锁,于是等待;直到线程A执行完毕,释放互斥锁, 57 //线程B才能申请新的互斥锁并执行lock里面的代码。 58 namespace _4.lock同步互斥锁 59 { 60 //为了能看的清楚一些,我是将两个线程分别执行两个方法,这两个方法调用同一个方法 61 //测试lock能不能使一个进程执行时另一个线程等待,这时不确定两个线程哪个先启动的,没确定优先级 62 63 #region 不加lock的输出 64 //我是线程t2 65 //我是线程t1 66 //我是线程t1 67 //我是线程t2 68 //我是线程t2 69 //我是线程t1 70 //我是线程t1 71 //我是线程t2 72 //我是线程t2 73 //我是线程t1 74 //不加lock 输出的没有顺序,加入操作大文件更改或其他操作时很容易死锁 75 #endregion 76 77 #region 加lock的输出 78 //我是线程t2 79 //我是线程t2 80 //我是线程t2 81 //我是线程t2 82 //我是线程t2 83 //我是线程t1 84 //我是线程t1 85 //我是线程t1 86 //我是线程t1 87 //我是线程t1 88 //加锁后多线程同步时不会产生死锁,一个线程执行时另一个线程在等待 89 //别误会为什么是线程t2先执行的,可以想象好多人在跑步比赛,这是随机的 90 #endregion 91 92 partial class Class1//我没有 写成public 93 { 94 //Class1 c1 = new Class1();//要用public 就实例化 使用lock(c1) 95 public void T1(object tt1) 96 { 97 Count(tt1); 98 } 99 public void T2(object tt2) 100 { 101 Count(tt2); 102 } 103 public void Count(object str) 104 { 105 lock (this)//不加锁直接把这段去掉 106 for (int i = 0; i < 5; i++) 107 { 108 Thread.Sleep(500); 109 Console.WriteLine(str as string); 110 } 111 } 112 } 113 class Program 114 { 115 static void Main(string[] args) 116 { 117 Class1 c1 = new Class1(); 118 Thread t1 = new Thread(c1.T1); 119 Thread t2 = new Thread(c1.T2); 120 121 t1.IsBackground = true; 122 t2.IsBackground = true; 123 124 string str1 = "我是线程t1"; 125 string str2 = "我是线程t2"; 126 127 t1.Start(str1); 128 t2.Start(str2); 129 130 while (true) 131 { 132 if (t1.IsAlive == true || t2.IsAlive == true) 133 { 134 //使主线程停顿一下,只是方便理解,不加这段也是一样的, 135 //两个线程没执行完毕时主线程已经停在循环内了 136 Thread.Sleep(1000); 137 } 138 else 139 break; 140 } 141 Console.WriteLine("回车,结束"); 142 Console.Read(); 143 } 144 } 145 } lock同步互斥锁 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 //对于整数数据类型的简单操作,可以用 Interlocked 类的成员来实现线程同步, 6 //存在于System.Threading命名空间。Interlocked类有以下方法:Increment , 7 //Decrement , Exchange 和CompareExchange 。使用Increment 和Decrement 8 //可以保证对一个整数的加减为一个原子操作。Exchange 方法自动交换指定变量的值。 9 //CompareExchange 方法组合了两个操作:比较两个值以及根据比较的结果将第三个值 10 //存储在其中一个变量中。比较和交换操作也是按原子操作执行的。 11 namespace _5.Interlocked整数数据类型同步 12 { 13 class Program 14 { 15 static void Main(string[] args) 16 { 17 int i = 2; 18 System.Threading.Interlocked.Increment(ref i);//递增i--> 2+1 = 3 19 Console.WriteLine(i); 20 System.Threading.Interlocked.Decrement(ref i);//递减i--> 3-1 = 2 21 Console.WriteLine(i); 22 System.Threading.Interlocked.Exchange(ref i, 100);//指定的值返回i=100 23 Console.WriteLine(i); 24 System.Threading.Interlocked.CompareExchange(ref i, 10, 100);//如果10和100是个相等的数就返回相同的那个数,不等就返回第一个数 25 Console.WriteLine(i); 26 27 Console.Read(); 28 } 29 } 30 } Interlocked整数数据类型同步 1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Threading; 5 6 //下面是ReaderWriterLock类中的四个主要方法: 7 // a. AcquireReaderLock(): 这个重载方法获取一个读者锁,接受一个整型或者TimeSpan类型的timeout 值。timeout是一个检测死锁的利器。 8 // b. AcquireWriterLock(): 这个重载方法获取一个写者锁,接受一个整型或者TimeSpan类型的timeout 值。 9 // c. ReleaseReaderLock(): 释放读者锁。 10 // d. ReleaseWriterLock(): 释放写者锁。 11 12 //线程wt1 和 wt2 是WriteInts()方法中获得写锁的写者线程,线程rt1 和 rt2 是在ReadInts()方法中 13 //获得读者锁的读者线程。在WriteInts()方法中,变量x 和 y 的值分别被改成a 和 b. 当线程wt1 或 14 //wt2 通过调用AcquireWriterLock() 方法获得一个写者锁后,那么直到这个线程通过调用 15 //ReleaseWriterLock()方法释放锁之前任何其他线程(包括读者线程rt1 和 rt2)都不被允许访问相应对象。 16 //这个行为与Monitors类似 。在ReadInts()方法中,线程rt1 和 rt2 通过调用AcquireReaderLock()方法 17 //获得读者锁, 这两个线程可以并发地访问变量x 和 y. 直到读者线程释放它们的读者锁以后,写者线程 18 //(wt1 和 wt2)才被允许访问对应对象。只有读者线程在获得读者锁以后才可以并发地访问。 19 20 //Monitors类对于只想来读数据而非写数据来说过于“安全”了。Monitors 也有一些性能问题, 21 //对于只读类型的访问来说,性能瓶颈是可以避免的。ReaderWriterLock类通过允许任意数量的线程 22 //并发地读取数据来提供一个解决数据读-写问题的完美方案。当线程更新数据时锁住数据。当没有 23 //写者线程拥有锁的时候写者线程可以获得锁。写者锁可以在没有读者线程或者写者线程拥有锁的时候 24 //获得锁。因此,ReaderWriterLock 就像是一段关键部分代码, 它也支持一个timeout 值,而这方面 25 //在检测死锁时非常有用。 26 namespace ReadWriteLock 27 { 28 public class ReadWrite 29 { 30 private ReaderWriterLock rwl; 31 private int x; 32 private int y; 33 34 public ReadWrite() 35 { 36 rwl = new ReaderWriterLock(); 37 } 38 39 public void ReadInts(ref int a, ref int b) 40 { 41 rwl.AcquireReaderLock(Timeout.Infinite); 42 try 43 { 44 a = this.x; 45 b = this.y; 46 } 47 finally 48 { 49 rwl.ReleaseReaderLock(); 50 } 51 } 52 53 public void WriteInts(int a, int b) 54 { 55 rwl.AcquireWriterLock(Timeout.Infinite); 56 try 57 { 58 this.x = a; 59 this.y = b; 60 Console.WriteLine("x = " + this.x 61 + " y = " + this.y 62 + " ThreadID = " + Thread.CurrentThread.GetHashCode()); 63 } 64 finally 65 { 66 rwl.ReleaseWriterLock(); 67 } 68 } 69 } 70 71 public class RWApp 72 { 73 private ReadWrite rw = new ReadWrite(); 74 75 public static void Main(string[] args) 76 { 77 RWApp e = new RWApp(); 78 79 Thread wt1 = new Thread(new ThreadStart(e.Write)); 80 wt1.Start(); 81 Thread wt2 = new Thread(new ThreadStart(e.Write)); 82 wt2.Start(); 83 84 Thread rt1 = new Thread(new ThreadStart(e.Read)); 85 rt1.Start(); 86 Thread rt2 = new Thread(new ThreadStart(e.Read)); 87 rt2.Start(); 88 89 Console.ReadLine(); 90 } 91 92 private void Write() 93 { 94 int a = 10; 95 int b = 11; 96 Console.WriteLine("************** Write *************"); 97 98 for (int i = 0; i < 5; i++) 99 { 100 this.rw.WriteInts(a++, b++); 101 Thread.Sleep(1000); 102 } 103 } 104 105 private void Read() 106 { 107 int a = 10; 108 int b = 11; 109 Console.WriteLine("************** Read *************"); 110 111 for (int i = 0; i < 5; i++) 112 { 113 this.rw.ReadInts(ref a, ref b); 114 Console.WriteLine("For i = " + i 115 + " a = " + a 116 + " b = " + b 117 + " TheadID = " + Thread.CurrentThread.GetHashCode()); 118 Thread.Sleep(1000); 119 } 120 } 121 } 122 } 123 #region 拥有独占权的 写的时候 读要等待 124 //在考虑资源访问的时候,惯性上我们会对资源实施lock机制,但是在某些情况下, 125 //我们仅仅需要读取资源的数据,而不是修改资源的数据,在这种情况下获取资源的 126 //独占权无疑会影响运行效率,因此.Net提供了一种机制,使用ReaderWriterLock进行 127 //资源访问时,如果在某一时刻资源并没有获取写的独占权,那么可以获得多个读的访问权, 128 //单个写入的独占权,如果某一时刻已经获取了写入的独占权,那么其它读取的访问权必须进行等待, 129 130 //private static ReaderWriterLock m_readerWriterLock = new ReaderWriterLock(); 131 //private static int m_int = 0; 132 //[STAThread] 133 //static void Main(string[] args) 134 //{ 135 //Thread readThread = new Thread(new ThreadStart(Read)); 136 //readThread.Name = "ReadThread1"; 137 //Thread readThread2 = new Thread(new ThreadStart(Read)); 138 //readThread2.Name = "ReadThread2"; 139 //Thread writeThread = new Thread(new ThreadStart(Writer)); 140 //writeThread.Name = "WriterThread"; 141 //readThread.Start(); 142 //readThread2.Start(); 143 //writeThread.Start(); 144 //readThread.Join(); 145 //readThread2.Join(); 146 //writeThread.Join(); 147 148 //Console.ReadLine(); 149 //} 150 //private static void Read() 151 //{ 152 //while (true) 153 //{ 154 //Console.WriteLine("ThreadName " + Thread.CurrentThread.Name + " AcquireReaderLock"); 155 //m_readerWriterLock.AcquireReaderLock(10000); 156 //Console.WriteLine(String.Format("ThreadName : {0} m_int : {1}", Thread.CurrentThread.Name, m_int)); 157 //m_readerWriterLock.ReleaseReaderLock(); 158 //} 159 //} 160 161 //private static void Writer() 162 //{ 163 //while (true) 164 //{ 165 //Console.WriteLine("ThreadName " + Thread.CurrentThread.Name + " AcquireWriterLock"); 166 //m_readerWriterLock.AcquireWriterLock(1000); 167 //Interlocked.Increment(ref m_int); 168 //Thread.Sleep(5000); 169 //m_readerWriterLock.ReleaseWriterLock(); 170 //Console.WriteLine("ThreadName " + Thread.CurrentThread.Name + " ReleaseWriterLock"); 171 //} 172 //} 173 174 //可以看到,当WriterThread获取到写入独占权后,任何其它读取的线程都必须等待, 175 //直到WriterThread释放掉写入独占权后,才能获取到数据的访问权,应该注意的是, 176 //上述打印信息很明显显示出,可以多个线程同时获取数据的读取权, 177 //这从ReadThread1和ReadThread2的信息交互输出可以看出。 178 #endregion ReadWriteLock 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Runtime.Remoting.Contexts; 6 using System.Threading; 7 8 #region 详解 9 //SynchronizationAttribute 10 // 当我们确定某个类的实例在同一时刻只能被一个线程访问时,我们可以直接将类标识成Synchronization的,这样,CLR会自动对这个类实施同步机制,实际上,这里面涉及到同步域的概念,当类按如下设计时,我们可以确保类的实例无法被多个线程同时访问 11 // 1). 在类的声明中,添加System.Runtime.Remoting.Contexts.SynchronizationAttribute属性。 12 // 2). 继承至System.ContextBoundObject 13 // 需要注意的是,要实现上述机制,类必须继承至System.ContextBoundObject,换句话说,类必须是上下文绑定的。 14 // 一个示范类代码如下: 15 //[System.Runtime.Remoting.Contexts.Synchronization] 16 //public class SynchronizedClass : System.ContextBoundObject 17 //{ 18 19 //} 20 #endregion 21 22 //SynchronizationAttribute对于缺乏同步经验的程序员来说,使用起来很方便。 23 24 namespace _9.SynchronizationAttribute多线程同步代码区 25 { 26 class Program 27 { 28 static void Main(string[] args) 29 { 30 Sy c1 = new Sy(); 31 Thread t1 = new Thread(c1.T1); 32 Thread t2 = new Thread(c1.T2); 33 34 t1.IsBackground = true; 35 t2.IsBackground = true; 36 37 string str1 = "我是线程t1"; 38 string str2 = "我是线程t2"; 39 40 t1.Start(str1); 41 t2.Start(str2); 42 43 while (true) 44 { 45 if (t1.IsAlive == true || t2.IsAlive == true) 46 { 47 //使主线程停顿一下,只是方便理解,不加这段也是一样的, 48 //两个线程没执行完毕时主线程已经停在循环内了 49 Thread.Sleep(1000); 50 } 51 else 52 break; 53 } 54 Console.WriteLine("回车,结束"); 55 Console.Read(); 56 } 57 } 58 [Synchronization] 59 public class Sy : ContextBoundObject //上下文绑定 60 { 61 public void T1(object tt1) 62 { 63 Count(tt1); 64 } 65 public void T2(object tt2) 66 { 67 Count(tt2); 68 } 69 public void Count(object str) 70 { 71 //lock (this) 72 //我之前写过的lock锁,不加lock两个线程会同时执行这个方法,很容易会死锁 73 //我这方法内容少,大内容就容易死锁了。 74 //这样对比就很明显了,不加lock线程也实现了同步,一个线程执行时另个一个线程等待 75 //这就是SynchronizationAttribute它的好处 76 for (int i = 0; i < 5; i++) 77 { 78 Thread.Sleep(500); 79 Console.WriteLine(str as string); 80 } 81 } 82 } 83 } SynchronizationAttribute多线程同步代码区 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace _7._2Mutex验证两个进程内的线程同时访问同一个资源 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 #region 只能运行一个客户端程序 13 bool flag = false; 14 System.Threading.Mutex mutex = new System.Threading.Mutex(true, "Test", out flag); 15 //第一个参数:true--给调用线程赋予互斥体的初始所属权 16 //第一个参数:互斥体的名称 17 //第三个参数:返回值,如果调用线程已被授予互斥体的初始所属权,则返回true 18 if (!flag) 19 { 20 Console.WriteLine("只能运行一个客户端程序!"); 21 Console.Read(); 22 Environment.Exit(1);//退出程序 23 } 24 #endregion 25 26 Console.Read(); 27 } 28 } 29 } 30 31 //------------------------------------------------------------- 32 using System; 33 using System.Collections.Generic; 34 using System.Linq; 35 using System.Text; 36 using System.Threading; 37 38 //C#语言有很多值得学习的地方,这里我们主要介绍C# Mutex对象,包括介绍控制好多个线程相互之间的联系等方面。 39 //如何控制好多个线程相互之间的联系,不产生冲突和重复,这需要用到互斥对象,即:System.Threading 命名空间中的 Mutex 类。 40 //我们可以把Mutex看作一个出租车,乘客看作线程。乘客首先等车,然后上车,最后下车。当一个乘客在车上时,其他乘客就只有 41 //等他下车以后才可以上车。而线程与C# Mutex对象的关系也正是如此,线程使用Mutex.WaitOne()方法等待C# Mutex对象被释放, 42 //如果它等待的C# Mutex对象被释放了,它就自动拥有这个对象,直到它调用Mutex.ReleaseMutex()方法释放这个对象,而在此期间, 43 //其他想要获取这个C# Mutex对象的线程都只有等待。 44 //下面这个例子使用了C# Mutex对象来同步四个线程,主线程等待四个线程的结束,而这四个线程的运行又是与两个C# Mutex对象相关联的。 45 //其中还用到AutoResetEvent类的对象,可以把它理解为一个信号灯。这里用它的有信号状态来表示一个线程的结束。 46 47 #region 这个有些复杂 先不研究 48 namespace ThreadExample 49 { 50 public class MutexSample 51 { 52 static Mutex gM1; 53 static Mutex gM2; 54 const int ITERS = 100; 55 static AutoResetEvent Event1 = new AutoResetEvent(false); 56 static AutoResetEvent Event2 = new AutoResetEvent(false); 57 static AutoResetEvent Event3 = new AutoResetEvent(false); 58 static AutoResetEvent Event4 = new AutoResetEvent(false); 59 60 public static void Main(String[] args) 61 { 62 Console.WriteLine("Mutex Sample "); 63 //创建一个Mutex对象,并且命名为MyMutex 64 gM1 = new Mutex(true, "MyMutex"); 65 //创建一个未命名的Mutex 对象. 66 gM2 = new Mutex(true); 67 Console.WriteLine(" - Main Owns gM1 and gM2"); 68 69 AutoResetEvent[] evs = new AutoResetEvent[4]; 70 evs[0] = Event1; //为后面的线程t1,t2,t3,t4定义AutoResetEvent对象 71 evs[1] = Event2; 72 evs[2] = Event3; 73 evs[3] = Event4; 74 75 MutexSample tm = new MutexSample(); 76 Thread t1 = new Thread(new ThreadStart(tm.t1Start)); 77 Thread t2 = new Thread(new ThreadStart(tm.t2Start)); 78 Thread t3 = new Thread(new ThreadStart(tm.t3Start)); 79 Thread t4 = new Thread(new ThreadStart(tm.t4Start)); 80 t1.Start();// 使用Mutex.WaitAll()方法等待一个Mutex数组中的对象全部被释放 81 t2.Start();// 使用Mutex.WaitOne()方法等待gM1的释放 82 t3.Start();// 使用Mutex.WaitAny()方法等待一个Mutex数组中任意一个对象被释放 83 t4.Start();// 使用Mutex.WaitOne()方法等待gM2的释放 84 85 Thread.Sleep(2000); 86 Console.WriteLine(" - Main releases gM1"); 87 gM1.ReleaseMutex(); //线程t2,t3结束条件满足 88 89 Thread.Sleep(1000); 90 Console.WriteLine(" - Main releases gM2"); 91 gM2.ReleaseMutex(); //线程t1,t4结束条件满足 92 93 //等待所有四个线程结束 94 WaitHandle.WaitAll(evs); 95 Console.WriteLine(" Mutex Sample"); 96 Console.ReadLine(); 97 } 98 99 public void t1Start() 100 { 101 Console.WriteLine("t1Start started, Mutex.WaitAll(Mutex[])"); 102 Mutex[] gMs = new Mutex[2]; 103 gMs[0] = gM1;//创建一个Mutex数组作为Mutex.WaitAll()方法的参数 104 gMs[1] = gM2; 105 Mutex.WaitAll(gMs);//等待gM1和gM2都被释放 106 Thread.Sleep(2000); 107 Console.WriteLine("t1Start finished, Mutex.WaitAll(Mutex[]) satisfied"); 108 Event1.Set(); //线程结束,将Event1设置为有信号状态 109 } 110 public void t2Start() 111 { 112 Console.WriteLine("t2Start started, gM1.WaitOne( )"); 113 gM1.WaitOne();//等待gM1的释放 114 Console.WriteLine("t2Start finished, gM1.WaitOne( ) satisfied"); 115 Event2.Set();//线程结束,将Event2设置为有信号状态 116 } 117 public void t3Start() 118 { 119 Console.WriteLine("t3Start started, Mutex.WaitAny(Mutex[])"); 120 Mutex[] gMs = new Mutex[2]; 121 gMs[0] = gM1;//创建一个Mutex数组作为Mutex.WaitAny()方法的参数 122 gMs[1] = gM2; 123 Mutex.WaitAny(gMs);//等待数组中任意一个Mutex对象被释放 124 Console.WriteLine("t3Start finished, Mutex.WaitAny(Mutex[])"); 125 Event3.Set();//线程结束,将Event3设置为有信号状态 126 } 127 public void t4Start() 128 { 129 Console.WriteLine("t4Start started, gM2.WaitOne( )"); 130 gM2.WaitOne();//等待gM2被释放 131 Console.WriteLine("t4Start finished, gM2.WaitOne( )"); 132 Event4.Set();//线程结束,将Event4设置为有信号状态 133 } 134 } 135 } 136 #endregion Mutex--------不会 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 using System.Runtime.CompilerServices; 7 8 #region 讲解 9 //MethodImplAttribute 10 //如果临界区是跨越整个方法的,也就是说,整个方法内部的代码都需要上锁的话, 11 //使用MethodImplAttribute属性会更简单一些。这样就不用在方法内部加锁了, 12 //只需要在方法上面加上 [MethodImpl(MethodImplOptions.Synchronized)] 就可以了, 13 //MehthodImpl和MethodImplOptions都在命名空间System.Runtime.CompilerServices 里面。 14 //但要注意这个属性会使整个方法加锁,直到方法返回,才释放锁。因此,使用上不太灵活。 15 //如果要提前释放锁,则应该使用Monitor或lock。 16 #endregion 17 18 //我这人比较懒,我还是用lock那个试验来说明如何给方法加锁的 19 namespace _10.MethodImplAttribute给方法加锁 20 { 21 partial class Class1//我没有 写成public 22 { 23 //Class1 c1 = new Class1();//要用public 就实例化 使用lock(c1) 24 public void T1(object tt1) 25 { 26 Count(tt1); 27 } 28 public void T2(object tt2) 29 { 30 Count(tt2); 31 } 32 33 //首先先确定了,要被多个线程访问的共同方法。就是他 34 //然后在该方法头上写上[MethodImpl(MethodImplOptions.Synchronized)] 35 //这样就可以去掉lock锁了,结果是一样的 36 [MethodImpl(MethodImplOptions.Synchronized)] 37 public void Count(object str) 38 { 39 //lock (this) 40 for (int i = 0; i < 5; i++) 41 { 42 Thread.Sleep(500);//可以不写。为了看效果用的 43 Console.WriteLine(str as string); 44 } 45 } 46 } 47 48 class Program 49 { 50 static void Main(string[] args) 51 { 52 Class1 c1 = new Class1(); 53 Thread t1 = new Thread(c1.T1); 54 Thread t2 = new Thread(c1.T2); 55 56 t1.IsBackground = true; 57 t2.IsBackground = true; 58 59 string str1 = "我是线程t1"; 60 string str2 = "我是线程t2"; 61 62 t1.Start(str1); 63 t2.Start(str2); 64 65 while (true) 66 { 67 if (t1.IsAlive == true || t2.IsAlive == true) 68 { 69 //使主线程停顿一下,只是方便理解,不加这段也是一样的, 70 //两个线程没执行完毕时主线程已经停在循环内了 71 Thread.Sleep(1000); 72 } 73 else 74 break; 75 } 76 Console.WriteLine("回车,结束"); 77 Console.Read(); 78 } 79 } 80 } MethodImplAttribute给方法加锁 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 //用lock和Monitor可以很好地起到线程同步的作用,但它们无法实现线程之间传递事件。 7 //如果要实现线程同步的同时,线程之间还要有交互,就要用到同步事件。 8 //同步事件是有两个状态(终止和非终止)的对象,它可以用来激活和挂起线程。 9 10 //AutoResetEvent自动变为非终止,就是说一个AutoResetEvent只能激活一个线程。 11 namespace _12.同步事件AutoResetEvent_线程之间传递事件_ 12 { 13 class Program 14 { 15 const int numIterations = 5; 16 17 static AutoResetEvent myResetEvent = new AutoResetEvent(false); 18 static int number; 19 static void Main() 20 { 21 Thread myReaderThread1 = new Thread(new ThreadStart(MyReadThreadProc1)); 22 Thread myReaderThread2 = new Thread(new ThreadStart(MyReadThreadProc2)); 23 myReaderThread1.Name = "ReaderThread1"; 24 myReaderThread2.Name = "ReaderThread2"; 25 myReaderThread1.Start(); 26 myReaderThread2.Start(); 27 for (int i = 1; i <= numIterations; i++) 28 { 29 Console.WriteLine("Writer thread writing value: {0}", i); 30 number = i; 31 // 发信号,说明值已经被写进去了。这里的意思是说Set是一个发信号的方法。 32 myResetEvent.Set(); 33 myResetEvent.Set(); 34 //怕会误会“是不是就发了一个信号,才一个线程接到的”,我这发两个信号,结果在下面 35 #region 这我就不多解释了,仔细想想就明白了,两个信号不一定是哪个线程接受到了(包括重复接收) 36 //Writer thread writing value: 1 37 //ReaderThread2 reading value: 1 38 //Writer thread writing value: 2 39 //ReaderThread2 reading value: 2 40 //ReaderThread1 reading value: 2 41 //Writer thread writing value: 3 42 //ReaderThread2 reading value: 3 43 //Writer thread writing value: 4 44 //ReaderThread2 reading value: 4 45 //ReaderThread1 reading value: 4 46 //Writer thread writing value: 5 47 //ReaderThread2 reading value: 5 48 #endregion 49 //事实证明AutoResetEvent的特性,不可以写成多发信号,只有一对一 50 51 Thread.Sleep(1000); 52 } 53 myReaderThread1.Abort(); 54 myReaderThread2.Abort(); 55 56 Console.Read(); 57 58 #region 结果是不固定的 我收集了一份,一看便知道AutoResetEvent它的特点了 59 //它发送的信号,只有一个能接收到,不会启动多个线程 60 61 //Writer thread writing value: 1 62 //ReaderThread1 reading value: 1 63 //Writer thread writing value: 2 64 //ReaderThread1 reading value: 2 65 //Writer thread writing value: 3 66 //ReaderThread2 reading value: 3 67 //Writer thread writing value: 4 68 //ReaderThread2 reading value: 4 69 //Writer thread writing value: 5 70 //ReaderThread1 reading value: 5 71 #endregion 72 } 73 static void MyReadThreadProc1() 74 { 75 while (true) 76 { 77 //在数据被作者写入之前不会被读者读取 78 myResetEvent.WaitOne();//接受到信号后,代表 写完可以看了 79 //还有一点就是:收到信号后会自动关闭信号 80 Console.WriteLine("{0} reading value: {1}", Thread.CurrentThread.Name, number); 81 //myResetEvent.Reset();//将其设置为无信号//已经无信号了,可以不用写 82 } 83 } 84 85 static void MyReadThreadProc2() 86 { 87 while (true) 88 { 89 //声明一下:AutoResetEvent里没有WaitAny和WaitAll方法 90 //在数据被作者写入之前不会被读者读取 91 myResetEvent.WaitOne();//接受到信号后,代表 写完可以看了 92 Console.WriteLine("{0} reading value: {1}", Thread.CurrentThread.Name, number); 93 } 94 } 95 } 96 } 同步事件AutoResetEvent、线程之间传递事件一对一 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 7 //将AutoResetEvent里的代码改成下面这样子 8 namespace _12.同步事件ManualResetEvent__线程之间传递事件一对多_ 9 { 10 class Program 11 { 12 const int numIterations = 5; 13 14 static ManualResetEvent myManualResetEvent = new ManualResetEvent(false); 15 static int number; 16 static void Main() 17 { 18 Thread myReaderThread1 = new Thread(new ThreadStart(MyReadThreadProc1)); 19 Thread myReaderThread2 = new Thread(new ThreadStart(MyReadThreadProc2)); 20 myReaderThread1.Name = "ReaderThread1"; 21 myReaderThread2.Name = "ReaderThread2"; 22 myReaderThread1.Start(); 23 myReaderThread2.Start(); 24 for (int i = 1; i <= numIterations; i++) 25 { 26 Console.WriteLine("Writer thread writing value: {0}", i); 27 number = i; 28 // 发信号,说明值已经被写进去了。这里的意思是说Set是一个发信号的方法。 29 myManualResetEvent.Set();//发送信号,两个线程都会收到信号 30 31 Thread.Sleep(1000); 32 } 33 myReaderThread1.Abort(); 34 myReaderThread2.Abort(); 35 36 Console.Read(); 37 } 38 static void MyReadThreadProc1() 39 { 40 while (true) 41 { 42 //在数据被作者写入之前不会被读者读取 43 myManualResetEvent.WaitOne();//接受到信号后,代表 写完可以看了 44 Console.WriteLine("{0} reading value: {1}", Thread.CurrentThread.Name, number); 45 myManualResetEvent.Reset();//将其设置为无信号 46 } 47 } 48 49 static void MyReadThreadProc2() 50 { 51 while (true) 52 { 53 //在数据被作者写入之前不会被读者读取 54 myManualResetEvent.WaitOne();//接受到信号后,代表 写完可以看了 55 //还有一点就是:收到信号后会不自动关闭信号 56 Console.WriteLine("{0} reading value: {1}", Thread.CurrentThread.Name, number); 57 myManualResetEvent.Reset();//将其设置为无信号 58 } 59 } 60 } 61 } 同步事件ManualResetEvent、线程之间传递事件一对多  3.线程池 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 7 //它有一些限制: 8 //1. 线程池中所有线程都是后台线程,如果进程的所有前台线程都结束了,所有的后台线程就会停止。不能把入池的线程改为前台线 程。 9 //2. 不能给入池的线程设置优先级或名称。 10 //3. 对于COM对象,入池的所有线程都是多线程单元(Multi-threaded apartment,MTA)线程。许多COM对象都需要单线程单元(Single -threaded apartment,STA)线程。 11 //4.入池的线程只能用于时间较短的任务。如果线程要一直运行(如Word的拼写检查器线程),就应使用Thread类创建一个线程。 12 //这是个简单的线程池操作,可以扩展利用多线程的同步,异步 13 //由于线程的创建和销毁需要耗费一定的开销,过多的使用线程会造成内存资源的浪费,出于对性能的考虑, 14 //于是引入了线程池的概念。线程池维护一个请求队列,线程池的代码从队列提取任务,然后委派给线程池 15 //的一个线程执行,线程执行完不会被立即销毁,这样既可以在后台执行任务,又可以减少线程创建和销毁 16 //所带来的开销。 17 18 //线程池线程默认为后台线程(IsBackground)。 19 namespace 线程池 20 { 21 class Program 22 { 23 static void Main(string[] args) 24 { 25 //将工作项加入到线程池队列中,这里可以传递一个线程参数 26 ThreadPool.QueueUserWorkItem(TestMethod, "Hello"); 27 Thread.Sleep(1000); 28 29 Console.ReadKey(); 30 } 31 32 public static void TestMethod(object data) 33 { 34 string datastr = data as string; 35 Console.WriteLine(datastr); 36 } 37 } 38 } 线程池 4.异步 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 7 namespace 异步1.线程池异步 8 { 9 class Program 10 { 11 public static void MyMethodA() 12 { 13 for (int i = 0; i < 3; i++) 14 { 15 Console.WriteLine("我是MyMethodA" + i); 16 } 17 } 18 public static void MyMethodB() 19 { 20 for (int i = 0; i < 3; i++) 21 { 22 Console.WriteLine("我是MyMethodB" + i); 23 } 24 } 25 26 static void Main(string[] args) 27 { 28 using (ManualResetEvent m1 = new ManualResetEvent(false)) 29 using (ManualResetEvent m2 = new ManualResetEvent(false)) 30 { 31 ThreadPool.QueueUserWorkItem(delegate 32 { 33 MyMethodA(); 34 m1.Set(); 35 }); 36 ThreadPool.QueueUserWorkItem(delegate 37 { 38 MyMethodB(); 39 m2.Set(); 40 }); 41 WaitHandle.WaitAll(new WaitHandle[] { m1, m2, }); 42 #region 看两次输出的结果,可以看出是异步进行的 43 //1. 44 //我是MyMethodA0 45 //我是MyMethodB0 46 //我是MyMethodB1 47 //我是MyMethodB2 48 //我是MyMethodA1 49 //我是MyMethodA2 50 //2. 51 //我是MyMethodA0 52 //我是MyMethodA1 53 //我是MyMethodB0 54 //我是MyMethodB1 55 //我是MyMethodB2 56 //我是MyMethodA2 57 #endregion 58 } 59 Console.Read(); 60 } 61 } 62 } 线程池异步 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Threading; 7 //Task 的优点以及功能 8 //1、在任务启动后,可以随时以任务延续的形式注册回调。 9 //2、通过使用 ContinueWhenAll 和 ContinueWhenAny 方法或者 WaitAll 方法或 WaitAny 方法,协调多个为了响应 Begin_ 方法而执行的操作。 10 //3、在同一Task 对象中封装异步 I/O 绑定和计算绑定操作。 11 //4、监视Task 对象的状态。 12 //5、使用TaskCompletionSource 将操作的状态封送到Task 对象。 13 namespace 异步2.Task_异步等待_wait_waitall_waitany 14 { 15 class Program 16 { 17 public static void MyMethodA() 18 { 19 for (int i = 0; i < 5; i++) 20 { 21 Console.WriteLine(DateTime.Now.ToString()); 22 Thread.Sleep(1000); 23 } 24 } 25 public static void MyMethodB() 26 { 27 for (int i = 0; i < 5; i++) 28 { 29 Console.WriteLine(DateTime.Now.ToString()); 30 Thread.Sleep(1000); 31 } 32 } 33 34 static void Main(string[] args) 35 { 36 Task t1 = new Task(MyMethodA);//也可以这么写都是一样的,前提要加上t1.Start()才能执行 37 Task t2 = Task.Factory.StartNew(delegate { MyMethodB(); }); 38 t1.Start(); 39 //它是一个一个的等待结束 40 //t1.Wait(); 41 //t2.Wait(); 42 43 //这是同时进行的异步.先把上面的两行注释掉 44 //等待对象完成执行过程 45 Task.WaitAll(t1, t2); 46 //Task.WaitAny(t1, t2);//等待一个线程结束 47 Console.WriteLine("主线程已结束"); 48 Console.Read(); 49 50 #region 不加Wait、WaitAll会怎样输出 51 //主线程已结束 52 //2016/12/8 9:52:34 53 //2016/12/8 9:52:34 54 //2016/12/8 9:52:35 55 //2016/12/8 9:52:35 56 //2016/12/8 9:52:36 57 //2016/12/8 9:52:36 58 //2016/12/8 9:52:37 59 //2016/12/8 9:52:37 60 //2016/12/8 9:52:38 61 //2016/12/8 9:52:38 62 63 //加上它还是有好处的 64 #endregion 65 } 66 } 67 } Task_异步等待_wait_waitall_waitany 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace 异步3.Task捕获异常 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 Task t1 = new Task(MyMethod); 14 t1.Start(); 15 t1.Wait(); 16 //发生一个或多个错误 17 //原因我告诉你: 18 //Task的异常会重新抛到Wait和AllWait中 19 //捕获异常方法很简单还记得try catch吧,把t1.Wait()写成下面的这样就行了 20 //try { t1.Wait();} catch (Exception e) { Console.WriteLine(e); } 21 22 Console.WriteLine("主线程代码运行结束"); 23 Console.ReadLine(); 24 } 25 26 static void MyMethod() 27 { 28 throw new Exception("Task异常测试"); 29 //点击运行时会在这里报异常,因为这是自己设置的异常,不用管他,点击F5继续 30 //你会发现异常停在了t1.Wait()位置 31 } 32 } 33 } Task捕获异常 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace 异步4.Task返回值 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 Task task = new Task(() => 14 { 15 int sum = 0; 16 Console.WriteLine("使用Task执行异步操作."); 17 for (int i = 0; i < 100; i++) 18 { 19 sum += i; 20 } 21 return sum; 22 }); 23 //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler) 24 task.Start(); 25 26 Console.WriteLine("主线程执行其他处理"); 27 //等待任务的完成执行过程。 28 task.Wait(); 29 //获得任务的执行结果 30 Console.WriteLine("任务执行结果:{0}", task.Result.ToString()); 31 32 Console.Read(); 33 } 34 } 35 } Task返回值 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 using System.Threading.Tasks; 7 8 //使用CancellationTokenSource对象需要与Task对象进行配合使用,Task会对当前运行的状态进行控制 9 //(这个不用我们关心是如何孔控制的)。而CancellationTokenSource则是外部对Task的控制, 10 //如取消、定时取消。 11 12 //Task.Factory.StartNew 创建并启动了 MyTask 方法,并传递了一个 CancellationTokenSource.Token 13 //对象进去。我们可以通过在外部CancellationTokenSource对象进行控制是否取消任务的运行。 14 //当在 MyTask 中的 cancelTokenSource.IsCancellationRequested 判断如果是取消了任务的话 15 //就跳出while循环执行。也就结束了任务 16 namespace 异步5.CancellationTokenSource_终止线程 17 { 18 class Program 19 { 20 //声明CancellationTokenSource对象 21 static CancellationTokenSource cancelTokenSource = new CancellationTokenSource(); 22 23 //程序入口 24 static void Main(string[] args) 25 { 26 ; 27 Task.Factory.StartNew(MyTask, cancelTokenSource.Token); 28 29 Console.WriteLine("请按回车键(Enter)停止"); 30 Console.ReadLine(); 31 32 cancelTokenSource.Cancel(); 33 34 Console.WriteLine("已停止"); 35 Console.ReadLine(); 36 } 37 38 //测试方法 39 static void MyTask() 40 { 41 //判断是否取消任务 42 while (!cancelTokenSource.IsCancellationRequested) 43 { 44 Console.WriteLine(DateTime.Now); 45 Thread.Sleep(1000); 46 } 47 } 48 } 49 } Task内CancellationTokenSource 终止单个线程 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 using System.Threading.Tasks; 7 8 namespace 异步5._2.CancellationTokenSource_终止多个线程 9 { 10 class Program 11 { 12 //声明CancellationTokenSource对象 13 static CancellationTokenSource c1 = new CancellationTokenSource(); 14 static CancellationTokenSource c2 = new CancellationTokenSource(); 15 static CancellationTokenSource c3 = new CancellationTokenSource(); 16 17 //使用多个CancellationTokenSource进行复合管理 18 static CancellationTokenSource compositeCancel = CancellationTokenSource.CreateLinkedTokenSource(c1.Token, c2.Token, c3.Token); 19 20 //程序入口 21 static void Main(string[] args) 22 { 23 //Task.Factory.StartNew(MyTask, compositeCancel.Token); 24 Task.Factory.StartNew(MyTask1, c1.Token); 25 Task.Factory.StartNew(MyTask2, c2.Token); 26 Task.Factory.StartNew(MyTask3, c3.Token); 27 28 Console.WriteLine("请按回车键(Enter)停止"); 29 Console.ReadLine(); 30 31 //任意一个 CancellationTokenSource 取消任务,那么所有任务都会被取消。 32 c1.Cancel(); 33 34 Console.WriteLine("已停止"); 35 Console.ReadLine(); 36 } 37 38 //测试方法 39 static void MyTask1() 40 { 41 //判断是否取消任务 42 while (!compositeCancel.IsCancellationRequested) 43 { 44 Console.WriteLine("C1"); 45 Thread.Sleep(1000); 46 } 47 } 48 static void MyTask2() 49 { 50 //判断是否取消任务 51 while (!compositeCancel.IsCancellationRequested) 52 { 53 Console.WriteLine("C2"); 54 Thread.Sleep(1000); 55 } 56 } 57 static void MyTask3() 58 { 59 //判断是否取消任务 60 while (!compositeCancel.IsCancellationRequested) 61 { 62 Console.WriteLine("C3"); 63 Thread.Sleep(1000); 64 } 65 } 66 } 67 } Task内CancellationTokenSource 终止多个线程 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 //在使用能够Task类的Wait方法等待一个任务时或派生类的Result属性获得任务 7 //执行结果都有可能阻塞线程,为了解决这个问题可以使用ContinueWith方法, 8 //他能在一个任务完成时自动启动一个新的任务来处理执行结果。 9 namespace _6.ContinueWith方法在任务完成时启动一个新任务 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 //创建一个任务 16 Task task = new Task(() => 17 { 18 int sum = 0; 19 Console.WriteLine("使用Task执行异步操作."); 20 for (int i = 0; i < 100; i++) 21 { 22 sum += i; 23 } 24 return sum; 25 }); 26 //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler) 27 task.Start(); 28 Console.WriteLine("主线程执行其他处理"); 29 #region 解释这段的意思 30 //我来详细的解释下这个地方: 31 // 1.先明确上面Task创建的线程和主线程都在开始工作 32 // 2.我想得到Task任务运行结果,假如我直接写个方法输出它的结果 33 // 想象一下,假如Task任务还没结束你就要输出它的结果了 34 // 那结果能准确吗? 35 // 假如我不用方法输出它的结果,我建个线程来输出它的结果 36 // 想象一下,Task任务和新建的线程同时运行中, 37 // 一个在时时刻刻更新数据一个在时时刻刻读出它的数据 38 // 相当于两个线程撞车了,阻塞。 39 // 用到ContinueWith方法来解决这个问题 40 //ContinueWith方法在任务完成时启动一个新任务 41 #endregion 42 //任务完成时执行处理。 43 Task cwt = task.ContinueWith(t => 44 { 45 Console.WriteLine("任务完成后的执行结果:{0}", t.Result.ToString()); 46 }); 47 Console.Read(); 48 } 49 } 50 } Task内ContinueWith方法在任务完成时启动一个新任务 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 using System.Threading.Tasks; 7 8 namespace 异步7.Task内TaskFactory类_创建一组具有相同状态的任务_ 9 { 10 class Program 11 { 12 static CancellationTokenSource cts = new CancellationTokenSource(); 13 //我们还可以使用计时取消任务,当一个任务超过了我们所设定的时间然后自动取消该任务的执行 14 //把上面的一行注释掉换成下面的这一样,会发现3s后自动停止 15 //static CancellationTokenSource cts = new CancellationTokenSource(3000); 16 //前提要在.net4.5之上的版本,我的是.net4.0 我使用不了 17 18 static void Main(string[] args) 19 { 20 string s1 = DateTime.Now.ToString("mmss");//yyyy-MM-dd HH24:mm:ss 21 string s2 = string.Empty; 22 Task parent = new Task(() => 23 { 24 //CancellationTokenSource cts = new CancellationTokenSource(5000); 25 26 //创建任务工厂 27 TaskFactory tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); 28 //添加一组具有相同状态的子任务 29 Task[] task = new Task[]{ 30 tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第一个任务。"); }), 31 tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第二个任务。"); }), 32 tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第三个任务。"); }), 33 tf.StartNew(() => { for (int i = 0; i < 10; i++)//这个区间大于5s,看看能不能自动关闭 34 { 35 if(!cts.IsCancellationRequested)//千万不要忘了写这句 36 { 37 Console.WriteLine("已经运行"+i+"s"); 38 Thread.Sleep(1000); 39 } 40 } }) 41 }; 42 }); 43 parent.Start(); 44 45 //.net4.0不支持定时操作,我自己写个定时关闭 46 while (true) 47 { 48 s2 = DateTime.Now.ToString("mmss"); 49 int i = int.Parse(s2) - int.Parse(s1); 50 51 if (i > 5)//运行时间规定5s钟,超时自动关闭 52 { 53 cts.Cancel(); 54 Console.WriteLine("已经运行" + i + "s" + " 用时超过5s" + " 5s后将自动关闭系统"); 55 56 Thread.Sleep(5000); 57 break; 58 #region 运行结果 59 //已经运行0s 60 //我是任务工厂里的第一个任务。 61 //我是任务工厂里的第二个任务。 62 //我是任务工厂里的第三个任务。 63 //已经运行1s 64 //已经运行2s 65 //已经运行3s 66 //已经运行4s 67 //已经运行5s 68 //已经运行6s 用时超过5s 5s后将自动关闭系统 69 #endregion 70 } 71 } 72 } 73 } 74 } Task内TaskFactory类(创建一组具有相同状态的任务)外加定时关闭 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Threading.Tasks; 10 using System.Threading; 11 12 #region 不会 13 //任务内部实现和任务调度 14 15 //1、任务内部有一组构成任务状态的属性,标识任务的唯一Id、表示任务的执行状态(TaskStatus)、任务创建时提供的回调函数的引用和传递给回调函数的数据对象AsyncState、对任务创建时的任务调度对象(TaskScheduler)的引用、对父任务的引用以及对执行上下文的引用和ManualResetEventSlim对象的引用。 16 //2、Task类和Task类都实现了标准的释放资源的接口,允许在任务完成处理的时候使用Dispose方法释放资源(关闭ManualResetEventSlim对象实例)。 17 //3、可以使用Task类的CurrentId属性获得正在执行的任务的Id,如果没有任务在执行CurrentId返回值为null,CurrentId是一个int?可空类型的属性。 18 //4、任务执行的生命周期通过TaskStatus类型的一个值来表示,TaskStatus所包含的值: 19 20 21 //public enum TaskStatus 22 //{ 23 // Created = 0, 24 // WaitingForActivation = 1, 25 // WaitingToRun = 2, 26 // Running = 3, 27 // WaitingForChildrenToComplete = 4, 28 // RanToCompletion = 5, 29 // Canceled = 6, 30 // Faulted = 7, 31 //} 32 33 //5、在任务内部由TaskScheduler类调度任务的执行,该类是一个抽象类,FCL中从他派生了两个派生类: 34 //ThreadPoolTaskScheduler线程池任务调度器 35 //所有任务默认都是采用ThreadPoolTaskScheduler调度任务,他是采用线程池来执行任务,可以通过TaskScheduler类的静态属性Default获得对默认任务调度器的引用。 36 //SynchronizationContextTaskScheduler同步上下文任务调度器 37 //SynchronizationContextTaskScheduler任务调度器能够用在Window form、WPF等应用程序,他的任务调度是采用的GUI线程,所以他能同步更新UI组件,可以通过TaskScheduler类的静态方法FromCurrentSynchronizationContext获得对一个同步上下文任务调度起的引用。 38 //6、例如 39 40 41 //private void button1_Click(object sender, EventArgs e) 42 //{ 43 // //获得同步上下文任务调度器 44 // TaskScheduler m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 45 46 // //创建任务,并采用默认任务调度器(线程池任务调度器)执行任务 47 // Task task = new Task(() => 48 // { 49 // //执行复杂的计算任务。 50 // Thread.Sleep(2000); 51 // int sum = 0; 52 // for (int i = 0; i < 100; i++) 53 // { 54 // sum += i; 55 // } 56 // return sum; 57 // }); 58 // var cts=new CancellationTokenSource(); 59 // //任务完成时启动一个后续任务,并采用同步上下文任务调度器调度任务更新UI组件。 60 // task.ContinueWith(t => {this.label1.Text="采用SynchronizationContextTaskScheduler任务调度器更新UI。\\r\\n计算结果是:"+task.Result.ToString(); }, 61 // cts.Token ,TaskContinuationOptions.AttachedToParent,m_syncContextTaskScheduler); 62 // task.Start(); 63 // } 64 #endregion 65 66 namespace WindowsFormsApplication1 67 { 68 public partial class Form1 : Form 69 { 70 public Form1() 71 { 72 InitializeComponent(); 73 } 74 75 static CancellationTokenSource cts; 76 private void button1_Click(object sender, EventArgs e) 77 { 78 label1.Text = ""; 79 //获得同步上下文任务调度器 80 TaskScheduler m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 81 82 //创建任务,并采用默认任务调度器(线程池任务调度器)执行任务 83 Task task = new Task(() => 84 { 85 //执行复杂的计算任务。 86 Thread.Sleep(2000); 87 int sum = 0; 88 for (int i = 0; i < 1000; i++) 89 { 90 sum += i; 91 } 92 return sum; 93 }); 94 var cts = new CancellationTokenSource(); 95 //任务完成时启动一个后续任务,并采用同步上下文任务调度器调度任务更新UI组件。 96 task.ContinueWith(t => { this.label1.Text = "采用SynchronizationContextTaskScheduler任务调度器更新UI。\\r\\n计算结果是:" + task.Result.ToString(); },//this.label1.Text="采用SynchronizationContextTaskScheduler任务调度器更新UI。\\r\\n计算结果是:"+task.Result.ToString(); 97 cts.Token, TaskContinuationOptions.AttachedToParent, m_syncContextTaskScheduler); 98 task.Start(); 99 } 100 } 101 } 任务内部实现和任务调度-------------(不会)  三、进程 进程简单操作: 1.查找进程_结束进程 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 using System.Threading; 7 8 namespace 查找进程_结束进程 9 { 10 class Program 11 { 12 static int ii = 0; 13 static void Main(string[] args) 14 { 15 Process[] pro = Process.GetProcesses();//获取已开启的所有进程 16 //可以改成坑人程序,把所有的进程全部关闭 17 //遍历所有查找到的进程 18 for (int i = 0; i < pro.Length; i++) 19 { 20 //判断此进程是否是要查找的进程 21 if (string.Compare(pro[i].ProcessName.ToString(), "想要结束的进程", true) == 0) 22 {//compare是不区分大小写的 23 pro[i].Kill();//结束进程 24 Console.WriteLine("进程结束"); 25 ii++; 26 } 27 } 28 if (ii == 0) 29 { 30 Console.WriteLine("没有这个进程"); 31 } 32 Console.Read(); 33 } 34 } 35 } 查找进程_结束进程 2. 进程打开指定文件 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 7 namespace 进程打开指定文件 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 //ProcessStartInfo psi = new ProcessStartInfo("字符串的知识.txt"); 14 //Process p = new Process(); 15 //p.StartInfo = psi; 16 //p.Start(); 17 18 //这两种是一样的,下面的这个比较简单一些 19 //Process p;//实例化一个Process对象 20 //p = Process.Start("字符串的知识.txt");//要开启的进程(或 要启用的程序),括号内为绝对路径 21 22 //我个人喜欢这么写,比较懒 23 //Process.Start("字符串的知识.txt"); 24 //打开我的vs软件 25 Process.Start(@"D:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe"); 26 27 //p.Kill();//结束进程 28 } 29 } 30 } 进程打开指定文件  进程间通信:  1.自定义消息进程间通信   1)、一个项目上的多进程通信       1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Runtime.InteropServices; 10 using System.Diagnostics; 11 12 #region 简单介绍下 0x0400 13 // 我们不仅可以传递系统已经定义好的消息,还可以传递自定义的消息(只需要发送消息端和 14 //接收消息端对自定义的消息值统一即可)。下面的发送和接收端同时演示了系统消息和自定义的消息。 15 //消息统一采用4位16进制的数。 16 //系统消息使用的是0x0100(WM_KEYDOWN);0x0000--0x0400是系统自定义的消息,其中0x0000为WM_NULL, 17 //0x0400为WM_USER。0x0400以后的数值我们可以作为自定义的消息值。 18 19 //简单记法:0x0400是个分界值,小于它是系统自定义的消息,大于它是用户自定义消息!! 20 #endregion 21 22 namespace 消息传递_发_ 23 { 24 public partial class Form1 : Form 25 { 26 public Form1() 27 { 28 InitializeComponent(); 29 } 30 31 [DllImport("User32.dll", EntryPoint = "SendMessage")] 32 private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam); 33 34 ProcessStartInfo startInfo = new ProcessStartInfo(); 35 Process pro = new Process(); 36 37 private void button1_Click(object sender, EventArgs e) 38 { 39 startInfo.FileName = @"消息传递(接).exe"; 40 pro.StartInfo = startInfo; 41 pro.Start(); 42 } 43 44 private void button2_Click(object sender, EventArgs e) 45 { 46 pro.Kill(); 47 } 48 49 //系统消息0x0100 50 //不知道对不对,错误了,请提醒 51 /*是系统发出来的消息,感觉很奇怪明明是自己输入的值 52 其实这输入完,点击“发送(系统消息)”时,系统利用这个窗体的句柄 53 把点击过的按键都存储了起来。将这一串键盘值组织成一个字符串*/ 54 private void button3_Click(object sender, EventArgs e) 55 { 56 IntPtr hWnd = pro.MainWindowHandle; 57 int data = Convert.ToInt32(this.textBox1.Text); 58 59 SendMessage(hWnd, 0x0100, data, 0); 60 } 61 62 //自己自定义的消息 大于0x0400 63 /* 这有个弯好好想,这是自己定义的消息,在“消息传递(接).exe”程序里 64 重写了接收处理函数defproc,当发送出去的数字,重写的方法里都会响应到。 65 不要迷茫为什么这个就是自己自定义消息,上面的是系统消息,因为0x0400是个分界值*/ 66 private void button4_Click(object sender, EventArgs e) 67 { 68 IntPtr hWnd = pro.MainWindowHandle; 69 int data = Convert.ToInt32(this.textBox1.Text); 70 71 //SendMessage(hWnd, Message.WM_TEST, data, 0); 72 SendMessage(hWnd, Message.WM_TEST, data, 0); 73 } 74 } 75 class Message 76 { 77 public const int USER = 0x0400; 78 //刚开始我也很纳闷,为什么都是固定的16进制呢,我上网一查,原来固定的值有256个,我给复制在下方了 79 public const int WM_TEST = USER + 101; 80 public const int WM_MSG = USER + 102; 81 } 82 } 83 #region C#SendMessage用法 84 // 函数功能:该函数将指定的消息发送到一个或多个窗口。此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回。该函数是应用程序和应用程序之间进行消息传递的主要手段之一。 85 // 函数原型:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam); 86 87 // 参数: 88 89 // hWnd:其窗口程序将接收消息的窗口的句柄。如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。 90 91 // Msg:指定被发送的消息。 92 93 // wParam:指定附加的消息指定信息。 94 95 // IParam:指定附加的消息指定信息。 96 97 // 返回值:返回值指定消息处理的结果,依赖于所发送的消息。 98 99 // 备注:需要用HWND_BROADCAST通信的应用程序应当使用函数RegisterWindowMessage来为应用程序间的通信取得一个唯一的消息。 100 101 // 如果指定的窗口是由调用线程创建的,则窗口程序立即作为子程序调用。如果指定的窗口是由不同线程创建的,则系统切换到该线程并调用恰当的窗口程序。线程间的消息只有在线程执行消息检索代码时才被处理。发送线程被阻塞直到接收线程处理完消息为止。 102 103 //C#中使用该函数首先导入命名空间: 104 //using System.Runtime.InteropServices; 105 106 //然后写API引用部分的代码,放入 class 内部 107 //[DllImport("user32.dll", EntryPoint = "SendMessage")] 108 // private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam); 109 110 //这个函数有四个参数,第一个是窗口句柄,窗口可以是任何类型的屏幕对象;第二个是用于区别其他消息的常量值;第三个通常是一个与消息有关的常量值,也可能是窗口或控件的句柄,第三个参数是可选参数,有的消息要,有的不需要,比如单击就不需要这个参数, 111 // 别的消息,比如鼠标移动的可能需要在这里加上一些鼠标的参数;第四个通常是一个指向内存中数据的指针。在C#中消息需要定义成windows系统中的原始的16进制数字,比如 const int WM_Lbutton = 0x201; //定义了鼠标的左键点击消息。详细值在最后。 112 113 //例如: 114 //const int BM_CLICK = 0xF5; 115 // IntPtr maindHwnd = FindWindow(null, "QQ用户登录"); //获得QQ登陆框的句柄 116 //if (maindHwnd != IntPtr.Zero) 117 // { 118 // IntPtr childHwnd = FindWindowEx(maindHwnd, IntPtr.Zero, null, "登录"); //获得按钮的句柄 119 // if (childHwnd != IntPtr.Zero) 120 // { 121 // SendMessage(childHwnd, BM_CLICK, 0, 0); //发送点击按钮的消息 122 // } 123 // else 124 // { 125 // MessageBox.Show("没有找到子窗口"); 126 // } 127 // } 128 //else 129 // { 130 // MessageBox.Show("没有找到窗口"); 131 // } 132 133 //wMsg参数常量值: 134 135 // //创建一个窗口 136 //const int WM_CREATE = 0x01; 137 // //当一个窗口被破坏时发送 138 //const int WM_DESTROY = 0x02; 139 // //移动一个窗口 140 //const int WM_MOVE = 0x03; 141 // //改变一个窗口的大小 142 //const int WM_SIZE = 0x05; 143 // //一个窗口被激活或失去激活状态 144 //const int WM_ACTIVATE = 0x06; 145 // //一个窗口获得焦点 146 //const int WM_SETFOCUS = 0x07; 147 // //一个窗口失去焦点 148 //const int WM_KILLFOCUS = 0x08; 149 // //一个窗口改变成Enable状态 150 //const int WM_ENABLE = 0x0A; 151 // //设置窗口是否能重画 152 //const int WM_SETREDRAW = 0x0B; 153 // //应用程序发送此消息来设置一个窗口的文本 154 //const int WM_SETTEXT = 0x0C; 155 // //应用程序发送此消息来复制对应窗口的文本到缓冲区 156 //const int WM_GETTEXT = 0x0D; 157 // //得到与一个窗口有关的文本的长度(不包含空字符) 158 //const int WM_GETTEXTLENGTH = 0x0E; 159 // //要求一个窗口重画自己 160 //const int WM_PAINT = 0x0F; 161 // //当一个窗口或应用程序要关闭时发送一个信号 162 //const int WM_CLOSE = 0x10; 163 // //当用户选择结束对话框或程序自己调用ExitWindows函数 164 //const int WM_QUERYENDSESSION = 0x11; 165 // //用来结束程序运行 166 //const int WM_QUIT = 0x12; 167 // //当用户窗口恢复以前的大小位置时,把此消息发送给某个图标 168 //const int WM_QUERYOPEN = 0x13; 169 // //当窗口背景必须被擦除时(例在窗口改变大小时) 170 //const int WM_ERASEBKGND = 0x14; 171 // //当系统颜色改变时,发送此消息给所有顶级窗口 172 //const int WM_SYSCOLORCHANGE = 0x15; 173 // //当系统进程发出WM_QUERYENDSESSION消息后,此消息发送给应用程序,通知它对话是否结束 174 //const int WM_ENDSESSION = 0x16; 175 // //当隐藏或显示窗口是发送此消息给这个窗口 176 //const int WM_SHOWWINDOW = 0x18; 177 // //发此消息给应用程序哪个窗口是激活的,哪个是非激活的 178 //const int WM_ACTIVATEAPP = 0x1C; 179 // //当系统的字体资源库变化时发送此消息给所有顶级窗口 180 //const int WM_FONTCHANGE = 0x1D; 181 // //当系统的时间变化时发送此消息给所有顶级窗口 182 //const int WM_TIMECHANGE = 0x1E; 183 // //发送此消息来取消某种正在进行的摸态(操作) 184 //const int WM_CANCELMODE = 0x1F; 185 // //如果鼠标引起光标在某个窗口中移动且鼠标输入没有被捕获时,就发消息给某个窗口 186 //const int WM_SETCURSOR = 0x20; 187 // //当光标在某个非激活的窗口中而用户正按着鼠标的某个键发送此消息给//当前窗口 188 //const int WM_MOUSEACTIVATE = 0x21; 189 // //发送此消息给MDI子窗口//当用户点击此窗口的标题栏,或//当窗口被激活,移动,改变大小 190 //const int WM_CHILDACTIVATE = 0x22; 191 // //此消息由基于计算机的训练程序发送,通过WH_JOURNALPALYBACK的hook程序分离出用户输入消息 192 //const int WM_QUEUESYNC = 0x23; 193 // //此消息发送给窗口当它将要改变大小或位置 194 //const int WM_GETMINMAXINFO = 0x24; 195 // //发送给最小化窗口当它图标将要被重画 196 //const int WM_PAINTICON = 0x26; 197 // //此消息发送给某个最小化窗口,仅//当它在画图标前它的背景必须被重画 198 //const int WM_ICONERASEBKGND = 0x27; 199 // //发送此消息给一个对话框程序去更改焦点位置 200 //const int WM_NEXTDLGCTL = 0x28; 201 // //每当打印管理列队增加或减少一条作业时发出此消息 202 //const int WM_SPOOLERSTATUS = 0x2A; 203 // //当button,combobox,listbox,menu的可视外观改变时发送 204 //const int WM_DRAWITEM = 0x2B; 205 // //当button, combo box, list box, list view control, or menu item 被创建时 206 //const int WM_MEASUREITEM = 0x2C; 207 // //此消息有一个LBS_WANTKEYBOARDINPUT风格的发出给它的所有者来响应WM_KEYDOWN消息 208 //const int WM_VKEYTOITEM = 0x2E; 209 // //此消息由一个LBS_WANTKEYBOARDINPUT风格的列表框发送给他的所有者来响应WM_CHAR消息 210 //const int WM_CHARTOITEM = 0x2F; 211 // //当绘制文本时程序发送此消息得到控件要用的颜色 212 //const int WM_SETFONT = 0x30; 213 // //应用程序发送此消息得到当前控件绘制文本的字体 214 //const int WM_GETFONT = 0x31; 215 // //应用程序发送此消息让一个窗口与一个热键相关连 216 //const int WM_SETHOTKEY = 0x32; 217 // //应用程序发送此消息来判断热键与某个窗口是否有关联 218 //const int WM_GETHOTKEY = 0x33; 219 // //此消息发送给最小化窗口,当此窗口将要被拖放而它的类中没有定义图标,应用程序能返回一个图标或光标的句柄,当用户拖放图标时系统显示这个图标或光标 220 //const int WM_QUERYDRAGICON = 0x37; 221 // //发送此消息来判定combobox或listbox新增加的项的相对位置 222 //const int WM_COMPAREITEM = 0x39; 223 // //显示内存已经很少了 224 //const int WM_COMPACTING = 0x41; 225 // //发送此消息给那个窗口的大小和位置将要被改变时,来调用setwindowpos函数或其它窗口管理函数 226 //const int WM_WINDOWPOSCHANGING = 0x46; 227 // //发送此消息给那个窗口的大小和位置已经被改变时,来调用setwindowpos函数或其它窗口管理函数 228 //const int WM_WINDOWPOSCHANGED = 0x47; 229 // //当系统将要进入暂停状态时发送此消息 230 //const int WM_POWER = 0x48; 231 // //当一个应用程序传递数据给另一个应用程序时发送此消息 232 //const int WM_COPYDATA = 0x4A; 233 // //当某个用户取消程序日志激活状态,提交此消息给程序 234 //const int WM_CANCELJOURNA = 0x4B; 235 // //当某个控件的某个事件已经发生或这个控件需要得到一些信息时,发送此消息给它的父窗口 236 //const int WM_NOTIFY = 0x4E; 237 // //当用户选择某种输入语言,或输入语言的热键改变 238 //const int WM_INPUTLANGCHANGEREQUEST = 0x50; 239 // //当平台现场已经被改变后发送此消息给受影响的最顶级窗口 240 //const int WM_INPUTLANGCHANGE = 0x51; 241 // //当程序已经初始化windows帮助例程时发送此消息给应用程序 242 //const int WM_TCARD = 0x52; 243 // //此消息显示用户按下了F1,如果某个菜单是激活的,就发送此消息个此窗口关联的菜单,否则就发送给有焦点的窗口,如果//当前都没有焦点,就把此消息发送给//当前激活的窗口 244 //const int WM_HELP = 0x53; 245 // //当用户已经登入或退出后发送此消息给所有的窗口,//当用户登入或退出时系统更新用户的具体设置信息,在用户更新设置时系统马上发送此消息 246 //const int WM_USERCHANGED = 0x54; 247 // //公用控件,自定义控件和他们的父窗口通过此消息来判断控件是使用ANSI还是UNICODE结构 248 //const int WM_NOTIFYFORMAT = 0x55; 249 // //当用户某个窗口中点击了一下右键就发送此消息给这个窗口 250 // //const int WM_CONTEXTMENU = ??; 251 // //当调用SETWINDOWLONG函数将要改变一个或多个 窗口的风格时发送此消息给那个窗口 252 //const int WM_STYLECHANGING = 0x7C; 253 // //当调用SETWINDOWLONG函数一个或多个 窗口的风格后发送此消息给那个窗口 254 //const int WM_STYLECHANGED = 0x7D; 255 // //当显示器的分辨率改变后发送此消息给所有的窗口 256 //const int WM_DISPLAYCHANGE = 0x7E; 257 // //此消息发送给某个窗口来返回与某个窗口有关连的大图标或小图标的句柄 258 //const int WM_GETICON = 0x7F; 259 // //程序发送此消息让一个新的大图标或小图标与某个窗口关联 260 //const int WM_SETICON = 0x80; 261 // //当某个窗口第一次被创建时,此消息在WM_CREATE消息发送前发送 262 //const int WM_NCCREATE = 0x81; 263 // //此消息通知某个窗口,非客户区正在销毁 264 //const int WM_NCDESTROY = 0x82; 265 // //当某个窗口的客户区域必须被核算时发送此消息 266 //const int WM_NCCALCSIZE = 0x83; 267 // //移动鼠标,按住或释放鼠标时发生 268 //const int WM_NCHITTEST = 0x84; 269 // //程序发送此消息给某个窗口当它(窗口)的框架必须被绘制时 270 //const int WM_NCPAINT = 0x85; 271 // //此消息发送给某个窗口仅当它的非客户区需要被改变来显示是激活还是非激活状态 272 //const int WM_NCACTIVATE = 0x86; 273 // //发送此消息给某个与对话框程序关联的控件,widdows控制方位键和TAB键使输入进入此控件通过应 274 //const int WM_GETDLGCODE = 0x87; 275 // //当光标在一个窗口的非客户区内移动时发送此消息给这个窗口 非客户区为:窗体的标题栏及窗 的边框体 276 //const int WM_NCMOUSEMOVE = 0xA0; 277 // //当光标在一个窗口的非客户区同时按下鼠标左键时提交此消息 278 //const int WM_NCLBUTTONDOWN = 0xA1; 279 // //当用户释放鼠标左键同时光标某个窗口在非客户区十发送此消息 280 //const int WM_NCLBUTTONUP = 0xA2; 281 // //当用户双击鼠标左键同时光标某个窗口在非客户区十发送此消息 282 //const int WM_NCLBUTTONDBLCLK = 0xA3; 283 // //当用户按下鼠标右键同时光标又在窗口的非客户区时发送此消息 284 //const int WM_NCRBUTTONDOWN = 0xA4; 285 // //当用户释放鼠标右键同时光标又在窗口的非客户区时发送此消息 286 //const int WM_NCRBUTTONUP = 0xA5; 287 // //当用户双击鼠标右键同时光标某个窗口在非客户区十发送此消息 288 //const int WM_NCRBUTTONDBLCLK = 0xA6; 289 // //当用户按下鼠标中键同时光标又在窗口的非客户区时发送此消息 290 //const int WM_NCMBUTTONDOWN = 0xA7; 291 // //当用户释放鼠标中键同时光标又在窗口的非客户区时发送此消息 292 //const int WM_NCMBUTTONUP = 0xA8; 293 // //当用户双击鼠标中键同时光标又在窗口的非客户区时发送此消息 294 //const int WM_NCMBUTTONDBLCLK = 0xA9; 295 // //WM_KEYDOWN 按下一个键 296 //const int WM_KEYDOWN = 0x0100; 297 // //释放一个键 298 //const int WM_KEYUP = 0x0101; 299 // //按下某键,并已发出WM_KEYDOWN, WM_KEYUP消息 300 //const int WM_CHAR = 0x102; 301 // //当用translatemessage函数翻译WM_KEYUP消息时发送此消息给拥有焦点的窗口 302 //const int WM_DEADCHAR = 0x103; 303 // //当用户按住ALT键同时按下其它键时提交此消息给拥有焦点的窗口 304 //const int WM_SYSKEYDOWN = 0x104; 305 // //当用户释放一个键同时ALT 键还按着时提交此消息给拥有焦点的窗口 306 //const int WM_SYSKEYUP = 0x105; 307 // //当WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函数翻译后提交此消息给拥有焦点的窗口 308 //const int WM_SYSCHAR = 0x106; 309 // //当WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函数翻译后发送此消息给拥有焦点的窗口 310 //const int WM_SYSDEADCHAR = 0x107; 311 // //在一个对话框程序被显示前发送此消息给它,通常用此消息初始化控件和执行其它任务 312 //const int WM_INITDIALOG = 0x110; 313 // //当用户选择一条菜单命令项或当某个控件发送一条消息给它的父窗口,一个快捷键被翻译 314 //const int WM_COMMAND = 0x111; 315 // //当用户选择窗口菜单的一条命令或//当用户选择最大化或最小化时那个窗口会收到此消息 316 //const int WM_SYSCOMMAND = 0x112; 317 // //发生了定时器事件 318 //const int WM_TIMER = 0x113; 319 // //当一个窗口标准水平滚动条产生一个滚动事件时发送此消息给那个窗口,也发送给拥有它的控件 320 //const int WM_HSCROLL = 0x114; 321 // //当一个窗口标准垂直滚动条产生一个滚动事件时发送此消息给那个窗口也,发送给拥有它的控件 322 //const int WM_VSCROLL = 0x115; 323 // //当一个菜单将要被激活时发送此消息,它发生在用户菜单条中的某项或按下某个菜单键,它允许程序在显示前更改菜单 324 //const int WM_INITMENU = 0x116; 325 // //当一个下拉菜单或子菜单将要被激活时发送此消息,它允许程序在它显示前更改菜单,而不要改变全部 326 //const int WM_INITMENUPOPUP = 0x117; 327 // //当用户选择一条菜单项时发送此消息给菜单的所有者(一般是窗口) 328 //const int WM_MENUSELECT = 0x11F; 329 // //当菜单已被激活用户按下了某个键(不同于加速键),发送此消息给菜单的所有者 330 //const int WM_MENUCHAR = 0x120; 331 // //当一个模态对话框或菜单进入空载状态时发送此消息给它的所有者,一个模态对话框或菜单进入空载状态就是在处理完一条或几条先前的消息后没有消息它的列队中等待 332 //const int WM_ENTERIDLE = 0x121; 333 // //在windows绘制消息框前发送此消息给消息框的所有者窗口,通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置消息框的文本和背景颜色 334 //const int WM_CTLCOLORMSGBOX = 0x132; 335 // //当一个编辑型控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置编辑框的文本和背景颜色 336 //const int WM_CTLCOLOREDIT = 0x133; 337 338 // //当一个列表框控件将要被绘制前发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置列表框的文本和背景颜色 339 //const int WM_CTLCOLORLISTBOX = 0x134; 340 // //当一个按钮控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置按纽的文本和背景颜色 341 //const int WM_CTLCOLORBTN = 0x135; 342 // //当一个对话框控件将要被绘制前发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置对话框的文本背景颜色 343 //const int WM_CTLCOLORDLG = 0x136; 344 // //当一个滚动条控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置滚动条的背景颜色 345 //const int WM_CTLCOLORSCROLLBAR = 0x137; 346 // //当一个静态控件将要被绘制时发送此消息给它的父窗口通过响应这条消息,所有者窗口可以 通过使用给定的相关显示设备的句柄来设置静态控件的文本和背景颜色 347 //const int WM_CTLCOLORSTATIC = 0x138; 348 // //当鼠标轮子转动时发送此消息个当前有焦点的控件 349 //const int WM_MOUSEWHEEL = 0x20A; 350 // //双击鼠标中键 351 //const int WM_MBUTTONDBLCLK = 0x209; 352 // //释放鼠标中键 353 //const int WM_MBUTTONUP = 0x208; 354 // //移动鼠标时发生,同WM_MOUSEFIRST 355 // const int WM_MOUSEMOVE = 0x200; 356 // //按下鼠标左键 357 //const int WM_LBUTTONDOWN = 0x201; 358 // //释放鼠标左键 359 //const int WM_LBUTTONUP = 0x202; 360 // //双击鼠标左键 361 //const int WM_LBUTTONDBLCLK = 0x203; 362 // //按下鼠标右键 363 //const int WM_RBUTTONDOWN = 0x204; 364 // //释放鼠标右键 365 //const int WM_RBUTTONUP = 0x205; 366 // //双击鼠标右键 367 //const int WM_RBUTTONDBLCLK = 0x206; 368 // //按下鼠标中键 369 //const int WM_MBUTTONDOWN = 0x207; 370 371 // const int WM_USER = 0x0400; 372 // const int MK_LBUTTON = 0x0001; 373 // const int MK_RBUTTON = 0x0002; 374 // const int MK_SHIFT = 0x0004; 375 // const int MK_CONTROL = 0x0008; 376 // const int MK_MBUTTON = 0x0010; 377 // const int MK_XBUTTON1 = 0x0020; 378 // const int MK_XBUTTON2 = 0x0040; 379 #endregion 主程序 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Runtime.InteropServices; 10 11 namespace 消息传递_接_ 12 { 13 public partial class Form1 : Form 14 { 15 public Form1() 16 { 17 InitializeComponent(); 18 } 19 20 class Message 21 { 22 public const int USER = 0x0400; 23 public const int WM_TEST = USER + 101; 24 public const int WM_MSG = USER + 102; 25 } 26 27 [DllImport("User32.dll", EntryPoint = "SendMessage")] 28 private static extern int SendMessage(IntPtr hWnd, int msg, uint wParam, uint lParam); 29 30 //响应和处理自定义消息 31 //在C#中,任何一个窗口都有也消息的接收处理函数,就是defproc函数 32 protected override void DefWndProc(ref System.Windows.Forms.Message m) 33 { 34 string message; 35 switch (m.Msg) 36 { 37 case Message.WM_TEST://处理消息 38 39 message = string.Format("收到从应用程序发出的消息!参数为:{0}, {1}", m.WParam, m.LParam); 40 MessageBox.Show(message); 41 break; 42 case Message.WM_MSG: 43 44 message = string.Format("收到从DLL发出的消息!参数为:{0}, {1}", m.WParam, m.LParam); 45 46 MessageBox.Show(message);//显示一个消息框 47 48 break; 49 50 default: 51 base.DefWndProc(ref m);//调用基类函数处理非自定义消息 52 break; 53 } 54 } 55 private void Form1_KeyDown(object sender, KeyEventArgs e) 56 { 57 this.label1.Text = e.KeyValue.ToString(); 58 } 59 } 60 } 被调用的程序    2)、不同项目上的多进程通信        1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Runtime.InteropServices; 10 using DATASTRUCT; 11 12 namespace Sender 13 { 14 public partial class Form1 : Form 15 { 16 public Form1() 17 { 18 InitializeComponent(); 19 } 20 21 //Win32 API函数 22 [DllImport("User32.dll", EntryPoint = "SendMessage")] 23 private static extern int SendMessage(int hWnd, int Msg, int wParam, ref DataStruct lParam); 24 25 [DllImport("User32.dll", EntryPoint = "FindWindow")] 26 private static extern int FindWindow(string lpClassName, string lpWindowName); 27 28 const int WM_COPYDATA = 0x004A; 29 30 private void btnSend_Click(object sender, EventArgs e) 31 { 32 int hWnd = FindWindow(null, @"frmReceiver");//这是接收项目运行时的任务名 33 if (hWnd == 0) 34 { 35 MessageBox.Show("未找到消息接受者!"); 36 } 37 else 38 { 39 byte[] sarr = System.Text.Encoding.Default.GetBytes(textBox2.Text); 40 int len = sarr.Length; 41 DataStruct cds; 42 cds.dwData = (IntPtr)Convert.ToInt16(textBox1.Text);//可以是任意值 43 cds.cbData = len + 1;//指定lpData内存区域的字节数 44 cds.lpData = textBox2.Text;//发送给目标窗口所在进程的数据 45 SendMessage(hWnd, WM_COPYDATA, 0, ref cds); 46 } 47 } 48 } 49 } Sender 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Runtime.InteropServices; 10 using DATASTRUCT; 11 12 namespace Receiver 13 { 14 public partial class frmReceiver : Form 15 { 16 const int WM_COPYDATA = 0x004A; 17 public frmReceiver() 18 { 19 InitializeComponent(); 20 } 21 22 protected override void DefWndProc(ref Message m) 23 { 24 switch (m.Msg) 25 { 26 case WM_COPYDATA: 27 DataStruct cds = new DataStruct(); 28 Type t = cds.GetType(); 29 cds = (DataStruct)m.GetLParam(t); 30 string strResult = cds.dwData.ToString() + ":" + cds.lpData; 31 listView1.Items.Add(strResult); 32 break; 33 default: 34 base.DefWndProc(ref m); 35 break; 36 } 37 } 38 } 39 } Receiver 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Runtime.InteropServices; 6 7 namespace DATASTRUCT 8 { 9 public struct DataStruct 10 { 11 public IntPtr dwData; 12 public int cbData; 13 [MarshalAs(UnmanagedType.LPStr)] 14 public string lpData; 15 } 16 } DATASTRUCT  2.信号量 1 using System; 2 using System.Threading; 3 class Program 4 { 5 static int g_cnt; 6 static void Main(string[] args) 7 { 8 Semaphore semaphore = new Semaphore(1, 1); 9 ParameterizedThreadStart ts = new ParameterizedThreadStart(x => 10 { 11 Semaphore s = (Semaphore)x; 12 for (int i = 0; i < 50000; ++i) 13 { 14 s.WaitOne(); 15 ++g_cnt; 16 s.Release(); 17 } 18 }); 19 Thread t1 = new Thread(ts); 20 Thread t2 = new Thread(ts); 21 g_cnt = 0; 22 t1.Start(semaphore); 23 t1.Join(); 24 Console.WriteLine(g_cnt); 25 t2.Start(semaphore); 26 t2.Join(); 27 Console.WriteLine(g_cnt); 28 Console.ReadKey(); 29 } 30 } semaphore信号量简单用法 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 7 #region 记住这个 8 //semaphore信号量类(又称信号灯) 9 //用于控制对资源池的访问。线程通过调用semaphore的WaitOne进入信号量, 10 //通过调用semaphore的Release方法退出信号量。 11 12 //信号量分为两种类型:局部信号量和已命名的系统信号量。如果您使用接受名称的构造函数创建 13 //Semaphore 对象,则该对象与具有该名称的操作系统信号量关联。已命名的系统信号量在整个操作 14 //系统中都可见,可用于同步进程活动。您可以创建多个 Semaphore 对象来表示同一个已命名的系 15 //统信号量,也可以使用 OpenExisting 方法打开现有的已命名系统信号量。www.it165.net 16 17 //局部信号量仅存在于您的进程内。您的进程中任何引用局部 Semaphore 对象的线程都可以使用它。 18 //每个 Semaphore 对象都是一个单独的局部信号量。 19 #endregion 20 21 namespace Semaphore简单用法 22 { 23 class mySemaphore 24 { 25 //第一个参数,代表当前授权次数。 26 // 0表示没有授权(证)。 27 //第二个参数,代表Semaphore实例最多能容纳几个授权证。 28 // 1表示最大授权次数为1次。 29 // 超出允许的授权次数,比如说sem.Release连续调用了两次,会抛出异常。 30 public static Semaphore sem = new Semaphore(0, 1); 31 public static void Main() 32 { 33 //添加一次授权。 34 //释放一个sem.WaitOne()的阻塞。 35 sem.Release(); 36 myThread mythrd1 = new myThread("Thrd #1"); 37 myThread mythrd2 = new myThread("Thrd #2"); 38 myThread mythrd3 = new myThread("Thrd #3"); 39 myThread mythrd4 = new myThread("Thrd #4"); 40 mythrd1.thrd.Join(); 41 mythrd2.thrd.Join(); 42 mythrd3.thrd.Join(); 43 mythrd4.thrd.Join(); 44 45 Console.ReadKey(); 46 } 47 } 48 class myThread 49 { 50 public Thread thrd; 51 public myThread(string name) 52 { 53 thrd = new Thread(this.run); 54 thrd.Name = name; 55 thrd.Start(); 56 } 57 void run() 58 { 59 Console.WriteLine(thrd.Name + "正在等待一个许可(证)……"); 60 //如果不加参数会导致无限等待。 61 if (mySemaphore.sem.WaitOne(1000)) 62 { 63 Console.WriteLine(thrd.Name + "申请到许可(证)……"); 64 Thread.Sleep(500); 65 //虽然下面添加了许可,但是,其它线程可能没拿到许可,超时退出了。 66 Console.WriteLine(thrd.Name + "添加一个许可(证)……"); 67 mySemaphore.sem.Release(); 68 } 69 else 70 { 71 Console.WriteLine(thrd.Name + " 超时(等了一段时间还是没拿到许可(证))退出……"); 72 } 73 } 74 } 75 } Semaphore简单用法   信号量(共享内存)写                        信号量(共享内存)读     1 using System.Collections.Generic; 2 using System.ComponentModel; 3 using System.Data; 4 using System.Drawing; 5 using System.Linq; 6 using System.Text; 7 using System.Windows.Forms; 8 using System.Runtime.InteropServices; 9 using System.Threading; 10 using System; 11 12 namespace _1 13 { 14 public partial class Form1 : Form 15 { 16 const int INVALID_HANDLE_VALUE = -1; 17 const int PAGE_READWRITE = 0x04; 18 //共享内存 19 [DllImport("Kernel32.dll", EntryPoint = "CreateFileMapping")] 20 private static extern IntPtr CreateFileMapping(IntPtr hFile, //HANDLE hFile, 21 UInt32 lpAttributes,//LPSECURITY_ATTRIBUTES lpAttributes, //0 22 UInt32 flProtect,//DWORD flProtect 23 UInt32 dwMaximumSizeHigh,//DWORD dwMaximumSizeHigh, 24 UInt32 dwMaximumSizeLow,//DWORD dwMaximumSizeLow, 25 string lpName//LPCTSTR lpName 26 ); 27 28 [DllImport("Kernel32.dll", EntryPoint = "OpenFileMapping")] 29 private static extern IntPtr OpenFileMapping( 30 UInt32 dwDesiredAccess,//DWORD dwDesiredAccess, 31 int bInheritHandle,//BOOL bInheritHandle, 32 string lpName//LPCTSTR lpName 33 ); 34 35 const int FILE_MAP_ALL_ACCESS = 0x0002; 36 const int FILE_MAP_WRITE = 0x0002; 37 38 [DllImport("Kernel32.dll", EntryPoint = "MapViewOfFile")] 39 private static extern IntPtr MapViewOfFile( 40 IntPtr hFileMappingObject,//HANDLE hFileMappingObject, 41 UInt32 dwDesiredAccess,//DWORD dwDesiredAccess 42 UInt32 dwFileOffsetHight,//DWORD dwFileOffsetHigh, 43 UInt32 dwFileOffsetLow,//DWORD dwFileOffsetLow, 44 UInt32 dwNumberOfBytesToMap//SIZE_T dwNumberOfBytesToMap 45 ); 46 47 [DllImport("Kernel32.dll", EntryPoint = "UnmapViewOfFile")] 48 private static extern int UnmapViewOfFile(IntPtr lpBaseAddress); 49 50 [DllImport("Kernel32.dll", EntryPoint = "CloseHandle")] 51 private static extern int CloseHandle(IntPtr hObject); 52 53 54 55 private Semaphore m_Write; //可写的信号 56 private Semaphore m_Read; //可读的信号 57 private IntPtr handle; //文件句柄 58 private IntPtr addr; //共享内存地址 59 uint mapLength; //共享内存长 60 61 Thread threadRed; 62 public Form1() 63 { 64 InitializeComponent(); 65 //threadRed = new Thread(new ThreadStart(init)); 66 //threadRed.Start(); 67 mapLength = 1024; 68 } 69 70 private void button1_Click(object sender, EventArgs e) 71 { 72 try 73 { 74 m_Write = Semaphore.OpenExisting("WriteMap"); 75 m_Read = Semaphore.OpenExisting("ReadMap"); 76 handle = OpenFileMapping(FILE_MAP_WRITE, 0, "shareMemory");//打开一个现成的文件映射对象的函数 77 addr = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, 0);//将一个文件映射对象映射到当前应用程序的地址空间 78 //件映射对象映射到当前应用程序的地址空间后 79 m_Write.WaitOne(); 80 byte[] sendStr = Encoding.Default.GetBytes(textBox1.Text.ToString() + '\0'); 81 //如果要是超长的话,应另外处理,最好是分配足够的内存 82 if (sendStr.Length < mapLength) 83 Copy(sendStr, addr); 84 85 m_Read.Release(); 86 m_Write.Release(); 87 88 } 89 catch (WaitHandleCannotBeOpenedException) 90 { 91 MessageBox.Show("不存在系统信号量!"); 92 return; 93 } 94 } 95 96 static unsafe void Copy(byte[] byteSrc, IntPtr dst) 97 { 98 fixed (byte* pSrc = byteSrc) 99 { 100 byte* pDst = (byte*)dst; 101 byte* psrc = pSrc; 102 for (int i = 0; i < byteSrc.Length; i++) 103 { 104 *pDst = *psrc; 105 pDst++; 106 psrc++; 107 } 108 } 109 } 110 } 111 } 信号量(写) 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Runtime.InteropServices; 10 using System.Threading; 11 using System.Diagnostics; 12 13 14 namespace _3.信号量_共享内存_读 15 { 16 public partial class Form1 : Form 17 { 18 const int INVALID_HANDLE_VALUE = -1; 19 const int PAGE_READWRITE = 0x04; 20 21 [DllImport("User32.dll")] 22 private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow); 23 [DllImport("User32.dll")] 24 private static extern bool SetForegroundWindow(IntPtr hWnd); 25 26 //共享内存 27 [DllImport("Kernel32.dll", EntryPoint = "CreateFileMapping")] 28 private static extern IntPtr CreateFileMapping(IntPtr hFile, //HANDLE hFile, 29 UInt32 lpAttributes,//LPSECURITY_ATTRIBUTES lpAttributes, //0 30 UInt32 flProtect,//DWORD flProtect 31 UInt32 dwMaximumSizeHigh,//DWORD dwMaximumSizeHigh, 32 UInt32 dwMaximumSizeLow,//DWORD dwMaximumSizeLow, 33 string lpName//LPCTSTR lpName 34 ); 35 36 [DllImport("Kernel32.dll", EntryPoint = "OpenFileMapping")] 37 private static extern IntPtr OpenFileMapping( 38 UInt32 dwDesiredAccess,//DWORD dwDesiredAccess, 39 int bInheritHandle,//BOOL bInheritHandle, 40 string lpName//LPCTSTR lpName 41 ); 42 43 const int FILE_MAP_ALL_ACCESS = 0x0002; 44 const int FILE_MAP_WRITE = 0x0002; 45 46 [DllImport("Kernel32.dll", EntryPoint = "MapViewOfFile")] 47 private static extern IntPtr MapViewOfFile( 48 IntPtr hFileMappingObject,//HANDLE hFileMappingObject, 49 UInt32 dwDesiredAccess,//DWORD dwDesiredAccess 50 UInt32 dwFileOffsetHight,//DWORD dwFileOffsetHigh, 51 UInt32 dwFileOffsetLow,//DWORD dwFileOffsetLow, 52 UInt32 dwNumberOfBytesToMap//SIZE_T dwNumberOfBytesToMap 53 ); 54 55 [DllImport("Kernel32.dll", EntryPoint = "UnmapViewOfFile")] 56 private static extern int UnmapViewOfFile(IntPtr lpBaseAddress); 57 58 [DllImport("Kernel32.dll", EntryPoint = "CloseHandle")] 59 private static extern int CloseHandle(IntPtr hObject); 60 61 private Semaphore m_Write; //可写的信号 62 private Semaphore m_Read; //可读的信号 63 private IntPtr handle; //文件句柄 64 private IntPtr addr; //共享内存地址 65 uint mapLength; //共享内存长 66 67 //线程用来读取数据 68 Thread threadRed; 69 public Form1() 70 { 71 InitializeComponent(); 72 init(); 73 } 74 75 ////// 初始化共享内存数据 创建一个共享内存 76 private void init() 77 { 78 m_Write = new Semaphore(1, 1, "WriteMap");//开始的时候有一个可以写 79 m_Read = new Semaphore(0, 1, "ReadMap");//没有数据可读 80 mapLength = 1024; 81 IntPtr hFile = new IntPtr(INVALID_HANDLE_VALUE); 82 handle = CreateFileMapping(hFile, 0, PAGE_READWRITE, 0, mapLength, "shareMemory"); 83 addr = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, 0); 84 85 //handle = OpenFileMapping(0x0002, 0, "shareMemory"); 86 //addr = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, 0); 87 88 threadRed = new Thread(new ThreadStart(ThreadReceive)); 89 threadRed.IsBackground = true; 90 threadRed.Start(); 91 } 92 93 /// 94 /// 线程启动从共享内存中获取数据信息 95 /// 96 private void ThreadReceive() 97 { 98 // Delegate myI = new Delegate(abc); 99 while (true) 100 { 101 try 102 { 103 //m_Write = Semaphore.OpenExisting("WriteMap"); 104 //m_Read = Semaphore.OpenExisting("ReadMap"); 105 //handle = OpenFileMapping(FILE_MAP_WRITE, 0, "shareMemory"); 106 107 //读取共享内存中的数据: 108 //是否有数据写过来 109 m_Read.WaitOne(); 110 //IntPtr m_Sender = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, 0); 111 byte[] byteStr = new byte[100]; 112 byteCopy(byteStr, addr); 113 string str = Encoding.Default.GetString(byteStr, 0, byteStr.Length); 114 115 textBox1.Text = str; 116 // init(); 117 m_Write = new Semaphore(1, 1, "WriteMap");//开始的时候有一个可以写 118 m_Read = new Semaphore(0, 1, "ReadMap");//没有数据可读 119 } 120 catch (WaitHandleCannotBeOpenedException) 121 { 122 continue; 123 //Thread.Sleep(0); 124 } 125 126 } 127 } 128 public static void abc() 129 { } 130 //不安全的代码在项目生成的选项中选中允许不安全代码 131 static unsafe void byteCopy(byte[] dst, IntPtr src) 132 { 133 fixed (byte* pDst = dst) 134 { 135 byte* pdst = pDst; 136 byte* psrc = (byte*)src; 137 while ((*pdst++ = *psrc++) != '\0') ; 138 } 139 } 140 141 private void Form1_Load(object sender, EventArgs e) 142 { 143 CheckForIllegalCrossThreadCalls = false; 144 } 145 } 146 } 信号量(读)  3.内存映射文件 1 ////持久内存映射文件 2 //持久文件是与磁盘上的源文件关联的内存映射文件。在最后一个进程使用完此文件后,数据将保存到磁盘上的源文件中。这些内存映射文件适合用来处理非常大的源文件。 3 ////非持久内存映射文件 4 //非持久文件是未与磁盘上的源文件关联的内存映射文件。当最后一个进程使用完此文件后,数据将丢失,并且垃圾回收功能将回收此文件。这些文件适用于为进程间通信 (IPC) 创建共享内存。 5 //CreateFromFile 方法基于磁盘上的现有文件创建一个内存映射文件 持久和非持久   1)、发送、接受(缺点最多5个整数)      把接收.exe放在发送运行文件的当前路径内。还有一点要记住,这只能发送数字,最多五个数字 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Runtime.InteropServices; 10 using System.Diagnostics; 11 12 //目前,网上关于C#进程间通信的方法有很多种,但是总结起来它们不外乎从以下两个方面进行考虑 13 //一、在两个进程之间建立一个共同区域,其中一个进程改变这个区域的内容,而另一个进程则去读取它, 14 //反之亦然。比如,可以让两个进程共享同一块内存,通过改变和读取内存中的内容进行通信;或者, 15 //创建一个文件,两个进程同时占用,甚至可以利用注册表或者剪贴板充当这个“共同区域”。 16 //二、利用API函数去找到进程窗口的句柄,然后用API去控制这个窗口。例如,导入“User32.dll”中 17 //的FindWindow、FindWindowEx函数查找窗口,并获取窗口句柄,也可直接利用C#中的Process类来启动 18 //程序,并获取这个进程的主窗口的句柄,等等。 19 20 //在编程时,我们往往需要选择一种即方便编写,效率又高的程序。第一种类型相对比较复杂,而且效率 21 //不高,相比来讲,第二种类型在不降低程序运行效率的情况下编写更简单。下面我就以一个示例程序来 22 //讲解如何使用Process类和API实现两个进程之间的传输数据。 23 namespace _2发送 24 { 25 public partial class Form1 : Form 26 { 27 public Form1() 28 { 29 InitializeComponent(); 30 } 31 public const int USER = 0x0400; 32 public const int UM_1 = USER + 1; 33 [DllImport("User32.dll", EntryPoint = "SendMessage")] 34 //此方法各个参数表示的意义 35 //wnd:接收消息的窗口的句柄。如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。 36 //msg:指定被发送的消息类型。 37 //wP:消息内容。 38 //lP:指定附加的消息指定信息。 39 private static extern int SendMessage(IntPtr wnd, int msg, IntPtr wP, IntPtr lP); 40 41 ProcessStartInfo info = new ProcessStartInfo(); 42 Process pro = new Process(); 43 44 private void Form1_Load(object sender, EventArgs e) 45 { 46 info.FileName = "2接受.exe"; 47 pro.StartInfo = info; 48 } 49 50 private void button1_Click(object sender, EventArgs e) 51 { 52 pro.Start(); 53 } 54 55 private void button2_Click(object sender, EventArgs e) 56 { 57 pro.Kill(); 58 } 59 60 private void button3_Click(object sender, EventArgs e) 61 { 62 IntPtr hWnd = pro.MainWindowHandle; //获取Form1.exe主窗口句柄 63 int data = Convert.ToInt32(this.textBox1.Text); //获取文本框数据 64 SendMessage(hWnd, 0x0100, (IntPtr)data, (IntPtr)0); //发送WM_KEYDOWN消息 65 } 66 } 67 } 发送 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace _2接受 11 { 12 public partial class Form1 : Form 13 { 14 public Form1() 15 { 16 InitializeComponent(); 17 } 18 //0x0100 ,即Key down 事件 19 private void Form1_KeyDown(object sender, KeyEventArgs e) 20 { 21 this.label1.Text = Convert.ToString(e.KeyValue); 22 } 23 } 24 } 接受   2)、一个项目中两个窗口模拟进程间通信   1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Runtime.InteropServices; 10 11 //最主要的一部分: 12 ////发送消息 13 //public static extern void PostMessage(IntPtr hWnd, int msg, int wParam, int lParam); 14 ////处理消息 15 //protected override void DefWndProc(ref System.Windows.Forms.Message m) 16 17 //我们可以使用PostMessage或SendMessage向目标窗口发送消息, 18 //并重载目标窗口的消息处理函数WndProc来响应消息。自定义消息的边界是0x0400, 19 //我们自定义的消息应当比它大。 20 21 //为了传递字符串,我们需要把托管的string类型封送到非托管内存中, 22 //而在目标窗口从非托管内存中提取字符串,使用 Marshal.StringToHGlobalAuto() 23 //和Marshal.PtrToStringAuto()即可。 24 namespace _2.一个项目中两个窗口模拟进程间通信 25 { 26 public partial class Form1 : Form 27 { 28 IntPtr h; 29 public Form1() 30 { 31 InitializeComponent(); 32 } 33 private void button1_Click(object sender, EventArgs e) 34 { 35 var f = new Form2(); 36 h = f.Handle; 37 f.Show(); 38 } 39 private void button2_Click(object sender, EventArgs e) 40 { 41 string str = textBox1.Text; 42 IntPtr i = Marshal.StringToHGlobalAuto(str); 43 Win32Api.PostMessage(h, Win32Api.UM_1, 0, i); 44 } 45 } 46 } Form1 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Runtime.InteropServices; 10 11 //在C#中,我们可以使用PostMessage或SendMessage向目标窗口发送消息, 12 //并重载目标窗口的消息处理函数WndProc来响应消息。自定义消息的边界是0x0400, 13 //我们自定义的消息应当比它大。 14 15 //为了传递字符串,我们需要把托管的string类型封送到非托管内存中, 16 //而在目标窗口从非托管内存中提取字符串,使用 Marshal.StringToHGlobalAuto() 17 //和Marshal.PtrToStringAuto()即可。 18 namespace _2.一个项目中两个窗口模拟进程间通信 19 { 20 public partial class Form2 : Form 21 { 22 public Form2() 23 { 24 InitializeComponent(); 25 } 26 //处理消息 27 protected override void WndProc(ref Message m) 28 { 29 switch (m.Msg) 30 { 31 case Win32Api.UM_1: 32 string str = Marshal.PtrToStringAuto(m.LParam); 33 textBox1.Text += str + Environment.NewLine; 34 break; 35 default: 36 base.WndProc(ref m); 37 break; 38 } 39 } 40 } 41 } Form2 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Runtime.InteropServices; 6 //WINDOWS自定义消息WM_USER和WM_APP 7 //常量是Windows帮助应用程序定义私有窗口类里的私有消息,通常使用WM_USER+一个整数值,但总值不能超过0x7FFF。 8 //#define WM_USER 0x0400 -->1024 9 //常量是Windows帮助应用程序定义私有消息,通常使用WM_APP+一个整数值,但总值不能超过0xBFFF。 10 //#define WM_APP 0x8000 -->32768 11 //这两个都定义在WinUser.h中。 12 //范围 13 //表示 14 //0 ~ WM_USER–1 15 namespace _2.一个项目中两个窗口模拟进程间通信 16 { 17 class Win32Api 18 { 19 #region msg 20 public const int USER = 1;//0x0400;//用户自定义消息的开始数值1024 21 public const int UM_1 = USER + 1; 22 #endregion 23 #region api 24 [DllImport("user32.dll")] 25 //发送消息 26 public static extern void PostMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam); 27 #endregion 28 } 29 } Win32Api    3)、内存映射(不操作文件) 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO; 6 //引用内存映射文件命名空间 7 using System.IO.MemoryMappedFiles; 8 9 namespace _4.内存映射_写_ 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 long capacity = 1 << 10 << 10; 16 17 //创建或者打开共享内存 18 using (var mmf = MemoryMappedFile.CreateOrOpen("testMmf", capacity, MemoryMappedFileAccess.ReadWrite)) 19 { 20 //通过MemoryMappedFile的CreateViewAccssor方法获得共享内存的访问器 21 var viewAccessor = mmf.CreateViewAccessor(0, capacity); 22 //循环写入,使在这个进程中可以向共享内存中写入不同的字符串值 23 while (true) 24 { 25 Console.WriteLine("请输入一行要写入共享内存的文字:"); 26 27 string input = Console.ReadLine(); 28 29 //向共享内存开始位置写入字符串的长度 30 viewAccessor.Write(0, input.Length); 31 32 //向共享内存4位置写入字符 33 viewAccessor.WriteArray(4, input.ToArray(), 0, input.Length); 34 } 35 } 36 } 37 } 38 } 内存映射、写 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading; 6 //引用使用内存映射文件需要的命名空间 7 using System.IO.MemoryMappedFiles; 8 9 namespace _4.内存映射_读1_ 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 long capacity = 1 << 10 << 10; 16 17 using (var mmf = MemoryMappedFile.OpenExisting("testMmf")) 18 { 19 MemoryMappedViewAccessor viewAccessor = mmf.CreateViewAccessor(0, capacity); 20 21 //循环刷新共享内存字符串的值 22 while (true) 23 { 24 //读取字符长度 25 int strLength = viewAccessor.ReadInt32(0); 26 char[] charsInMMf = new char[strLength]; 27 //读取字符 28 viewAccessor.ReadArray(4, charsInMMf, 0, strLength); 29 Console.Clear(); 30 Console.Write(charsInMMf); 31 Console.Write("\r"); 32 Thread.Sleep(200); 33 } 34 } 35 } 36 } 37 } 内存映射、读1 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO.MemoryMappedFiles; 6 using System.IO; 7 8 namespace _4.内存映射_读2_ 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 long capacity = 1 << 10 << 10; 15 //打开共享内存 16 using (var mmf = MemoryMappedFile.OpenExisting("testMmf")) 17 { 18 //使用CreateViewStream方法返回stream实例 19 using (var mmViewStream = mmf.CreateViewStream(0, capacity)) 20 { 21 //这里要制定Unicode编码否则会出问题 22 using (BinaryReader rdr = new BinaryReader(mmViewStream, Encoding.Unicode)) 23 { 24 while (true) 25 { 26 mmViewStream.Seek(0, SeekOrigin.Begin); 27 28 int length = rdr.ReadInt32(); 29 30 char[] chars = rdr.ReadChars(length); 31 32 Console.Write(chars); 33 Console.Write("\r"); 34 35 System.Threading.Thread.Sleep(200); 36 Console.Clear(); 37 } 38 } 39 } 40 } 41 } 42 } 43 } 内存映射、读2    2)、文件存入内存(无映射) 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO; 6 7 //读取大文件可以使用它,但它不是最快的 8 namespace _5.内存映射文件_读放读_ 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 string content = string.Empty; 15 16 using (StreamReader sr = new StreamReader("1.txt")) 17 { 18 content = sr.ReadToEnd();//一次性读入内存 19 } 20 MemoryStream ms = new MemoryStream(Encoding.GetEncoding("GB2312").GetBytes(content));//放入内存流,以便逐行读取 21 22 using (StreamReader sr = new StreamReader(ms)) 23 { 24 while (sr.Peek() > -1) 25 { 26 Console.WriteLine(sr.ReadLine() + "\r\n"); 27 } 28 } 29 Console.Read(); 30 } 31 } 32 } 内存映射文件、读放读    3)、内存映射文件、视图流(读) 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO; 6 using System.IO.MemoryMappedFiles; 7 8 namespace _5.内存映射文件_读放读_ 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 var filePath = "1.txt"; 15 var fileSize = new FileInfo(filePath).Length; 16 17 using (var mm = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read)) 18 using (var stream = mm.CreateViewStream(0, fileSize, MemoryMappedFileAccess.Read)) 19 using (var reader = new StreamReader(stream, Encoding.GetEncoding("GB2312"))) 20 { 21 Console.WriteLine(reader.ReadToEnd()); 22 } 23 Console.Read(); 24 } 25 } 26 } 内存映射文件、读放读   4)、内存映射文件、视图访问器(读) 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO; 6 using System.IO.MemoryMappedFiles; 7 8 namespace _5.内存映射文件_读放读_ 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 var filePath = @"1.txt"; 15 var fileSize = new FileInfo(filePath).Length; 16 17 using (var mm = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read))//已知文件 内容不要为空,要不会出错 18 using (var accessor = mm.CreateViewAccessor(0, fileSize, MemoryMappedFileAccess.Read)) 19 { 20 byte[] bytes = new byte[fileSize]; 21 for (int i = 0; i < fileSize; i++) 22 { 23 bytes[i] = accessor.ReadByte(i); 24 } 25 26 var results = Encoding.GetEncoding("GB2312").GetString(bytes); 27 Console.WriteLine(results); 28 } 29 Console.Read(); 30 } 31 } 32 } 内存映射文件、读放读    5)、内存映射文件内容、写入、读取(持久性)     1.内存指定文件的内容映射在内存中(持久) 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO.MemoryMappedFiles; 6 using System.IO; 7 using System.Runtime.InteropServices; 8 //前提,这是把指定的文件内容加入到内存中 9 namespace _2.内存映射指定文件_持久_向内存中写 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 //持久内存映射文件:基于现有文件创建一个具有指定公用名的内存映射文件 16 using (var mmf = MemoryMappedFile.CreateFromFile("1.txt", FileMode.Open, "MyFile")) 17 18 // using (var mmf = MemoryMappedFile.CreateFromFile("1.txt", FileMode.Open, "123",0, MemoryMappedFileAccess.ReadWrite)) 19 { 20 //通过指定的 偏移量和大小 创建内存映射文件视图服务器 21 using (var accessor = mmf.CreateViewAccessor(0, 0, MemoryMappedFileAccess.ReadWrite)) //偏移量,可以控制数据存储的内存位置;大小,用来控制存储所占用的空间 22 { 23 Console.Read(); 24 } 25 } 26 27 } 28 } 29 } 内存映射指定文件_持久_向内存中写     2.内存映射指定文件内存中读(持久) 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO.MemoryMappedFiles; 6 using System.Runtime.InteropServices; 7 8 namespace _2.内存映射指定文件_持久_内存中读 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 //另一个进程或线程可以,在系统内存中打开一个具有指定名称的现有内存映射文件 15 using (var mmf = MemoryMappedFile.OpenExisting("MyFile")) 16 { 17 using (var accessor = mmf.CreateViewAccessor(0, 0, MemoryMappedFileAccess.ReadWrite)) 18 { 19 long strLength = accessor.Capacity; //访问器的容量 20 int size = Marshal.SizeOf(typeof(char)); 21 22 byte[] by = new byte[strLength]; 23 24 for (long i = 0; i < strLength; i += size) 25 { 26 by[i] = accessor.ReadByte(i); 27 28 } var results = Encoding.GetEncoding("GB2312").GetString(by); 29 Console.WriteLine(results); 30 } 31 } 32 Console.Read(); 33 } 34 } 35 } 内存映射指定文件_持久_内存中读     3.向内存中写、内存映射物理文件内容更改(持久) 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO.MemoryMappedFiles; 6 using System.IO; 7 using System.Runtime.InteropServices; 8 //前提,这是把指定的文件内容加入到内存中 9 namespace _2.内存映射指定文件_持久_向内存中写 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 string filePath = "1.txt"; 16 var fileSize = new FileInfo(filePath).Length; 17 int Size = (int)fileSize; 18 19 Console.WriteLine("请输入一行要写入共享内存的文字:"); 20 string input = Console.ReadLine(); 21 22 //持久内存映射文件:基于现有文件创建一个具有指定公用名的内存映射文件 23 using (var mmf = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open, "MyFile")) 24 { 25 //通过指定的 偏移量和大小 创建内存映射文件视图服务器 26 using (var accessor = mmf.CreateViewAccessor(0, 0, MemoryMappedFileAccess.ReadWrite)) //偏移量,可以控制数据存储的内存位置;大小,用来控制存储所占用的空间 27 { 28 Console.WriteLine(accessor.Capacity); 29 30 //向共享内存开始位置写入字符串的长度 31 accessor.Write(0, input.Length); 32 33 //向共享内存4位置写入字符 34 accessor.WriteArray(0, input.ToArray(), 0, input.Length); 35 36 Console.Read(); 37 } 38 } 39 } 40 } 41 } 内存映射指定文件_持久_向内存中写   6)、非持久(内存映射) 1 using System; 2 using System.IO; 3 using System.IO.MemoryMappedFiles; 4 using System.Runtime.InteropServices; 5 using System.Threading; 6 7 namespace 非持久_内存映射_ 8 { 9 /// 10 /// 用于共享内存方式通信的 值类型 结构体 11 /// 12 public struct ServiceMsg 13 { 14 public int Id; 15 public long NowTime; 16 } 17 18 internal class Program 19 { 20 private static void Main(string[] args) 21 { 22 Console.Write("请输入共享内存公用名(默认:testmap):"); 23 string shareName = Console.ReadLine(); 24 if (string.IsNullOrEmpty(shareName)) 25 shareName = "testmap"; 26 using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen(shareName, 1024000, MemoryMappedFileAccess.ReadWrite)) 27 { 28 Mutex mutex = Mutex.OpenExisting("testmapmutex"); 29 mutex.WaitOne(); 30 using (MemoryMappedViewStream stream = mmf.CreateViewStream(20, 0)) //注意这里的偏移量 31 { 32 var writer = new BinaryWriter(stream); 33 for (int i = 5; i < 10; i++) 34 { 35 writer.Write(i); 36 Console.WriteLine("{0}位置写入流:{0}", i); 37 } 38 } 39 using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor(1024, 10240)) 40 { 41 int colorSize = Marshal.SizeOf(typeof(ServiceMsg)); 42 var color = new ServiceMsg(); 43 for (int i = 0; i < colorSize * 5; i += colorSize) 44 { 45 color.Id = i; 46 color.NowTime = DateTime.Now.Ticks; 47 //accessor.Read(i, out color); 48 accessor.Write(i, ref color); 49 Console.WriteLine("{1}\tNowTime:{0}", new DateTime(color.NowTime), color.Id); 50 Thread.Sleep(1000); 51 } 52 } 53 Thread.Sleep(5000); 54 55 mutex.ReleaseMutex(); 56 } 57 Console.WriteLine("测试: 我是 即时通讯 - 状态服务 我启动啦!!!"); 58 Console.ReadKey(); 59 } 60 } 61 } 非持久_内存映射 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System; 6 using System.IO; 7 using System.IO.MemoryMappedFiles; 8 using System.Runtime.InteropServices; 9 using System.Threading; 10 11 namespace _3.非持久_读写_ 12 { 13 /// 14 /// 用于共享内存方式通信的 值类型 结构体 15 /// 16 public struct ServiceMsg 17 { 18 public int Id; 19 public long NowTime; 20 } 21 22 internal class Program 23 { 24 private static void Main(string[] args) 25 { 26 Console.Write("请输入共享内存公用名(默认:testmap):"); 27 string shareName = Console.ReadLine(); 28 if (string.IsNullOrEmpty(shareName)) 29 shareName = "testmap"; 30 using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen(shareName, 1024000, MemoryMappedFileAccess.ReadWrite)) 31 { 32 bool mutexCreated; 33 //进程间同步 34 var mutex = new Mutex(true, "testmapmutex", out mutexCreated); 35 using (MemoryMappedViewStream stream = mmf.CreateViewStream()) //创建文件内存视图流 36 { 37 var writer = new BinaryWriter(stream); 38 for (int i = 0; i < 5; i++) 39 { 40 writer.Write(i); 41 Console.WriteLine("{0}位置写入流:{0}", i); 42 } 43 } 44 45 mutex.ReleaseMutex(); 46 47 Console.WriteLine("启动状态服务,按【回车】读取共享内存数据"); 48 Console.ReadLine(); 49 50 mutex.WaitOne(); 51 using (MemoryMappedViewStream stream = mmf.CreateViewStream()) 52 { 53 var reader = new BinaryReader(stream); 54 for (int i = 0; i < 10; i++) 55 { 56 Console.WriteLine("{1}位置:{0}", reader.ReadInt32(), i); 57 } 58 } 59 60 using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor(1024, 10240)) 61 { 62 int colorSize = Marshal.SizeOf(typeof(ServiceMsg)); 63 ServiceMsg color; 64 for (int i = 0; i < 50; i += colorSize) 65 { 66 accessor.Read(i, out color); 67 Console.WriteLine("{1}\tNowTime:{0}", new DateTime(color.NowTime), color.Id); 68 } 69 } 70 mutex.ReleaseMutex(); 71 } 72 Console.WriteLine("测试: 我是 即时通讯 - 消息服务 我启动啦!!!"); 73 Console.ReadKey(); 74 } 75 } 76 } 非持久_读写_   7)、内存映射文件(电脑dll) 1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Runtime.InteropServices; 5 using System.IO; 6 using System.Data.SqlClient; 7 8 namespace _6.内存映射文件_dll_ 9 { 10 class ShareMemory 11 { 12 [DllImport("kernel32.dll")] 13 public static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpFileMappingAttributes, uint flProtect, uint dwMaximumSizeHigh, 14 uint dwMaximumSizeLow, string lpName); 15 16 [DllImport("kernel32.dll")] 17 public static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint 18 dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, IntPtr dwNumberOfBytesToMap); 19 20 [DllImport("kernel32.dll")] 21 public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress); 22 23 [DllImport("kernel32.dll")] 24 public static extern bool CloseHandle(IntPtr hObject); 25 26 [DllImport("kernel32.dll")] 27 public static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess, FileShare dwShareMode, IntPtr securityAttrs, 28 FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile); 29 30 [DllImport("kernel32.dll")] 31 public static extern uint GetFileSize(IntPtr hFile, IntPtr lpFileSizeHigh); 32 33 public const int GENERIC_READ = -2147483648; //0x80000000 34 public const int GENERIC_WRITE = 0x40000000; 35 public const int GENERIC_EXECUTE = 0x20000000; 36 public const int GENERIC_ALL = 0x10000000; 37 public const int FILE_ATTRIBUTE_NORMAL = 0x80; 38 public const int FILE_FLAG_SEQUENTIAL_SCAN = 0x8000000; 39 public const int INVALID_HANDLE_VALUE = -1; 40 41 public const int PAGE_NOACCESS = 1; 42 public const int PAGE_READONLY = 2; 43 public const int PAGE_READWRITE = 4; 44 45 public const int FILE_MAP_COPY = 1; 46 public const int FILE_MAP_WRITE = 2; 47 public const int FILE_MAP_READ = 4; 48 49 //////////////////////////////////////// 50 public static string MappingFileRead() 51 { 52 string result = ""; 53 try 54 { 55 IntPtr vFileHandle = CreateFile("1.txt", GENERIC_READ | GENERIC_WRITE, FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.Open, 56 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, IntPtr.Zero); 57 if (INVALID_HANDLE_VALUE != (int)vFileHandle) 58 { 59 IntPtr vMappingHandle = CreateFileMapping(vFileHandle, IntPtr.Zero, PAGE_READWRITE, 0, 0, "~MappingTemp"); 60 if (vMappingHandle != IntPtr.Zero) 61 { 62 IntPtr vHead = MapViewOfFile(vMappingHandle, FILE_MAP_COPY | FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, IntPtr.Zero); 63 if (vHead != IntPtr.Zero) 64 { 65 uint vSize = GetFileSize(vFileHandle, IntPtr.Zero); 66 byte[] res = new byte[vSize]; 67 Marshal.Copy(vHead, res, 0, (int)vSize); 68 result = ASCIIEncoding.Default.GetString(res); 69 string[] _str = result.Split('|'); 70 result = _str[0]; 71 /* 72 //*************************************** 73 //_IntPtr = Marshal.ReadIntPtr(vHead); 74 byte[] contentbyte1 = new byte[vSize]; 75 for (int i = 0; i < vSize; i++) 76 { 77 byte vTemp = Marshal.ReadByte((IntPtr)((int)vHead + i)); 78 contentbyte1[i] = vTemp; 79 } 80 Console.WriteLine(ASCIIEncoding.Default.GetString(contentbyte1));//全部值 81 //*************************************** 82 byte[] contentbyte = new byte[vSize]; 83 for (int i = 0; i < vSize; i++) 84 { 85 byte vTemp = Marshal.ReadByte((IntPtr)((int)vHead + i)); 86 contentbyte[i] = vTemp; 87 if (vTemp == '|')//设定标识结束符合 88 { 89 break; 90 } 91 } 92 Console.WriteLine(ASCIIEncoding.Default.GetString(contentbyte));//要取的值 93 result = ASCIIEncoding.Default.GetString(contentbyte); 94 //*************************************** 95 */ 96 UnmapViewOfFile(vHead); 97 } 98 CloseHandle(vMappingHandle); 99 } 100 CloseHandle(vFileHandle); 101 } 102 103 } 104 catch 105 { } 106 return result; 107 } 108 109 ////////////////////////////////// 110 public static IntPtr InvalidHandleValue = new IntPtr(-1); 111 public static void MappingFileWrite(string val) 112 { 113 try 114 { 115 IntPtr vFileHandle = CreateFile(@"c:/test.txt", GENERIC_READ | GENERIC_WRITE, FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.Open, 116 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, IntPtr.Zero); 117 if (INVALID_HANDLE_VALUE != (int)vFileHandle) 118 { 119 IntPtr vMappingHandle = CreateFileMapping(vFileHandle, IntPtr.Zero, PAGE_READWRITE, 0, (UInt32)(val.Length + 1), "~MappingTemp"); 120 if (vMappingHandle != IntPtr.Zero) 121 { 122 IntPtr vHead = MapViewOfFile(vMappingHandle, FILE_MAP_COPY | FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, IntPtr.Zero); 123 if (vHead != IntPtr.Zero) 124 { 125 byte[] by = ASCIIEncoding.Default.GetBytes(val + "|"); //设定标识结束符合 | 126 Marshal.Copy(by, 0, vHead, by.Length); 127 UnmapViewOfFile(vHead); 128 } 129 CloseHandle(vMappingHandle); 130 } 131 CloseHandle(vFileHandle); 132 } 133 134 } 135 catch 136 { } 137 } 138 139 ////////////////////////////////// 140 public static void MappingWrite1(string val) 141 { 142 try 143 { 144 IntPtr vMappingHandle = CreateFileMapping(InvalidHandleValue, IntPtr.Zero, PAGE_READWRITE, 0, (UInt32)(val.Length + 1), "~MappingTemp"); 145 if (vMappingHandle != IntPtr.Zero) 146 { 147 IntPtr vHead = MapViewOfFile(vMappingHandle, FILE_MAP_COPY | FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, IntPtr.Zero); 148 if (vHead != IntPtr.Zero) 149 { 150 byte[] by = ASCIIEncoding.Default.GetBytes(val + "|"); //设定标识结束符合 | 151 Marshal.Copy(by, 0, vHead, by.Length); 152 UnmapViewOfFile(vHead); 153 } 154 //CloseHandle(vMappingHandle); 155 } 156 157 } 158 catch 159 { } 160 } 161 162 ////////////////////////////////// 163 public static string MappingRead1() 164 { 165 string result = ""; 166 try 167 { 168 IntPtr vMappingHandle = CreateFileMapping(InvalidHandleValue, IntPtr.Zero, PAGE_READWRITE, 0, 7, "~MappingTemp"); 169 if (vMappingHandle != IntPtr.Zero) 170 { 171 IntPtr vHead = MapViewOfFile(vMappingHandle, FILE_MAP_COPY | FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, IntPtr.Zero); 172 if (vHead != IntPtr.Zero) 173 { 174 byte[] bytData = new byte[7]; 175 Marshal.Copy(vHead, bytData, 0, 7); 176 result = ASCIIEncoding.Default.GetString(bytData); 177 UnmapViewOfFile(vHead); 178 //Console.WriteLine(result);自己填的 179 } 180 CloseHandle(vMappingHandle); 181 } 182 } 183 catch 184 { 185 } 186 return result; 187 } 188 static void Main(string[] args) 189 { 190 Console.WriteLine(MappingFileRead()); 191 //MappingWrite1("123"); 192 //MappingRead1(); 193 Console.Read(); 194 } 195 } 196 } 197 198 199 #region 200 //namespace 201 //{ 202 // public class 203 // { 204 // [DllImport( "user32.dll", CharSet = CharSet.Auto )] 205 // public static extern IntPtr SendMessage( IntPtr hWnd, int Msg, int wParam, IntPtr lParam ); 206 207 // [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )] 208 // public static extern IntPtr CreateFileMapping( IntPtr hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName ); 209 210 // [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )] 211 // public static extern IntPtr OpenFileMapping( int dwDesiredAccess, [MarshalAs( UnmanagedType.Bool )] bool bInheritHandle, string lpName ); 212 213 // [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )] 214 // public static extern IntPtr MapViewOfFile( IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap ); 215 216 // [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )] 217 // public static extern bool UnmapViewOfFile( IntPtr pvBaseAddress ); 218 219 // [DllImport( "Kernel32.dll", CharSet = CharSet.Auto )] 220 // public static extern bool CloseHandle( IntPtr handle ); 221 222 // [DllImport( "kernel32", EntryPoint = "GetLastError" )] 223 // public static extern int GetLastError(); 224 225 // [DllImport( "kernel32.dll" )] 226 // static extern void GetSystemInfo( out SYSTEM_INFO lpSystemInfo ); 227 228 // [StructLayout( LayoutKind.Sequential )] 229 // public struct SYSTEM_INFO 230 // { 231 // public ushort processorArchitecture; 232 // ushort reserved; 233 // public uint pageSize; 234 // public IntPtr minimumApplicationAddress; 235 // public IntPtr maximumApplicationAddress; 236 // public IntPtr activeProcessorMask; 237 // public uint numberOfProcessors; 238 // public uint processorType; 239 // public uint allocationGranularity; 240 // public ushort processorLevel; 241 // public ushort processorRevision; 242 // } 243 // /// 244 // /// 获取系统的分配粒度 245 // /// 246 // /// 247 // public static uint GetPartitionsize() 248 // { 249 // SYSTEM_INFO sysInfo; 250 // GetSystemInfo( out sysInfo ); 251 // return sysInfo.allocationGranularity; 252 // } 253 254 // const int ERROR_ALREADY_EXISTS = 183; 255 256 // const int FILE_MAP_COPY = 0x0001; 257 // const int FILE_MAP_WRITE = 0x0002; 258 // const int FILE_MAP_READ = 0x0004; 259 // const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004; 260 261 // const int PAGE_READONLY = 0x02; 262 // const int PAGE_READWRITE = 0x04; 263 // const int PAGE_WRITECOPY = 0x08; 264 // const int PAGE_EXECUTE = 0x10; 265 // const int PAGE_EXECUTE_READ = 0x20; 266 // const int PAGE_EXECUTE_READWRITE = 0x40; 267 268 // const int SEC_COMMIT = 0x8000000; 269 // const int SEC_IMAGE = 0x1000000; 270 // const int SEC_NOCACHE = 0x10000000; 271 // const int SEC_RESERVE = 0x4000000; 272 273 // IntPtr m_fHandle; 274 275 // IntPtr m_hSharedMemoryFile = IntPtr.Zero; 276 // IntPtr m_pwData = IntPtr.Zero; 277 // bool m_bAlreadyExist = false; 278 // bool m_bInit = false; 279 // uint m_MemSize = 0x1400000;//20M 280 // long m_offsetBegin = 0; 281 // long m_FileSize = 0; 282 // FileReader File = new FileReader(); 283 284 285 // /// 286 // /// 初始化文件 287 // /// 288 // /// 缓冲大小 289 // public ShareMemory( string filename, uint memSize ) 290 // { 291 // // 分页映射文件时,每页的起始位置startpos,必须为64K的整数倍。 292 // // memSize即缓存区的大小必须是系统分配粒度的整倍说,window系统的分配粒度是64KB 293 // this.m_MemSize = memSize; 294 // Init( filename ); 295 // } 296 297 298 // /// 299 // /// 默认映射20M缓冲 300 // /// 301 // /// 302 // public ShareMemory( string filename ) 303 // { 304 // this.m_MemSize = 0x1400000; 305 // Init( filename ); 306 // } 307 308 // ~ShareMemory() 309 // { 310 // Close(); 311 // } 312 313 // /// 314 // /// 初始化共享内存 315 // /// 316 // /// 共享内存名称 317 // /// 共享内存大小 318 // /// 319 // /// 320 // protected void Init( string strName ) 321 // { 322 // //if (lngSize <= 0 || lngSize > 0x00800000) lngSize = 0x00800000; 323 324 // if ( !System.IO.File.Exists( strName ) ) throw new Exception( "未找到文件" ); 325 326 // System.IO.FileInfo f = new System.IO.FileInfo( strName ); 327 328 // m_FileSize = f.Length; 329 330 // m_fHandle = File.Open( strName ); 331 332 // if ( strName.Length > 0 ) 333 // { 334 // //创建文件映射 335 // m_hSharedMemoryFile = CreateFileMapping( m_fHandle, IntPtr.Zero, ( uint )PAGE_READONLY, 0, ( uint )m_FileSize, "mdata" ); 336 // if ( m_hSharedMemoryFile == IntPtr.Zero ) 337 // { 338 // m_bAlreadyExist = false; 339 // m_bInit = false; 340 // throw new Exception( "CreateFileMapping失败LastError=" + GetLastError().ToString() ); 341 // } 342 // else 343 // m_bInit = true; 344 345 // ////映射第一块文件 346 // //m_pwData = MapViewOfFile(m_hSharedMemoryFile, FILE_MAP_READ, 0, 0, (uint)m_MemSize); 347 // //if (m_pwData == IntPtr.Zero) 348 // //{ 349 // // m_bInit = false; 350 // // throw new Exception("m_hSharedMemoryFile失败LastError=" + GetLastError().ToString()); 351 // //} 352 353 // } 354 // } 355 // /// 356 // /// 获取高32位 357 // /// 358 // /// 359 // /// 360 // private static uint GetHighWord( UInt64 intValue ) 361 // { 362 // return Convert.ToUInt32( intValue >> 32 ); 363 // } 364 // /// 365 // /// 获取低32位 366 // /// 367 // /// 368 // /// 369 // private static uint GetLowWord( UInt64 intValue ) 370 // { 371 372 // return Convert.ToUInt32( intValue & 0x00000000FFFFFFFF ); 373 // } 374 375 // /// 376 // /// 获取下一个文件块 块大小为20M 377 // /// 378 // /// false 表示已经是最后一块文件 379 // public uint GetNextblock() 380 // { 381 // if ( !this.m_bInit ) throw new Exception( "文件未初始化。" ); 382 // //if ( m_offsetBegin + m_MemSize >= m_FileSize ) return false; 383 384 // uint m_Size = GetMemberSize(); 385 // if ( m_Size == 0 ) return m_Size; 386 387 // // 更改缓冲区大小 388 // m_MemSize = m_Size; 389 390 // //卸载前一个文件 391 // //bool l_result = UnmapViewOfFile( m_pwData ); 392 // //m_pwData = IntPtr.Zero; 393 394 395 // m_pwData = MapViewOfFile( m_hSharedMemoryFile, FILE_MAP_READ, GetHighWord( ( UInt64 )m_offsetBegin ), GetLowWord( ( UInt64 )m_offsetBegin ), m_Size ); 396 // if ( m_pwData == IntPtr.Zero ) 397 // { 398 // m_bInit = false; 399 // throw new Exception( "映射文件块失败" + GetLastError().ToString() ); 400 // } 401 // m_offsetBegin = m_offsetBegin + m_Size; 402 403 // return m_Size; //创建成功 404 // } 405 // /// 406 // /// 返回映射区大小 407 // /// 408 // /// 409 // private uint GetMemberSize() 410 // { 411 // if ( m_offsetBegin >= m_FileSize ) 412 // { 413 // return 0; 414 // } 415 // else if ( m_offsetBegin + m_MemSize >= m_FileSize ) 416 // { 417 // long temp = m_FileSize - m_offsetBegin; 418 // return ( uint )temp; 419 // } 420 // else 421 // return m_MemSize; 422 // } 423 424 // /// 425 // /// 关闭内存映射 426 // /// 427 // public void Close() 428 // { 429 // if ( m_bInit ) 430 // { 431 // UnmapViewOfFile( m_pwData ); 432 // CloseHandle( m_hSharedMemoryFile ); 433 // File.Close(); 434 // } 435 // } 436 437 // /// 438 // /// 从当前块中获取数据 439 // /// 440 // /// 数据 441 // /// 起始数据 442 // /// 数据长度,最大值=缓冲长度 443 // /// 读取完成是否卸载缓冲区 444 // /// 445 // public void Read( ref byte[] bytData, int lngAddr, int lngSize, bool Unmap ) 446 // { 447 // if ( lngAddr + lngSize > m_MemSize ) 448 // throw new Exception( "Read操作超出数据区" ); 449 // if ( m_bInit ) 450 // { 451 // // string bb = Marshal.PtrToStringAuto(m_pwData);// 452 // Marshal.Copy( m_pwData, bytData, lngAddr, lngSize ); 453 // } 454 // else 455 // { 456 // throw new Exception( "文件未初始化" ); 457 // } 458 459 // if ( Unmap ) 460 // { 461 // bool l_result = UnmapViewOfFile( m_pwData ); 462 // if ( l_result ) 463 // m_pwData = IntPtr.Zero; 464 // } 465 // } 466 467 // /// 468 // /// 从当前块中获取数据 469 // /// 470 // /// 数据 471 // /// 起始数据 472 // /// 数据长度,最大值=缓冲长度 473 // /// 474 // /// 475 // /// 476 // public void Read( ref byte[] bytData, int lngAddr, int lngSize ) 477 // { 478 // if ( lngAddr + lngSize > m_MemSize ) 479 // throw new Exception( "Read操作超出数据区" ); 480 // if ( m_bInit ) 481 // { 482 // Marshal.Copy( m_pwData, bytData, lngAddr, lngSize ); 483 // } 484 // else 485 // { 486 // throw new Exception( "文件未初始化" ); 487 // } 488 // } 489 490 // /// 491 // /// 从当前块中获取数据 492 // /// 493 // /// 缓存区偏移量 494 // /// 数据数组 495 // /// 数据数组开始复制的下标 496 // /// 数据长度,最大值=缓冲长度 497 // /// 498 // /// 499 // /// 返回实际读取值 500 // public uint ReadBytes( int lngAddr, ref byte[] byteData, int StartIndex, uint intSize ) 501 // { 502 // if ( lngAddr >= m_MemSize ) 503 // throw new Exception( "起始数据超过缓冲区长度" ); 504 505 // if ( lngAddr + intSize > m_MemSize ) 506 // intSize = m_MemSize - ( uint )lngAddr; 507 508 // if ( m_bInit ) 509 // { 510 // IntPtr s = new IntPtr( ( long )m_pwData + lngAddr ); // 地址偏移 511 // Marshal.Copy( s, byteData, StartIndex, ( int )intSize ); 512 // } 513 // else 514 // { 515 // throw new Exception( "文件未初始化" ); 516 // } 517 518 // return intSize; 519 // } 520 521 // /// 522 // /// 写数据 523 // /// 524 // /// 数据 525 // /// 起始地址 526 // /// 个数 527 // /// 528 // private int Write( byte[] bytData, int lngAddr, int lngSize ) 529 // { 530 // if ( lngAddr + lngSize > m_MemSize ) return 2; //超出数据区 531 // if ( m_bInit ) 532 // { 533 // Marshal.Copy( bytData, lngAddr, m_pwData, lngSize ); 534 // } 535 // else 536 // { 537 // return 1; //共享内存未初始化 538 // } 539 // return 0; //写成功 540 // } 541 // } 542 // internal class FileReader 543 // { 544 // const uint GENERIC_READ = 0x80000000; 545 // const uint OPEN_EXISTING = 3; 546 // System.IntPtr handle; 547 548 // [DllImport( "kernel32", SetLastError = true )] 549 // public static extern System.IntPtr CreateFile( 550 // string FileName, // file name 551 // uint DesiredAccess, // access mode 552 // uint ShareMode, // share mode 553 // uint SecurityAttributes, // Security Attributes 554 // uint CreationDisposition, // how to create 555 // uint FlagsAndAttributes, // file attributes 556 // int hTemplateFile // handle to template file 557 // ); 558 559 // [System.Runtime.InteropServices.DllImport( "kernel32", SetLastError = true )] 560 // static extern bool CloseHandle 561 // ( 562 // System.IntPtr hObject // handle to object 563 // ); 564 565 566 567 // public IntPtr Open( string FileName ) 568 // { 569 // // open the existing file for reading 570 // handle = CreateFile 571 // ( 572 // FileName, 573 // GENERIC_READ, 574 // 0, 575 // 0, 576 // OPEN_EXISTING, 577 // 0, 578 // 0 579 // ); 580 581 // if ( handle != System.IntPtr.Zero ) 582 // { 583 // return handle; 584 // } 585 // else 586 // { 587 // throw new Exception( "打开文件失败" ); 588 // } 589 // } 590 591 // public bool Close() 592 // { 593 // return CloseHandle( handle ); 594 // } 595 // } 596 //} 597 598 599 //using System; 600 //using System.Collections.Generic; 601 //using System.Text; 602 //using System.Runtime.InteropServices; 603 //using System.IO; 604 605 //namespace TestOpenFileMap 606 //{ 607 // public class FileMap 608 // { 609 // [StructLayout(LayoutKind.Sequential)] 610 // internal struct SYSTEM_INFO 611 // { 612 // public uint dwOemId; 613 // public uint dwPageSize; 614 // public uint lpMinimumApplicationAddress; 615 // public uint lpMaximumApplicationAddress; 616 // public uint dwActiveProcessorMask; 617 // public uint dwNumberOfProcessors; 618 // public uint dwProcessorType; 619 // public uint dwAllocationGranularity; 620 // public uint dwProcessorLevel; 621 // public uint dwProcessorRevision; 622 // } 623 624 // private const uint GENERIC_READ = 0x80000000; 625 // private const uint GENERIC_WRITE =0x40000000; 626 // private const int OPEN_EXISTING = 3; 627 // private const int INVALID_HANDLE_VALUE = -1; 628 // private const int FILE_ATTRIBUTE_NORMAL = 0x80; 629 // private const uint FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; 630 // private const uint PAGE_READWRITE = 0x04; 631 632 // private const int FILE_MAP_COPY = 1; 633 // private const int FILE_MAP_WRITE = 2; 634 // private const int FILE_MAP_READ = 4; 635 636 // /// 637 // /// 内存映射文件句柄 638 // /// 639 // /// 640 // /// 641 // /// 642 // /// 643 // /// 644 // /// 645 // /// 646 // [DllImport("kernel32.dll")] 647 // internal static extern IntPtr CreateFileMapping(IntPtr hFile, 648 // IntPtr lpFileMappingAttributes, uint flProtect, 649 // uint dwMaximumSizeHigh, 650 // uint dwMaximumSizeLow, string lpName); 651 // /// 652 // /// 内存映射文件 653 // /// 654 // /// 655 // /// 656 // /// 657 // /// 658 // /// 659 // /// 660 // [DllImport("kernel32.dll")] 661 // internal static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint 662 // dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, 663 // uint dwNumberOfBytesToMap); 664 665 // /// 666 // /// 撤消文件映像 667 // /// 668 // /// 669 // /// 670 // [DllImport("kernel32.dll")] 671 // internal static extern bool UnmapViewOfFile(IntPtr lpBaseAddress); 672 673 // /// 674 // /// 关闭内核对象句柄 675 // /// 676 // /// 677 // /// 678 // [DllImport("kernel32.dll")] 679 // internal static extern bool CloseHandle(IntPtr hObject); 680 681 // /// 682 // /// 打开要映射的文件 683 // /// 684 // /// 685 // /// 686 // /// 687 // /// 688 // /// 689 // /// 690 // /// 691 // /// 692 // [DllImport("kernel32.dll")] 693 // internal static extern IntPtr CreateFile(string lpFileName, 694 // uint dwDesiredAccess, FileShare dwShareMode, IntPtr securityAttrs, 695 // FileMode dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile); 696 // /// 697 // /// 得到文件大小 698 // /// 699 // /// 700 // /// 701 // /// 702 // [DllImport("kernel32.dll", SetLastError = true)] 703 // internal static extern uint GetFileSize(IntPtr hFile, out uint highSize); 704 705 // /// 706 // /// 得到系统信息 707 // /// 708 // /// 709 // [DllImport("kernel32.dll", SetLastError = true)] 710 // internal static extern void GetSystemInfo(ref SYSTEM_INFO lpSystemInfo); 711 712 // /// 713 // /// 使用内存文件映射得到文件内容 714 // /// 715 // /// 文件路径 716 // /// 717 // public StringBuilder GetFileContent(string path) 718 // { 719 // StringBuilder sb = new StringBuilder(); 720 // IntPtr fileHandle = CreateFile(path, 721 // GENERIC_READ | GENERIC_WRITE, FileShare.Read | FileShare.Write, 722 // IntPtr.Zero, FileMode.Open, 723 // FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, IntPtr.Zero); 724 // if (INVALID_HANDLE_VALUE != (int)fileHandle) 725 // { 726 // IntPtr mappingFileHandle = CreateFileMapping( 727 // fileHandle, IntPtr.Zero, PAGE_READWRITE, 0, 0, "~MappingTemp"); 728 // if (mappingFileHandle != IntPtr.Zero) 729 // { 730 // SYSTEM_INFO systemInfo = new SYSTEM_INFO(); ; 731 // GetSystemInfo(ref systemInfo); 732 // //得到系统页分配粒度 733 // uint allocationGranularity = systemInfo.dwAllocationGranularity; 734 // uint fileSizeHigh=0; 735 // //get file size 736 // uint fileSize = GetFileSize(fileHandle, out fileSizeHigh); 737 // fileSize |= (((uint)fileSizeHigh) << 32); 738 // //关闭文件句柄 739 // CloseHandle(fileHandle); 740 // uint fileOffset = 0; 741 // uint blockBytes = 1000 * allocationGranularity; 742 // if (fileSize < 1000 * allocationGranularity) 743 // blockBytes = fileSize; 744 // //分块读取内存,适用于几G的文件 745 // while (fileSize > 0) 746 // { 747 // // 映射视图,得到地址 748 // IntPtr lpbMapAddress = MapViewOfFile(mappingFileHandle, FILE_MAP_COPY | FILE_MAP_READ | FILE_MAP_WRITE, 749 // (uint)(fileOffset >> 32), (uint)(fileOffset & 0xFFFFFFFF), 750 // blockBytes); 751 // if (lpbMapAddress == IntPtr.Zero) 752 // { 753 // return sb; 754 // } 755 // // 对映射的视图进行访问 756 // byte[] temp = new byte[blockBytes]; 757 // //从非托管的内存中复制内容到托管的内存中 758 // Marshal.Copy(lpbMapAddress, temp, 0, (int)blockBytes); 759 760 // //用循环太慢了,文件有几M的时候就慢的要死,还是用上面的方法直接 761 // //for (uint i = 0; i < dwBlockBytes; i++) 762 // //{ 763 // // byte vTemp = Marshal.ReadByte((IntPtr)((int)lpbMapAddress + i)); 764 // // temp[i] = vTemp; 765 // //} 766 767 // //此时用ASCII解码比较快,但有中文会有乱码,用gb2312即ANSI编码也比较快,16M的文件大概4秒就读出来了 768 // //但用unicode解码,文件大的时候会非常慢,会现卡死的状态,不知道为什么? 769 // //ASCIIEncoding encoding = new ASCIIEncoding(); 770 // //System.Text.UnicodeEncoding encoding = new UnicodeEncoding(); 771 // //sb.Append(encoding.GetString(temp)); 772 // sb.Append(System.Text.Encoding.GetEncoding("gb2312").GetString(temp)); 773 // // 撤消文件映像 774 // UnmapViewOfFile(lpbMapAddress); 775 // // 修正参数 776 // fileOffset += blockBytes; 777 // fileSize -= blockBytes; 778 // } 779 780 // //close file mapping handle 781 // CloseHandle(mappingFileHandle); 782 // } 783 // } 784 // return sb; 785 // } 786 787 // } 788 //} 789 #endregion 内存映射文件_dll   8)、内存映射文件(共享内存)      1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Text; 7 using System.Windows.Forms; 8 9 namespace 内存映射文件_共享内存_ 10 { 11 public partial class Form1 : Form 12 { 13 public Form1() 14 { 15 InitializeComponent(); 16 } 17 18 ShareMem MemDB = new ShareMem(); 19 20 private void btnOpen_Click(object sender, EventArgs e) 21 { 22 if (MemDB.Init("YFMemTest", 10240) != 0) 23 { 24 //初始化失败 25 MessageBox.Show("初始化失败"); 26 } 27 else 28 { 29 btnOpen.Enabled = false; 30 chkWrite.Enabled = true; 31 tmrTime.Enabled = true; 32 } 33 } 34 35 private void tmrTime_Tick(object sender, EventArgs e) 36 { 37 byte[] bytData = new byte[16]; 38 int intRet = MemDB.Read(ref bytData, 0, 16); 39 lstData.Items.Clear(); 40 if (intRet == 0) 41 { 42 for (int i = 0; i < 16; i++) 43 { 44 lstData.Items.Add(bytData[i].ToString()); 45 } 46 47 if (chkWrite.Checked) 48 { 49 bytData[0]++; 50 bytData[1] += 2; 51 if (bytData[0] > 200) bytData[0] = 0; 52 if (bytData[1] > 200) bytData[1] = 0; 53 MemDB.Write(bytData, 0, 16); 54 } 55 } 56 } 57 } 58 } 内存映射文件_共享内存 练习: 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO; 6 using System.IO.MemoryMappedFiles; 7 using System.Threading; 8 //CreateNew 和 CreateOrOpen 方法创建一个未映射到磁盘上的现有文件的内存映射文件 9 namespace 练习1非持久文件内存映射 10 { 11 class Program 12 { 13 // Process A: 14 static void Main(string[] args) 15 { 16 using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("字符串的知识.txt", 10000)) 17 { 18 bool mutexCreated; 19 Mutex mutex = new Mutex(true, "testmapmutex", out mutexCreated); 20 using (MemoryMappedViewStream stream = mmf.CreateViewStream()) 21 { 22 BinaryWriter writer = new BinaryWriter(stream); 23 writer.Write(1); 24 } 25 mutex.ReleaseMutex(); 26 27 Console.WriteLine("Start Process B and press ENTER to continue."); 28 Console.ReadLine(); 29 30 Console.WriteLine("Start Process C and press ENTER to continue."); 31 Console.ReadLine(); 32 33 mutex.WaitOne(); 34 using (MemoryMappedViewStream stream = mmf.CreateViewStream()) 35 { 36 BinaryReader reader = new BinaryReader(stream); 37 Console.WriteLine("Process A says: {0}", reader.ReadBoolean()); 38 Console.WriteLine("Process B says: {0}", reader.ReadBoolean()); 39 Console.WriteLine("Process C says: {0}", reader.ReadBoolean()); 40 } 41 mutex.ReleaseMutex(); 42 } 43 Console.ReadKey(); 44 } 45 } 46 } 非持久文件内存映射 1 using System; 2 using System.IO; 3 using System.IO.MemoryMappedFiles; 4 using System.Text; 5 6 namespace ConsoleDemo 7 { 8 class Program 9 { 10 private const string TXT_FILE_PATH = @"a.txt"; 11 private const string SPLIT_VARCHAR = "囧"; 12 private const char SPLIT_CHAR = '囧'; 13 private static long FILE_SIZE = 0; 14 static void Main(string[] args) 15 { 16 //long ttargetRowNum = 39999999; 17 long ttargetRowNum = 10000000; 18 DateTime beginTime = DateTime.Now; 19 string line = CreateMemoryMapFile(ttargetRowNum); 20 double totalSeconds = DateTime.Now.Subtract(beginTime).TotalSeconds; 21 Console.WriteLine(line); 22 Console.WriteLine(string.Format("查找第{0}行,共耗时:{1}s", ttargetRowNum, totalSeconds)); 23 Console.ReadLine(); 24 } 25 26 /// 27 /// 创建内存映射文件 28 /// 29 private static string CreateMemoryMapFile(long ttargetRowNum) 30 { 31 string line = string.Empty; 32 using (FileStream fs = new FileStream(TXT_FILE_PATH, FileMode.Open, FileAccess.ReadWrite)) 33 { 34 long targetRowNum = ttargetRowNum + 1;//目标行 35 long curRowNum = 1;//当前行 36 FILE_SIZE = fs.Length; 37 using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(fs, "test", fs.Length, MemoryMappedFileAccess.ReadWrite, null, HandleInheritability.None, false)) 38 { 39 long offset = 0; 40 //int limit = 250; 41 int limit = 200; 42 try 43 { 44 StringBuilder sbDefineRowLine = new StringBuilder(); 45 do 46 { 47 long remaining = fs.Length - offset; 48 using (MemoryMappedViewStream mmStream = mmf.CreateViewStream(offset, remaining > limit ? limit : remaining)) 49 //using (MemoryMappedViewStream mmStream = mmf.CreateViewStream(offset, remaining)) 50 { 51 offset += limit; 52 using (StreamReader sr = new StreamReader(mmStream)) 53 { 54 //string ss = sr.ReadToEnd().ToString().Replace("\n", "囧").Replace(Environment.NewLine, "囧"); 55 string ss = sr.ReadToEnd().ToString().Replace("\n", SPLIT_VARCHAR).Replace(Environment.NewLine, SPLIT_VARCHAR); 56 if (curRowNum <= targetRowNum) 57 { 58 if (curRowNum < targetRowNum) 59 { 60 string s = sbDefineRowLine.ToString(); 61 int pos = s.LastIndexOf(SPLIT_CHAR); 62 if (pos > 0) 63 sbDefineRowLine.Remove(0, pos); 64 65 } 66 else 67 { 68 line = sbDefineRowLine.ToString(); 69 return line; 70 } 71 if (ss.Contains(SPLIT_VARCHAR)) 72 { 73 curRowNum += GetNewLineNumsOfStr(ss); 74 sbDefineRowLine.Append(ss); 75 } 76 else 77 { 78 sbDefineRowLine.Append(ss); 79 } 80 } 81 //sbDefineRowLine.Append(ss); 82 //line = sbDefineRowLine.ToString(); 83 //if (ss.Contains(Environment.NewLine)) 84 //{ 85 // ++curRowNum; 86 // //curRowNum++; 87 // //curRowNum += GetNewLineNumsOfStr(ss); 88 // //sbDefineRowLine.Append(ss); 89 //} 90 //if (curRowNum == targetRowNum) 91 //{ 92 // string s = ""; 93 //} 94 95 sr.Dispose(); 96 } 97 98 mmStream.Dispose(); 99 } 100 } while (offset < fs.Length); 101 } 102 catch (Exception e) 103 { 104 Console.WriteLine(e.Message); 105 } 106 return line; 107 } 108 } 109 } 110 111 private static long GetNewLineNumsOfStr(string s) 112 { 113 string[] _lst = s.Split(SPLIT_CHAR); 114 return _lst.Length - 1; 115 } 116 } 117 } 验证内存映射文件读取的速度  有个进程+映射综合程序自己写!!!!!    四、网络编程 了解部分: 1.tup 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Net; 6 using System.Net.Sockets; 7 8 namespace _2.s 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 byte[] SendBuf = Encoding.UTF8.GetBytes("Hello!"); //发给客户端的消息; 15 byte[] RecvBuf = new byte[1024]; //申请接收缓存; 16 int RecvBytes = 0; //接收字节数; 17 string recvmsg = null; //接收消息; 18 19 IPEndPoint localEP = new IPEndPoint(IPAddress.Parse("172.16.2.93"), 6666); //本地端地址 20 TcpListener Listener = new TcpListener(localEP); //建立监听类,并绑定到指定的端地址 21 Listener.Start(10); //开始监听 22 Console.WriteLine("服务器开始监听—等待客户连接...."); 23 TcpClient remoteClient = Listener.AcceptTcpClient(); //等待连接(阻塞) 24 Console.WriteLine("用户:{0} 已连接!", remoteClient.Client.RemoteEndPoint.ToString()); //打印客户端连接信息; 25 remoteClient.Client.Send(SendBuf); //发送欢迎信息; 26 27 try 28 { 29 while (true) 30 { 31 RecvBytes = remoteClient.Client.Receive(RecvBuf); //接受服务器发送过来的消息; 32 recvmsg = Encoding.UTF8.GetString(RecvBuf, 0, RecvBytes); //将接受到的字节码转化为string类型; 33 Console.WriteLine("用户: {0}.", recvmsg); //打印我是毕小帅; 34 35 //string str = Console.ReadLine(); 36 //byte[] Sendf = Encoding.UTF8.GetBytes(str); 37 //remoteClient.Client.Send(Sendf); 38 } 39 } 40 catch 41 { 42 remoteClient.Close(); //关闭连接; 43 Console.Read(); 44 } 45 } 46 } 47 } server 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Net; 6 using System.Net.Sockets; 7 8 namespace _1.c 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 byte[] SendBuf = Encoding.UTF8.GetBytes("我是毕小帅"); //发给服务端的消息; 15 byte[] RecvBuf = new byte[1024]; //申请接收缓存; 16 int RecvBytes = 0; //接收字节数; 17 string recvmsg = null; //接收消息; 18 //127.0.0.1 19 IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse("172.16.2.93"), 6666);//远程服务器端地址; 20 TcpClient remoteServer = new TcpClient();//创建TCPClient类来与服务器通信; 21 remoteServer.Connect(remoteEP); //调用connect方法连接远端服务器; 22 Console.WriteLine("我的IP地址: {0}.", remoteServer.Client.LocalEndPoint);//打印自己使用的端地址; 23 RecvBytes = remoteServer.Client.Receive(RecvBuf); //接受服务器发送过来的消息; 24 recvmsg = Encoding.UTF8.GetString(RecvBuf, 0, RecvBytes); //将接受到的字节码转化为string类型; 25 Console.WriteLine("服务器: {0}.", recvmsg); //打印欢迎信息; 26 27 remoteServer.Client.Send(SendBuf); //发给服务端的消息 28 29 while (true) 30 { 31 //RecvBytes = remoteServer.Client.Receive(RecvBuf); 32 //recvmsg = Encoding.UTF8.GetString(RecvBuf, 0, RecvBytes); 33 //Console.WriteLine("用户: {0}.", recvmsg); 34 35 string str = Console.ReadLine(); 36 byte[] Sendf = Encoding.UTF8.GetBytes(str); 37 remoteServer.Client.Send(Sendf); 38 } 39 Console.Read(); 40 } 41 } 42 } client 2.udp注意:udp没有服务端这个概念,我只是模拟形容个服务端 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Net.Sockets; 7 using System.Net; 8 using System.Threading; 9 namespace UDP_Server 10 { 11 class Program 12 { 13 static Socket server; 14 static void Main(string[] args) 15 { 16 server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);//定义udp 17 server.Bind(new IPEndPoint(IPAddress.Parse("172.16.2.93"), 6001));//绑定端口号和IP 18 Console.WriteLine("服务端已经开启"); 19 Thread t = new Thread(ReciveMsg);//开启接收消息线程 20 t.Start(); 21 22 Thread t2 = new Thread(sendMsg);//开启发送消息线程 23 t2.Start(); 24 } 25 /// 26 /// 向特定ip的主机的端口发送数据报 27 /// 28 static void sendMsg() 29 { 30 EndPoint point = new IPEndPoint(IPAddress.Parse("172.16.2.93"), 6000); 31 while (true) 32 { 33 string msg = Console.ReadLine(); 34 server.SendTo(Encoding.UTF8.GetBytes(msg), point); 35 } 36 } 37 /// 38 /// 接收发送给本机ip对应端口号的数据报 39 /// 40 static void ReciveMsg() 41 { 42 while (true) 43 { 44 EndPoint point = new IPEndPoint(IPAddress.Any, 0);//用来保存发送方的ip和端口号 45 byte[] buffer = new byte[1024]; 46 int length = server.ReceiveFrom(buffer, ref point);//接收数据报 47 string message = Encoding.UTF8.GetString(buffer, 0, length); 48 Console.WriteLine(point.ToString() + message); 49 } 50 } 51 } 52 } UDP_Server 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Net; 7 using System.Net.Sockets; 8 using System.Threading; 9 namespace UDP_client 10 { 11 class Program 12 { 13 static Socket client; 14 static void Main(string[] args) 15 { 16 client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 17 client.Bind(new IPEndPoint(IPAddress.Parse("172.16.2.93"), 6000)); 18 Thread t = new Thread(sendMsg); 19 t.Start(); 20 Thread t2 = new Thread(ReciveMsg); 21 t2.Start(); 22 Console.WriteLine("客户端已经开启"); 23 } 24 /// 25 /// 向特定ip的主机的端口发送数据报 26 /// 27 static void sendMsg() 28 { 29 EndPoint point = new IPEndPoint(IPAddress.Parse("172.16.2.93"), 6001); 30 while (true) 31 { 32 string msg = Console.ReadLine(); 33 client.SendTo(Encoding.UTF8.GetBytes(msg), point); 34 } 35 } 36 37 /// 38 /// 接收发送给本机ip对应端口号的数据报 39 /// 40 static void ReciveMsg() 41 { 42 while (true) 43 { 44 EndPoint point = new IPEndPoint(IPAddress.Any, 0);//用来保存发送方的ip和端口号 45 byte[] buffer = new byte[1024]; 46 int length = client.ReceiveFrom(buffer, ref point);//接收数据报 47 string message = Encoding.UTF8.GetString(buffer, 0, length); 48 Console.WriteLine(point.ToString() + message); 49 } 50 } 51 } 52 } UDP_client 3.标准的socket 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Net; 6 using System.Net.Sockets; 7 8 //服务器端: 9 //第一步:用指定的端口号和服务器的ip建立一个EndPoint对像; 10 //第二步:建立一个Socket对像; 11 //第三步:用socket对像的Bind()方法绑定EndPoint; 12 //第四步:用socket对像的Listen()方法开始监听; 13 //第五步:接受到客户端的连接,用socket对像的Accept()方法创建新的socket对像用于和请求的客户端进行通信; 14 //第六步:通信结束后一定记得关闭socket; 15 namespace 标准的socket_server_ 16 { 17 class Program 18 { 19 static void Main(string[] args) 20 { 21 int port = 2000; 22 string host = "127.0.0.1"; 23 24 ///创建终结点(EndPoint) 25 IPAddress ip = IPAddress.Parse(host);//把ip地址字符串转换为IPAddress类型的实例 26 IPEndPoint ipe = new IPEndPoint(ip, port);//用指定的端口和ip初始化IPEndPoint类的新实例 27 28 ///创建socket并开始监听 29 Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建一个socket对像,如果用udp协议,则要用SocketType.Dgram类型的套接字 30 s.Bind(ipe);//绑定EndPoint对像(2000端口和ip地址) 31 s.Listen(0);//开始监听 32 Console.WriteLine("等待客户端连接"); 33 34 ///接受到client连接,为此连接建立新的socket,并接受信息 35 Socket temp = s.Accept();//为新建连接创建新的socket 36 Console.WriteLine("建立连接"); 37 string recvStr = ""; 38 byte[] recvBytes = new byte[1024]; 39 int bytes; 40 bytes = temp.Receive(recvBytes, recvBytes.Length, 0);//从客户端接受信息 41 recvStr += Encoding.ASCII.GetString(recvBytes, 0, bytes); 42 43 ///给client端返回信息 44 Console.WriteLine("server get message:{0}", recvStr);//把客户端传来的信息显示出来 45 string sendStr = "ok!Client send message successful!"; 46 byte[] bs = Encoding.ASCII.GetBytes(sendStr); 47 temp.Send(bs, bs.Length, 0);//返回信息给客户端 48 temp.Close(); 49 s.Close(); 50 Console.ReadLine(); 51 } 52 } 53 } 标准的socket_server 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Net; 6 using System.Net.Sockets; 7 8 //第一步:用指定的端口号和服务器的ip建立一个EndPoint对像; 9 //第二步:建立一个Socket对像; 10 //第三步:用socket对像的Connect()方法以上面建立的EndPoint对像做为参数,向服务器发出连接请求; 11 //第四步:如果连接成功,就用socket对像的Send()方法向服务器发送信息; 12 //第五步:用socket对像的Receive()方法接受服务器发来的信息 ; 13 //第六步:通信结束后一定记得关闭socket; 14 namespace 标准socket_client_ 15 { 16 class Program 17 { 18 static void Main(string[] args) 19 { 20 try 21 { 22 int port = 2000; 23 string host = "127.0.0.1"; 24 ///创建终结点EndPoint 25 IPAddress ip = IPAddress.Parse(host); 26 //IPAddress ipp = new IPAddress("127.0.0.1"); 27 IPEndPoint ipe = new IPEndPoint(ip, port);//把ip和端口转化为IPEndpoint实例 28 29 ///创建socket并连接到服务器 30 Socket c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建Socket 31 Console.WriteLine("Conneting…"); 32 c.Connect(ipe);//连接到服务器 33 34 ///向服务器发送信息 35 string sendStr = "hello!This is a socket test"; 36 byte[] bs = Encoding.ASCII.GetBytes(sendStr);//把字符串编码为字节 37 Console.WriteLine("Send Message"); 38 c.Send(bs, bs.Length, 0);//发送信息 39 40 ///接受从服务器返回的信息 41 string recvStr = ""; 42 byte[] recvBytes = new byte[1024]; 43 int bytes; 44 bytes = c.Receive(recvBytes, recvBytes.Length, 0);//从服务器端接受返回信息 45 recvStr += Encoding.ASCII.GetString(recvBytes, 0, bytes); 46 Console.WriteLine("client get message:{0}", recvStr);//显示服务器返回信息 47 ///一定记着用完socket后要关闭 48 c.Close(); 49 } 50 catch (ArgumentNullException e) 51 { 52 Console.WriteLine("argumentNullException: {0}", e); 53 } 54 catch (SocketException e) 55 { 56 Console.WriteLine("SocketException:{0}", e); 57 } 58 Console.WriteLine("Press Enter to Exit"); 59 Console.ReadKey(); 60 } 61 } 62 } 标准socket_client   详细讲解: 1.复习委托机制     1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Threading; 10 11 namespace _1.委托机制 12 { 13 public partial class Form1 : Form 14 { 15 //第一步: 16 //定义委托 17 delegate void ChengekjDelegate(int value); 18 //声明委托 19 ChengekjDelegate chengekjDelegate; 20 21 public Form1() 22 { 23 InitializeComponent(); 24 } 25 26 private void butStart_Click(object sender, EventArgs e) 27 { 28 //第四步: 29 //实例委托 30 chengekjDelegate = new ChengekjDelegate(SetprogressBarValue1); 31 SetprogressBarValue(chengekjDelegate); 32 33 //这里不用担心实例化两次会不会产生什么错误,主线程是按顺序从上到下执行 34 //没有开启新的线程,所以这里执行的线程只有主线程,第一个实例结束后才能开始第二个 35 chengekjDelegate = new ChengekjDelegate(SetprogessBarValue2); 36 SetprogressBarValue(chengekjDelegate); 37 } 38 39 //第五步: 40 //创建一个调用这个委托的方法 41 private void SetprogressBarValue(ChengekjDelegate setvalue) 42 { 43 for (int i = 0; i <= 100; i++) 44 { 45 Application.DoEvents();//实验时可以用它,做项目时看需求再说,这是阻塞直线程的 46 Thread.Sleep(50); 47 //这时把setvalue看成SetprogessBar1(参数)方法 48 setvalue(i); 49 } 50 } 51 52 //这是第二步: 53 //要进行委托的方法,设置进度条1的方法 54 private void SetprogressBarValue1(int value) 55 { 56 progressBar1.Value = value; 57 } 58 //设置进度条2的方法 59 private void SetprogessBarValue2(int value) 60 { 61 progressBar2.Value = value; 62 } 63 } 64 } 委托机制 细心一些会发现:点击开始之后,拖动窗体进度条会停止不动。进度条在滚动时突然关闭程序会发现程序还在运行没有立即停止。 2.复习回调机制 跟第一图是一样的 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Threading; 10 11 //回调=线程+委托 12 namespace _2.回调机制 13 { 14 public partial class Form1 : Form 15 { 16 //定义回调 17 private delegate void SetprogressBarValueht(int value); 18 //声明回调 19 private SetprogressBarValueht setprogressBarValueht; 20 21 public Form1() 22 { 23 InitializeComponent(); 24 } 25 26 private void butStart_Click(object sender, EventArgs e) 27 { 28 //CheckForIllegalCrossThreadCalls = false;//虽然它是比较万能的跨线程操作,不建议用它,建议回调 29 //用委托替代上面的这个跨线程 30 setprogressBarValueht = new SetprogressBarValueht(SetValuewt); 31 Thread setValueThread = new Thread(SetprogressBar2); 32 setValueThread.IsBackground = true; 33 setValueThread.Start(); 34 35 for (int i = 0; i <= 100; i++) 36 { 37 Application.DoEvents(); 38 progressBar1.Value = i; 39 Thread.Sleep(50); 40 } 41 } 42 43 //设置进度条2线程的方法 44 private void SetprogressBar2() 45 { 46 for (int i = 0; i <= 100; i++) 47 { 48 //progressBar2.Value = i;//当用委托回调时这行也要改了 49 progressBar2.Invoke(setprogressBarValueht, i); 50 Thread.Sleep(50); 51 } 52 } 53 54 //设置进度条2的 被委托的方法 55 private void SetValuewt(int value) 56 { 57 progressBar2.Value = value; 58 } 59 } 60 } 回调机制 点击开始之后,拖动进度条,明显发现不同 3.TCP服务端 1 using System; 2 using System.Net; 3 using System.Net.Sockets; 4 using System.Text; 5 using System.Data; 6 using System.Drawing; 7 using System.Windows.Forms; 8 using System.Threading; 9 using System.Collections.Generic; 10 using System.ComponentModel; 11 12 namespace TCP服务端 13 { 14 public partial class frmMain : Form 15 { 16 // 显示接收消息回调 17 private delegate void ShowReceiveMsgCallBack(string text); 18 private ShowReceiveMsgCallBack showReceiveMsgCallBack; 19 20 // 网络连接相关 21 private TcpListener myTcpListener; // TCP连接侦听器 22 private NetworkStream ns; // 网络数据流 23 private TcpClient tcpClient; // TCP客户端 24 private Thread TcpClientAcceptThread; // 客户端连接监听线程 25 26 // 窗体构造方法 27 public frmMain() 28 { 29 InitializeComponent(); 30 } 31 32 // 窗体启动加载事件 33 private void frmMain_Load(object sender, EventArgs e) 34 { 35 // 初始化回调 36 showReceiveMsgCallBack = new ShowReceiveMsgCallBack(ShowReceiveMsg); 37 } 38 39 // 开始监听按钮被单击事件 40 private void btnStartListen_Click(object sender, EventArgs e) 41 { 42 try 43 { 44 // 创建并实例化IP终端结点 45 IPEndPoint ipEndPoint = 46 new IPEndPoint(IPAddress.Parse(txtServerIP.Text), Convert.ToInt32(txtServerPort.Text)); 47 // 实例化TCP连接侦听器 48 myTcpListener = new TcpListener(ipEndPoint); 49 // 启动TCP连接侦听器 50 myTcpListener.Start(); 51 // 创建并启动TCP连接侦听接受线程 52 TcpClientAcceptThread = new Thread(TcpClientAccept); 53 TcpClientAcceptThread.IsBackground = true; 54 TcpClientAcceptThread.Start(); 55 // 修改控件状态 56 btnStartListen.Enabled = false; 57 btnStopListen.Enabled = true; 58 } 59 catch (Exception ex) 60 { 61 MessageBox.Show(ex.Message); 62 } 63 } 64 65 // 停止监听按钮被单击事件 66 private void btnStopListen_Click(object sender, EventArgs e) 67 { 68 // 停止监听 69 myTcpListener.Stop(); 70 // 终止监听线程 71 TcpClientAcceptThread.Abort(); 72 // 修改控件状态 73 btnStartListen.Enabled = true; 74 btnStopListen.Enabled = false; 75 } 76 77 // TCP连接侦听接受线程 78 private void TcpClientAccept() 79 { 80 try 81 { 82 // 接收TCP客户端 83 tcpClient = myTcpListener.AcceptTcpClient(); 84 // 获取绑定的网络数据流 85 ns = tcpClient.GetStream(); 86 87 // 循环接收消息 88 while (true) 89 { 90 int readLen = tcpClient.Available; 91 if (readLen > 0) 92 { 93 // 创建并实例化用于存放数据的字节数组 94 byte[] getData = new byte[readLen]; 95 // 从网络数据流中读取数据 96 ns.Read(getData, 0, getData.Length); 97 // 将字节数组转换为文本形式 98 string getMsg = Encoding.Default.GetString(getData); 99 // 将消息添加到接收消息列表中 100 lstReceiveMsg.Invoke(showReceiveMsgCallBack, getMsg); 101 Thread.Sleep(5000); 102 // 返回相同消息给客户端 103 ns.Write(getData, 0, getData.Length); 104 } 105 } 106 } 107 catch (ThreadAbortException) 108 { 109 // 人为终止线程 110 } 111 catch (Exception ex) 112 { 113 // 连接发生异常 114 MessageBox.Show(ex.Message); 115 // 释放相关系统资源 116 if (tcpClient != null) 117 tcpClient.Close(); 118 if (ns != null) 119 ns.Dispose(); 120 } 121 } 122 123 // 显示接收消息回调方法 124 private void ShowReceiveMsg(string text) 125 { 126 lstReceiveMsg.Items.Add(text); 127 } 128 } 129 } TCP服务端 4.同步TCP客户端 1 using System; 2 using System.Net; 3 using System.Net.Sockets; 4 using System.Text; 5 using System.Data; 6 using System.Drawing; 7 using System.Windows.Forms; 8 using System.Threading; 9 using System.Collections.Generic; 10 using System.ComponentModel; 11 12 namespace 同步TCP客户端 13 { 14 public partial class frmMain : Form 15 { 16 // 显示接收消息回调 17 private delegate void ShowReceiveMsgCallBack(string msg); 18 private ShowReceiveMsgCallBack showReceiveMsgCallBack; 19 // 修改进度条值回调 20 private delegate void SetProgressBarValueCallBack(); 21 private SetProgressBarValueCallBack setProgressBarValueCallBack; 22 23 // * 网络连接相关 * 24 private TcpClient myTcpClient; // TCP客户端 25 private NetworkStream ns; // 网络数据流 26 private Thread receiveMsgThread; // 接收消息线程 27 28 29 // 窗体构造方法 30 public frmMain() 31 { 32 InitializeComponent(); 33 } 34 35 // 窗体启动加载事件 36 private void frmMain_Load(object sender, EventArgs e) 37 { 38 // 初始化回调 39 showReceiveMsgCallBack = new ShowReceiveMsgCallBack(ShowReceiveMsg); 40 setProgressBarValueCallBack = new SetProgressBarValueCallBack(SetProgressBarValue); 41 } 42 43 // 发起连接按钮被单击事件 44 private void btnConnect_Click(object sender, EventArgs e) 45 { 46 // 创建并实例化IP终端结点 47 IPEndPoint ipEndPoint = 48 new IPEndPoint(IPAddress.Parse(txtServerIP.Text), Convert.ToInt32(txtServerPort.Text)); 49 // 实例化TCP客户端 50 myTcpClient = new TcpClient(); 51 52 try 53 { 54 // 发起TCP连接 55 myTcpClient.Connect(ipEndPoint); 56 // 获取绑定的网络数据流 57 ns = myTcpClient.GetStream(); 58 // 实例化并启动接收消息线程 59 receiveMsgThread = new Thread(ReceiveMsg); 60 receiveMsgThread.Start(); 61 // 修改控件状态 62 btnConnect.Enabled = false; 63 btnDisconnect.Enabled = true; 64 btnSendMsg.Enabled = true; 65 } 66 catch (Exception ex) 67 { 68 // 捕捉到连接异常 69 MessageBox.Show(ex.Message); 70 } 71 } 72 73 // 断开连接按钮被单击事件 74 private void btnDisconnect_Click(object sender, EventArgs e) 75 { 76 // 断开TCP连接 77 myTcpClient.Close(); 78 // 销毁绑定的网络数据流 79 ns.Dispose(); 80 // 销毁接收消息线程 81 receiveMsgThread.Abort(); 82 // 修改控件状态 83 btnConnect.Enabled = true; 84 btnDisconnect.Enabled = false; 85 btnSendMsg.Enabled = false; 86 } 87 88 // 发送消息按钮被单击事件 89 private void btnSendMsg_Click(object sender, EventArgs e) 90 { 91 // 将要发送的文本转换为字符数组形式 92 byte[] sendData = Encoding.Default.GetBytes(txtSendMsg.Text); 93 // 将数据写入到网络数据流中 94 ns.Write(sendData, 0, sendData.Length); 95 } 96 97 // 接收消息线程 98 private void ReceiveMsg() 99 { 100 while (true) 101 { 102 try 103 { 104 // 创建并实例化用于存放数据的字节数组 105 byte[] getData = new byte[1024]; 106 // 从网络数据流中读取数据 107 ns.Read(getData, 0, getData.Length); 108 // 将字节数组转换为文本形式 109 string getMsg = Encoding.Default.GetString(getData); 110 // 将消息添加到接收消息列表中 111 lstReceiveMsg.Invoke(showReceiveMsgCallBack, getMsg); 112 // 修改进度条值 113 pgProgressBar.Invoke(setProgressBarValueCallBack); 114 } 115 catch (ThreadAbortException) 116 { 117 // 捕捉到线程被终止异常则表示人为的断开TCP连接 118 // 不需要弹出错误提示 119 } 120 catch (Exception ex) 121 { 122 // 接收消息发生异常 123 MessageBox.Show(ex.Message); 124 // 释放相关系统资源 125 if (ns != null) 126 ns.Dispose(); 127 break; 128 } 129 } 130 } 131 132 // 显示接收消息方法 133 private void ShowReceiveMsg(string msg) 134 { 135 lstReceiveMsg.Items.Add(msg); 136 } 137 138 // 修改进度条值的方法 139 private void SetProgressBarValue() 140 { 141 // 判断是否已经到进度条最大值 142 if (pgProgressBar.Value < pgProgressBar.Maximum) 143 { 144 // 未到最大值,递增1 145 pgProgressBar.Value++; 146 } 147 else 148 { 149 // 已到最大值,回到起点0 150 pgProgressBar.Value = 0; 151 } 152 } 153 } 154 } 同步TCP客户端 5.异步TCP客户端 1 using System; 2 using System.Net; 3 using System.Net.Sockets; 4 using System.Text; 5 using System.Data; 6 using System.Drawing; 7 using System.Windows.Forms; 8 using System.Threading; 9 using System.Collections.Generic; 10 using System.ComponentModel; 11 12 namespace 异步TCP客户端 13 { 14 public partial class frmMain : Form 15 { 16 // 显示接收消息回调 17 private delegate void ShowReceiveMsgCallBack(string msg); 18 private ShowReceiveMsgCallBack showReceiveMsgCallBack; 19 // 修改进度条值回调 20 private delegate void SetProgressBarValueCallBack(); 21 private SetProgressBarValueCallBack setProgressBarValueCallBack; 22 // 接收消息回调 23 private delegate void ReceiveMsgCallBack(out string msg); 24 private ReceiveMsgCallBack receiveMsgCallBack; 25 26 // * 网络连接相关 * 27 private TcpClient myTcpClient; // TCP客户端 28 private NetworkStream ns; // 网络数据流 29 private Thread receiveMsgThread; // 接收消息线程 30 31 // 窗体构造方法 32 public frmMain() 33 { 34 InitializeComponent(); 35 } 36 37 // 窗体启动加载事件 38 private void frmMain_Load(object sender, EventArgs e) 39 { 40 // 初始化回调 41 showReceiveMsgCallBack = new ShowReceiveMsgCallBack(ShowReceiveMsg); 42 setProgressBarValueCallBack = new SetProgressBarValueCallBack(SetProgressBarValue); 43 receiveMsgCallBack = new ReceiveMsgCallBack(ReceiveMsgMethod); 44 } 45 46 // 发起连接按钮被单击事件 47 private void btnConnect_Click(object sender, EventArgs e) 48 { 49 // 创建连接异步回调 50 AsyncCallback connectCallBack = new AsyncCallback(ConnectMethod); 51 // 实例化TCP客户端 52 myTcpClient = new TcpClient(); 53 // 发起TCP异步连接 54 IAsyncResult result = myTcpClient.BeginConnect( 55 IPAddress.Parse(txtServerIP.Text), Convert.ToInt32(txtServerPort.Text), connectCallBack, myTcpClient); 56 MessageBox.Show("异步发起连接!"); 57 } 58 59 // 断开连接按钮被单击事件 60 private void btnDisconnect_Click(object sender, EventArgs e) 61 { 62 // 销毁接收消息线程 63 receiveMsgThread.Abort(); 64 } 65 66 // 发送消息按钮被单击事件 67 private void btnSendMsg_Click(object sender, EventArgs e) 68 { 69 // 将要发送的文本转换为字符数组形式 70 byte[] sendData = Encoding.Default.GetBytes(txtSendMsg.Text); 71 // 将数据写入到网络数据流中 72 ns.Write(sendData, 0, sendData.Length); 73 } 74 75 // 接收消息线程 76 private void ReceiveMsg() 77 { 78 while (true) 79 { 80 try 81 { 82 string getMsg = null; 83 // 开始异步接收消息 84 IAsyncResult result = receiveMsgCallBack.BeginInvoke(out getMsg, null, null); 85 // 循环滚动进度条 86 while (!result.IsCompleted) 87 { 88 pgProgressBar.Invoke(setProgressBarValueCallBack); 89 Application.DoEvents(); 90 Thread.Sleep(500); 91 } 92 // 结束异步接收消息 93 receiveMsgCallBack.EndInvoke(out getMsg, result); 94 95 if (getMsg != null) 96 { 97 // 将消息添加到接收消息列表中 98 lstReceiveMsg.Invoke(showReceiveMsgCallBack, getMsg); 99 } 100 } 101 catch (ThreadAbortException) 102 { 103 // 捕捉到线程被终止异常则表示人为的断开TCP连接 104 // 不需要弹出错误提示 105 } 106 catch (Exception ex) 107 { 108 // 接收消息发生异常 109 MessageBox.Show(ex.Message); 110 // 释放相关系统资源 111 if (ns != null) 112 ns.Dispose(); 113 break; 114 } 115 } 116 } 117 118 // 连接异步回调 119 private void ConnectMethod(IAsyncResult iar) 120 { 121 try 122 { 123 // 获取对象 124 myTcpClient = (TcpClient)iar.AsyncState; 125 // 结束异步连接 126 myTcpClient.EndConnect(iar); 127 // 获取绑定的网络数据流 128 ns = myTcpClient.GetStream(); 129 // 实例化并启动接收消息线程 130 receiveMsgThread = new Thread(ReceiveMsg); 131 receiveMsgThread.Start(); 132 } 133 catch (Exception ex) 134 { 135 // 捕捉到连接异常 136 MessageBox.Show(ex.Message); 137 } 138 } 139 140 // 接收消息异步回调 141 private void ReceiveMsgMethod(out string msg) 142 { 143 msg = null; 144 try 145 { 146 // 创建并实例化用于存放数据的字节数组 147 byte[] getData = new byte[1024]; 148 // 从网络数据流中读取数据 149 ns.Read(getData, 0, getData.Length); 150 // 将字节数组转换为文本形式 151 msg = Encoding.Default.GetString(getData); 152 } 153 catch (Exception ex) 154 { 155 // 接收消息发生异常 156 MessageBox.Show(ex.Message); 157 // 释放相关系统资源 158 if (ns != null) 159 ns.Dispose(); 160 } 161 } 162 163 // 显示接收消息方法 164 private void ShowReceiveMsg(string msg) 165 { 166 lstReceiveMsg.Items.Add(msg); 167 } 168 169 // 修改进度条值的方法 170 private void SetProgressBarValue() 171 { 172 // 判断是否已经到进度条最大值 173 if (pgProgressBar.Value < pgProgressBar.Maximum) 174 { 175 // 未到最大值,递增1 176 pgProgressBar.Value++; 177 } 178 else 179 { 180 // 已到最大值,回到起点0 181 pgProgressBar.Value = 0; 182 } 183 } 184 } 185 } 异步TCP客户端 6.UDP进程通信      1 using System; 2 using System.Net; 3 using System.Net.Sockets; 4 using System.Text; 5 using System.Data; 6 using System.Drawing; 7 using System.Windows.Forms; 8 using System.Threading; 9 using System.Collections.Generic; 10 using System.ComponentModel; 11 12 namespace UPD进程通信 13 { 14 public partial class frmMain : Form 15 { 16 // 显示消息回调 17 private delegate void ShowMsgCallBack(string text); 18 private ShowMsgCallBack showMsgCallBack; 19 20 private UdpClient myUdpClient; // UDP客户端 21 private UdpClient anonyUdpClient; // 匿名UDP客户端 22 23 // 窗构造方法 24 public frmMain() 25 { 26 InitializeComponent(); 27 } 28 29 // 窗体启动加载事件 30 private void frmMain_Load(object sender, EventArgs e) 31 { 32 // 初始化回调 33 showMsgCallBack = new ShowMsgCallBack(ShowMsg); 34 } 35 36 // 启动按钮被单击事件 37 private void btnStart_Click(object sender, EventArgs e) 38 { 39 // 创建并实例化IP终端结点 40 IPEndPoint ipEndPoint = 41 new IPEndPoint(IPAddress.Parse(txtLocalIP.Text), Convert.ToInt32(txtLocalPort.Text)); 42 // 实例化UDP客户端(可用于实名发送和接收) 43 myUdpClient = new UdpClient(ipEndPoint); 44 45 // 启动消息接收线程 46 Thread ReceiveMessageThread = new Thread(ReceiveMessage); 47 ReceiveMessageThread.IsBackground = true; 48 ReceiveMessageThread.Start(); 49 } 50 51 // 发送消息按钮被单击事件 52 private void btnSendMsg_Click(object sender, EventArgs e) 53 { 54 // 判断是否为匿名发送 55 if (chkAnony.Checked) 56 { 57 // 匿名发送(套接字绑定的端口由系统自动分配) 58 anonyUdpClient = new UdpClient(); 59 } 60 61 // 启动消息发送线程 62 Thread SendMessageThread = new Thread(SendMessage); 63 SendMessageThread.IsBackground = true; 64 SendMessageThread.Start(); 65 } 66 67 // 消息发送线程 68 private void SendMessage() 69 { 70 // 创建并实例化IP终端结点 71 IPEndPoint ipEndPoint = 72 new IPEndPoint(IPAddress.Parse(txtSendIP.Text), Convert.ToInt32(txtSendPort.Text)); 73 74 // 准备发送的数据 75 byte[] sendData = Encoding.Default.GetBytes(txtSendMsg.Text); 76 77 // 判断是否为匿名发送 78 if (chkAnony.Checked) 79 { 80 // 匿名发送 81 anonyUdpClient.Send(sendData, sendData.Length, ipEndPoint); 82 // 关闭匿名UDP客户端 83 anonyUdpClient.Close(); 84 } 85 else 86 { 87 // 实名发送 88 myUdpClient.Send(sendData, sendData.Length, ipEndPoint); 89 } 90 91 } 92 93 // 消息接收线程 94 private void ReceiveMessage() 95 { 96 // 创建并实例化IP终端结点 97 IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 0); 98 99 // 消息接收循环 100 while (true) 101 { 102 try 103 { 104 // 同步阻塞接收消息 105 byte[] getData = myUdpClient.Receive(ref ipEndPoint); 106 string msg = Encoding.Default.GetString(getData); 107 // 显示消息到列表框中 108 lstReceiveMsg.Invoke(showMsgCallBack, ipEndPoint.ToString() + ":" + msg); 109 } 110 catch (ThreadAbortException) 111 { 112 // 人为终止线程 113 } 114 catch (Exception ex) 115 { 116 // 接收发生异常 117 MessageBox.Show(ex.Message); 118 } 119 } 120 } 121 122 // 显示消息回调方法 123 private void ShowMsg(string text) 124 { 125 lstReceiveMsg.Items.Add(text); 126 } 127 } 128 } UPD进程通信 7.UDP广播和组播 UDP组播是采用的无连接,数据报的连接方式,所以是不可靠的.也就是数据能不能到达接受端和数据到达的顺序都是不能保证的.但是由于UDP不用保证数据的可靠性,所有数据的传送速度是很快的.  IP网络传输方式共分为单播,组播(多播),广播三种。   IP组播通信需要一个特殊的组播地址,IP组播地址是一组D类IP地址,范围从224.0.0.0 到  239.255.255.255。其中还有很多地址是为特殊的目的保留的。224.0.0.0到224.0.0.255的地址最好不要用,因为他们大多是 为了特殊的目的保持的(比如IGMP协议)    相对于极度消耗网络带宽的广播来说(广播只能在内网广播),UDP组播有了很大的优化,只有终端加入到了一个广播组,UDP组播的数据才能被他接收到。   1 using System; 2 using System.Net; 3 using System.Net.Sockets; 4 using System.Text; 5 using System.Data; 6 using System.Drawing; 7 using System.Windows.Forms; 8 using System.Threading; 9 using System.Collections.Generic; 10 using System.ComponentModel; 11 12 namespace 广播和组播 13 { 14 public partial class frmMain : Form 15 { 16 // 显示消息回调 17 private delegate void ShowMsgCallBack(string text); 18 private ShowMsgCallBack showMsgCallBack; 19 20 private UdpClient myUdpClient; // UDP客户端 21 22 // 窗体构造方法 23 public frmMain() 24 { 25 InitializeComponent(); 26 } 27 28 // 窗体启动加载事件 29 private void frmMain_Load(object sender, EventArgs e) 30 { 31 // 初始化回调 32 showMsgCallBack = new ShowMsgCallBack(ShowMsg); 33 } 34 35 // 启动按钮被单击事件 36 private void btnStart_Click(object sender, EventArgs e) 37 { 38 // 创建并实例化IP终端结点 39 IPEndPoint ipEndPoint = 40 new IPEndPoint(IPAddress.Parse(txtLocalIP.Text), Convert.ToInt32(txtLocalPort.Text)); 41 // 实例化UDP客户端(可用于实名发送和接收) 42 myUdpClient = new UdpClient(ipEndPoint); 43 44 // 判断是否加入组播 45 if (chkJoinMutliBroadcast.Checked) 46 { 47 // 加入组播,并设置路由器最大转发次数 48 myUdpClient.JoinMulticastGroup(IPAddress.Parse(txtMutliBroadcastIP.Text), 50); 49 } 50 51 // 启动消息接收线程 52 Thread ReceiveMessageThread = new Thread(ReceiveMessage); 53 ReceiveMessageThread.IsBackground = true; 54 ReceiveMessageThread.Start(); 55 } 56 57 // 发送消息按钮被单击事件 58 private void btnSendMsg_Click(object sender, EventArgs e) 59 { 60 // 创建IP终端结点 61 IPEndPoint ipEndPoint; 62 63 // 判断消息发送类型 64 if (chkBroadcast.Checked) 65 { 66 // 广播模式(由系统自动提供广播地址) 67 ipEndPoint = new IPEndPoint(IPAddress.Broadcast, 33333); 68 } 69 else 70 { 71 // 组播模式 72 ipEndPoint = new IPEndPoint(IPAddress.Parse(txtSendMutliBroadcastIP.Text), 33333); 73 } 74 75 // 准备发送的数据 76 byte[] sendData = Encoding.Default.GetBytes(txtSendMsg.Text); 77 myUdpClient.Send(sendData, sendData.Length, ipEndPoint); 78 } 79 80 // 消息接收线程 81 private void ReceiveMessage() 82 { 83 // 创建并实例化IP终端结点 84 IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 0); 85 86 // 消息接收循环 87 while (true) 88 { 89 try 90 { 91 // 同步阻塞接收消息 92 byte[] getData = myUdpClient.Receive(ref ipEndPoint); 93 string msg = Encoding.Default.GetString(getData); 94 // 显示消息到列表框中 95 lstReceiveMsg.Invoke(showMsgCallBack, ipEndPoint.ToString() + ":" + msg); 96 } 97 catch (ThreadAbortException) 98 { 99 // 人为终止线程 100 } 101 catch (Exception ex) 102 { 103 // 接收发生异常 104 MessageBox.Show(ex.Message); 105 } 106 } 107 } 108 109 // 显示消息回调方法 110 private void ShowMsg(string text) 111 { 112 lstReceiveMsg.Items.Add(text); 113 } 114 } 115 } 广播和组播 8.局域网聊天室 1 using System; 2 using System.Net; 3 using System.Net.Sockets; 4 using System.Text; 5 using System.Data; 6 using System.Drawing; 7 using System.Windows.Forms; 8 using System.Threading; 9 using System.Collections.Generic; 10 using System.ComponentModel; 11 12 namespace 局域网聊天室 13 { 14 public partial class frmServer : Form 15 { 16 // 添加客户端回调 17 private delegate void AddNewClientCallBack(string endPoint); 18 private AddNewClientCallBack addNewClientCallBack; 19 // 显示接收消息回调 20 private delegate void ShowReceiveMsgCallBack(string text); 21 private ShowReceiveMsgCallBack showReceiveMsgCallBack; 22 // 删除客户端回调 23 private delegate void DeleteClientCallBack(int index); 24 private DeleteClientCallBack deleteClientCallBack; 25 26 // * 网络连接相关 * 27 private TcpListener myTcpListener; // TCP连接侦听器 28 private TcpClient tcpClient; // TCP客户端 29 private Thread TcpClientAcceptThread; // 客户端连接监听线程 30 private Thread receiveMsgThread; // 接收消息线程 31 private Dictionary ClientSocketDic; // 保存客户端终端信息与Socket字典 32 33 // 窗体构造方法 34 public frmServer() 35 { 36 InitializeComponent(); 37 } 38 39 // 窗体启动加载事件 40 private void frmServer_Load(object sender, EventArgs e) 41 { 42 // 初始化属性 43 ClientSocketDic = new Dictionary(); 44 45 // 初始化回调 46 showReceiveMsgCallBack = new ShowReceiveMsgCallBack(ShowReceiveMsg); 47 addNewClientCallBack = new AddNewClientCallBack(AddNewClient); 48 deleteClientCallBack = new DeleteClientCallBack(DeleteClient); 49 } 50 51 // 启动服务端按钮被单击事件 52 private void btnStart_Click(object sender, EventArgs e) 53 { 54 try 55 { 56 // 创建并实例化IP终端结点 57 IPEndPoint ipEndPoint = 58 new IPEndPoint(IPAddress.Parse(txtServerIP.Text), int.Parse(txtServerPort.Text)); 59 // 实例化TCP连接侦听器 60 myTcpListener = new TcpListener(ipEndPoint); 61 // 启动TCP连接侦听器 62 myTcpListener.Start(); 63 // 创建并启动TCP连接侦听接受线程 64 TcpClientAcceptThread = new Thread(TcpClientAccept); 65 TcpClientAcceptThread.IsBackground = true; 66 TcpClientAcceptThread.Start(); 67 // 修改控件状态 68 btnStart.Enabled = false; 69 } 70 catch (Exception ex) 71 { 72 MessageBox.Show(ex.Message); 73 } 74 } 75 76 // TCP连接侦听接受线程 77 private void TcpClientAccept() 78 { 79 try 80 { 81 // 循环接收 82 while (true) 83 { 84 // 接收TCP客户端 85 tcpClient = myTcpListener.AcceptTcpClient(); 86 // 实例化并启动接收消息线程 87 receiveMsgThread = new Thread(ReceiveMsg); 88 receiveMsgThread.IsBackground = true; 89 receiveMsgThread.Start(); 90 // 将客户端终端信息加入列表 91 lstClient.Invoke(addNewClientCallBack, tcpClient.Client.RemoteEndPoint.ToString()); 92 // 将客户端加入字典 93 ClientSocketDic.Add(tcpClient.Client.RemoteEndPoint.ToString(), tcpClient.Client); 94 } 95 } 96 catch (ThreadAbortException) 97 { 98 // 人为终止线程 99 } 100 catch (Exception ex) 101 { 102 // 连接发生异常 103 MessageBox.Show(ex.Message); 104 // 释放相关系统资源 105 if (tcpClient != null) 106 tcpClient.Close(); 107 } 108 } 109 110 // 接收消息线程 111 private void ReceiveMsg() 112 { 113 // 获取客户端终端信息 114 string endPoint = tcpClient.Client.RemoteEndPoint.ToString(); 115 // 获取绑定的网络数据流 116 NetworkStream ns = tcpClient.GetStream(); 117 // 循环接收消息 118 while (true) 119 { 120 try 121 { 122 int readLen = tcpClient.Available; 123 if (readLen >= 0) 124 { 125 // 创建并实例化用于存放数据的字节数组 126 byte[] getData = new byte[1024]; 127 // 从网络数据流中读取数据 128 ns.Read(getData, 0, getData.Length); 129 // 将字节数组转换为文本形式 130 string getMsg = Encoding.Default.GetString(getData); 131 // 读取长度为0表示客户端已掉线 132 if (getMsg.Replace("\0", "").Length == 0) 133 { 134 for (int i = 0; i < lstClient.Items.Count; i++) 135 { 136 // 从客户端列表删除客户端 137 if (string.Equals(lstClient.Items[i].ToString(), endPoint)) 138 { 139 lstClient.Invoke(deleteClientCallBack, i); 140 } 141 } 142 143 // 将客户端从字典中删除 144 ClientSocketDic.Remove(endPoint); 145 break; 146 } 147 // 将消息添加到接收消息列表中 148 lstMsg.Invoke(showReceiveMsgCallBack, endPoint + ":" + getMsg); 149 // 向除该客户端外的所有客户端转发消息 150 for (int i = 0; i < lstClient.Items.Count; i++) 151 { 152 // 判断是否不为该客户端 153 if (!string.Equals(lstClient.Items[i].ToString(), endPoint)) 154 { 155 ClientSocketDic[lstClient.Items[i].ToString()].Send(Encoding.Default.GetBytes(endPoint + ":" + getMsg.Replace("\0", ""))); 156 } 157 } 158 } 159 } 160 catch (ThreadAbortException) 161 { 162 // 捕捉到线程被终止异常则表示人为的断开TCP连接 163 // 不需要弹出错误提示 164 } 165 catch (Exception ex) 166 { 167 // 接收消息发生异常 168 MessageBox.Show(ex.Message); 169 // 释放相关系统资源 170 if (ns != null) 171 ns.Dispose(); 172 break; 173 } 174 } 175 } 176 177 // 添加客户端回调方法 178 private void AddNewClient(string endPoint) 179 { 180 lstClient.Items.Add(endPoint); 181 } 182 183 // 显示接收消息回调方法 184 private void ShowReceiveMsg(string text) 185 { 186 lstMsg.Items.Add(text); 187 } 188 189 // 删除客户端回调党法 190 private void DeleteClient(int index) 191 { 192 lstClient.Items.RemoveAt(index); 193 } 194 } 195 } 局域网聊天室 9.WebClient 1 using System; 2 using System.Net; 3 using System.Text; 4 using System.Data; 5 using System.Drawing; 6 using System.Windows.Forms; 7 using System.Threading; 8 using System.Collections.Generic; 9 using System.ComponentModel; 10 11 namespace Web_Client 12 { 13 public partial class frmMain : Form 14 { 15 // 窗体构造方法 16 public frmMain() 17 { 18 InitializeComponent(); 19 } 20 21 // 获取源码按钮被单击事件 22 private void btnGetSource_Click(object sender, EventArgs e) 23 { 24 // 创建并实例化 WebClient 对象 25 WebClient webClient = new WebClient(); 26 // 取消代理设置(默认为开启,会造成程序假死) 27 webClient.Proxy = null; 28 // 获取网页源码 29 txtSource.Text = webClient.DownloadString(txtWebAddress.Text); 30 } 31 32 // 同步下载按钮被单击事件 33 private void btnSyncDownload_Click(object sender, EventArgs e) 34 { 35 // 创建并实例化 WebClient 对象 36 WebClient webClient = new WebClient(); 37 // 取消代理设置(默认为开启,会造成程序假死) 38 webClient.Proxy = null; 39 // 同步下载文件 40 webClient.DownloadFile(txtRemotePath.Text, txtLocalPath.Text); 41 MessageBox.Show("同步下载完成!"); 42 } 43 44 // 异步下载按钮被单击事件 45 private void btnAsyncDownload_Click(object sender, EventArgs e) 46 { 47 // 创建并实例化 WebClient 对象 48 WebClient webClient = new WebClient(); 49 // 取消代理设置(默认为开启,会造成程序假死) 50 webClient.Proxy = null; 51 // 关联事件 52 webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(webClient_DownloadFileCompleted); 53 webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged); 54 // 开始异步下载文件 55 webClient.DownloadFileAsync(new Uri(txtRemotePath.Text), txtLocalPath.Text); 56 } 57 58 // 异步下载文件进度改变事件 59 private void webClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) 60 { 61 //e.BytesReceived; // 已下载字节数 62 //e.TotalBytesToReceive; // 总下载字节数 63 // 可直接调用参数中的已下载百分比 64 pgDownload.Value = e.ProgressPercentage; 65 } 66 67 // 异步下载文件完成事件 68 private void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) 69 { 70 MessageBox.Show("异步下载完成!"); 71 } 72 } 73 } Web_Client 项目: 局域网: 1.tcp      1 using System; 2 using System.Drawing; 3 using System.Windows.Forms; 4 using System.Net.Sockets; 5 6 namespace Chatting 7 { 8 public partial class MainForm : Form 9 { 10 11 public MainForm() 12 { 13 InitializeComponent(); 14 } 15 16 SocketFunc socket; 17 System.Action ReceiveAction; 18 System.Action AccessAction; 19 20 private void MainForm_Load(object sender, EventArgs e) 21 { 22 //异步建立连接回调 23 AccessAction = () => 24 { 25 this.Invoke((MethodInvoker)delegate() 26 { 27 lblFriendIP.Visible = false; 28 txtIP.Visible = false; 29 btnConnect.Visible = false; 30 btnWaitAccess.Visible = false; 31 32 String friendIP = socket.communicateSocket.RemoteEndPoint.ToString(); 33 lblState.Text = String.Format("连接成功. 对方IP:{0}", friendIP); 34 35 try 36 { 37 socket.Receive(ReceiveAction); 38 } 39 catch (Exception exp) 40 { 41 MessageBox.Show(exp.Message, "错误"); 42 } 43 }); 44 45 }; 46 //异步接收消息回调 47 ReceiveAction = msg => 48 { 49 txtGetMsg.Invoke((MethodInvoker)delegate() 50 { 51 UpdateGetMsgTextBox(false, msg, Color.Red); 52 }); 53 }; 54 } 55 56 private void btnWaitAccess_Click(object sender, EventArgs e) 57 { 58 this.socket = new ServerSocket(); 59 try 60 { 61 this.socket.Access("", this.AccessAction); 62 } 63 catch (Exception ecp) 64 { 65 MessageBox.Show(ecp.Message, "错误"); 66 } 67 68 lblState.Text = "等待对方连接..."; 69 } 70 71 private void btnConnect_Click(object sender, EventArgs e) 72 { 73 this.socket = new ClientSocket(); 74 try 75 { 76 this.socket.Access(txtIP.Text, this.AccessAction); 77 } 78 catch (Exception ecp) 79 { 80 MessageBox.Show(ecp.Message, "错误"); 81 } 82 lblState.Text = "正在连接对方..."; 83 } 84 85 private void btnSendMsg_Click(object sender, EventArgs e) 86 { 87 string message = txtSendMsg.Text.Trim(); 88 if (string.IsNullOrEmpty(message)) 89 { 90 MessageBox.Show("消息内容不能为空!", "错误"); 91 txtSendMsg.Focus(); 92 return; 93 } 94 95 try 96 { 97 socket.Send(message); 98 } 99 catch(Exception ecp) 100 { 101 MessageBox.Show(ecp.Message, "错误"); 102 return; 103 } 104 105 UpdateGetMsgTextBox(true, message, Color.Blue); 106 txtSendMsg.Text = ""; 107 } 108 109 private void UpdateGetMsgTextBox(bool sendMsg, string message, Color color) 110 { 111 string appendText; 112 if (sendMsg == true) 113 { 114 appendText = "Me: " + System.DateTime.Now.ToString() 115 + Environment.NewLine 116 + message + Environment.NewLine; 117 } 118 else 119 { 120 appendText = "Friend: " + System.DateTime.Now.ToString() 121 + Environment.NewLine 122 + message + Environment.NewLine; 123 } 124 int txtGetMsgLength = txtGetMsg.Text.Length; 125 txtGetMsg.AppendText(appendText); 126 txtGetMsg.Select(txtGetMsgLength, appendText.Length - Environment.NewLine.Length*2 -message.Length); 127 txtGetMsg.SelectionColor = color; 128 129 txtGetMsg.ScrollToCaret(); 130 } 131 } 132 } 主窗体源码 1 using System; 2 using System.Net; 3 using System.Net.Sockets; 4 using System.Text; 5 6 namespace Chatting 7 { 8 public abstract class SocketFunc 9 { 10 //不管是服务端还是客户端, 建立连接后用这个Socket进行通信 11 public Socket communicateSocket = null; 12 13 //服务端和客户端建立连接的方式稍有不同, 子类会重载 14 public abstract void Access(string IP, System.Action AccessAciton); 15 16 //发送消息的函数 17 public void Send(string message) 18 { 19 if (communicateSocket.Connected == false) 20 { 21 throw new Exception("还没有建立连接, 不能发送消息"); 22 } 23 Byte[] msg = Encoding.UTF8.GetBytes(message); 24 communicateSocket.BeginSend(msg,0, msg.Length, SocketFlags.None, 25 ar => { 26 27 }, null); 28 } 29 30 //接受消息的函数 31 public void Receive(System.Action ReceiveAction) 32 { 33 //如果消息超过1024个字节, 收到的消息会分为(总字节长度/1024 +1)条显示 34 Byte[] msg = new byte[1024]; 35 //异步的接受消息 36 communicateSocket.BeginReceive(msg, 0, msg.Length, SocketFlags.None, 37 ar => { 38 //对方断开连接时, 这里抛出Socket Exception 39 //An existing connection was forcibly closed by the remote host 40 communicateSocket.EndReceive(ar); 41 ReceiveAction(Encoding.UTF8.GetString(msg).Trim('\0',' ')); 42 Receive(ReceiveAction); 43 }, null); 44 } 45 } 46 47 48 public class ServerSocket:SocketFunc 49 { 50 //服务端重载Access函数 51 public override void Access(string IP, System.Action AccessAciton) 52 { 53 Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 54 //本机预使用的IP和端口 55 IPEndPoint serverIP = new IPEndPoint(IPAddress.Any, 9050); 56 //绑定服务端设置的IP 57 serverSocket.Bind(serverIP); 58 //设置监听个数 59 serverSocket.Listen(1); 60 //异步接收连接请求 61 serverSocket.BeginAccept(ar => 62 { 63 base.communicateSocket = serverSocket.EndAccept(ar); 64 AccessAciton(); 65 }, null); 66 } 67 } 68 69 public class ClientSocket:SocketFunc 70 { 71 //客户端重载Access函数 72 public override void Access(string IP, System.Action AccessAciton) 73 { 74 base.communicateSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 75 base.communicateSocket.Bind(new IPEndPoint(IPAddress.Any, 9051)); 76 77 //服务器的IP和端口 78 IPEndPoint serverIP; 79 try 80 { 81 serverIP = new IPEndPoint(IPAddress.Parse(IP), 9050); 82 } 83 catch 84 { 85 throw new Exception(String.Format("{0}不是一个有效的IP地址!", IP)); 86 } 87 88 //客户端只用来向指定的服务器发送信息,不需要绑定本机的IP和端口,不需要监听 89 try 90 { 91 base.communicateSocket.BeginConnect(serverIP, ar => 92 { 93 AccessAciton(); 94 }, null); 95 } 96 catch 97 { 98 throw new Exception(string.Format("尝试连接{0}不成功!", IP)); 99 } 100 } 101 } 102 } SocketFunc  2.udp   1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Net; 10 using System.Net.Sockets; 11 using ITalkTradition.Code; 12 13 14 namespace ITalkTradition 15 { 16 public partial class FrmUser : Form 17 { 18 #region Field 19 20 /// 21 /// 在非主线程是对控件操作 22 /// 23 /// 24 private delegate void MyInvoke(string host); 25 26 /// 27 /// 充当服务器 进行监听接收 28 /// 29 private SocketUdpServer udpServer; 30 31 /// 32 /// 充当客户端 33 /// 34 private SocketUdpClient udpClient; 35 36 #endregion 37 38 #region Contructor 39 40 /// 41 /// 构造函数 42 /// 43 public FrmUser() 44 { 45 InitializeComponent(); 46 init(); 47 } 48 49 #endregion 50 51 #region Method 52 53 /// 54 /// 初始化数据 55 /// 56 private void init() 57 { 58 LanList.CurrentLanList = new List(); 59 this.Text = "当前局域网内在线用户"; 60 this.udpServer = SocketUdpServer.Instance; 61 this.udpServer.OnLineComplete += new SocketUdpServer.OnCompleteHander(this.OnLine_Event); 62 this.udpServer.DownLineComplete += new SocketUdpServer.OnCompleteHander(this.DownLine_Event); 63 this.udpServer.ErrorAppear += new SocketUdpServer.OnCompleteHander(this.Error_Event); 64 this.udpServer.Listen(); 65 this.udpClient = new SocketUdpClient(); 66 this.udpClient.Broadcast(DatagramType.OnLine); 67 68 } 69 70 /// 71 /// 上线增加用户 72 /// 73 /// 用户主机 74 private void AddUser(string host) 75 { 76 this.ilbUserList.Items.Add(host); 77 } 78 79 /// 80 /// 下线减少用户 81 /// 82 /// 用户主机在列表的序号 懒了以下 应该将回调的委托参数定义为int的,这里用了string 到程序需要转化为Int 83 private void RemoveUser(string hostIndex) 84 { 85 this.ilbUserList.Items.RemoveAt(Convert.ToInt32(hostIndex)); 86 } 87 88 #endregion 89 90 #region Event 91 92 /// 93 /// 上线事件 94 /// 95 /// 96 /// 97 private void OnLine_Event(SocketUdpServer socket, EventArgs e) 98 { 99 string host = socket.Message; 100 //如果该上线的用户在局域网列表中不存在 101 if (!LanList.CurrentLanList.Exists(x => x.Host == host)) 102 { 103 while (!this.IsHandleCreated) ; 104 this.ilbUserList.Invoke(new MyInvoke(this.AddUser), host); 105 //将上线用户添加进静态的局域网列表 106 LanList.CurrentLanList.Add(new LanInfo() 107 { 108 Host = host, 109 State = TalkState.Waiting, 110 RemoteEndPoint = socket.RemoteEndPoint 111 }); 112 } 113 } 114 115 /// 116 /// 下线事件 117 /// 118 /// 119 /// 120 private void DownLine_Event(SocketUdpServer socket, EventArgs e) 121 { 122 string host = socket.Message; 123 if (LanList.CurrentLanList.Exists(x => x.Host == host)) 124 { 125 ///判断是否是自己的主机下线 如果是自己的 则不需要操作 126 if (string.Compare(Dns.GetHostName(), host) != 0) 127 { 128 this.ilbUserList.Invoke(new MyInvoke(this.RemoveUser), LanList.CurrentLanList.FindIndex(x => x.Host == host).ToString()); 129 //将该用户从局域网列表中移除 130 131 LanList.CurrentLanList.RemoveAll(x => x.Host == host); 132 } 133 } 134 } 135 136 /// 137 /// 出现错误的事件 138 /// 139 /// 140 /// 141 private void Error_Event(SocketUdpServer socket, EventArgs e) 142 { 143 MessageBox.Show(socket.Message); 144 } 145 146 private void ilbUserList_DoubleClick(object sender, EventArgs e) 147 { 148 //XtraMessageBox.Show(ilbUserList.SelectedItem.ToString()); 149 string host = ilbUserList.SelectedItem.ToString(); 150 ///打开窗口 设置为正在交谈 151 LanList.SetTalkState(host, TalkState.Talking); 152 (new FrmTalk(host)).Show(); 153 } 154 155 /// 156 /// 窗体关闭事 进行下线广播 157 /// 158 /// 159 /// 160 private void FrmUser_FormClosed(object sender, FormClosedEventArgs e) 161 { 162 this.udpClient.Broadcast(DatagramType.DownLine); 163 this.udpServer.Stop(); 164 Application.Exit(); 165 } 166 167 /// 168 /// 刷新按钮 169 /// 170 /// 171 /// 172 private void btnRefresh_Click(object sender, EventArgs e) 173 { 174 //刷新 情况列表中的数据 重新上线广播 175 this.ilbUserList.Items.Clear(); 176 LanList.CurrentLanList.Clear(); 177 this.udpClient.Broadcast(DatagramType.OnLine); 178 } 179 180 181 182 #endregion 183 184 185 } 186 } Form1 双击ListBox内的用户名会出现下面的聊天窗口      1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Net; 10 using System.Net.Sockets; 11 using ITalkTradition.Code; 12 13 14 namespace ITalkTradition 15 { 16 public partial class FrmTalk : Form 17 { 18 #region Field 19 20 /// 21 /// 自己的socket 充当服务器 接收消息 22 /// 23 private SocketUdpServer selfSocket = null; 24 /// 25 /// 对话的socket 26 /// 27 private SocketUdpClient tallSocket = null; 28 /// 29 /// 谈话对方的局域网信息 30 /// 31 private LanInfo talkLan = null; 32 /// 33 /// 当前用户主机 34 /// 35 private string currentUserHost = ""; 36 37 /// 38 /// 对控件操作 在非主线程下需要调用此代理 39 /// 40 private delegate void MyInvoke(string user, string message); 41 42 #endregion 43 44 #region Constructor 45 46 /// 47 /// 通过远端主机名打开窗体 48 /// 49 /// 50 public FrmTalk(string host) 51 { 52 InitializeComponent(); 53 if (this.talkLan == null) 54 { 55 this.talkLan = LanList.CurrentLanList.Find(x => x.Host == host); 56 } 57 58 this.currentUserHost = Dns.GetHostName(); 59 this.Text = "正在和" + host + "聊天中"; 60 this.Initializion(); 61 } 62 63 /// 64 /// 通过远端 端点打开窗体 65 /// 66 /// 67 public FrmTalk(EndPoint remotePoint) 68 { 69 this.talkLan = LanList.CurrentLanList.Find(x => string.Compare(x.RemoteEndPoint.ToString(), remotePoint.ToString()) == 0); 70 (new FrmTalk(talkLan.Host)).Show(); 71 } 72 73 #endregion 74 75 #region Method 76 77 /// 78 /// 初始化方法 79 /// 80 private void Initializion() 81 { 82 this.selfSocket = SocketUdpServer.Instance; 83 ///绑定收到信息事件 84 this.selfSocket.OnChatComplete += new SocketUdpServer.OnCompleteHander(this.ReceiveEvent); 85 //给谈话的socket初始化endpoint 86 this.tallSocket = new SocketUdpClient(this.talkLan.RemoteEndPoint); 87 88 } 89 90 /// 91 /// 加载未读的信息 92 /// 93 private void LoadUnReadMessage() 94 { 95 Queue queque = QueueMessage.GetAndRemove(talkLan.Host); 96 MessageInfo messageInfo = null; 97 if (queque != null) 98 { 99 while (queque.Count > 0) 100 { 101 //出队列 102 messageInfo = queque.Dequeue(); 103 this.lbxMessage.Items.Add(talkLan.Host + ":" + messageInfo.ReceiveTime.ToString("yyyy-MM-dd HH:mm:ss")); 104 this.lbxMessage.Items.Add(messageInfo.Message); 105 } 106 } 107 } 108 109 /// 110 /// 添加一行 在listboxcontrol中 111 /// 112 /// 显示的用户 113 /// 消息 114 private void AddLine(string name, string message) 115 { 116 this.lbxMessage.Items.Add(name + ":" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); 117 this.lbxMessage.Items.Add(message); 118 } 119 120 /// 121 /// 发送信息 由键盘回车和发送按钮调用 122 /// 123 private void SendMessage() 124 { 125 try 126 { 127 string message = this.memInput.Text; 128 if (string.IsNullOrEmpty(message)) 129 { 130 MessageBox.Show("发送信息不能为空"); 131 } 132 else 133 { 134 this.tallSocket.Send(message); 135 this.AddLine("我", message); 136 this.memInput.Text = ""; 137 } 138 } 139 catch (Exception ex) 140 { 141 MessageBox.Show(ex.Message); 142 } 143 } 144 145 #endregion 146 147 #region Event 148 149 /// 150 /// 表单加载 151 /// 152 /// 153 /// 154 private void FrmTalk_Load(object sender, EventArgs e) 155 { 156 this.LoadUnReadMessage(); 157 } 158 159 /// 160 /// 接收信息回调事件 161 /// 162 /// 163 /// 164 private void ReceiveEvent(SocketUdpServer socket, EventArgs e) 165 { 166 //判断 远端的网络端点是否是当前的 打开的窗体 167 if (string.Compare(this.talkLan.RemoteEndPoint.ToString(), socket.RemoteEndPoint.ToString()) == 0) 168 { 169 this.lbxMessage.Invoke(new MyInvoke(this.AddLine), this.talkLan.Host, socket.Message); 170 } 171 } 172 173 /// 174 /// 信息发送按钮 175 /// 176 /// 177 /// 178 private void btnSend_Click(object sender, EventArgs e) 179 { 180 this.SendMessage(); 181 } 182 183 184 185 private void FrmTalk_FormClosed(object sender, FormClosedEventArgs e) 186 { 187 //将其设置为非交谈状态 188 LanList.SetTalkState(talkLan.Host, TalkState.Waiting); 189 } 190 191 private void memInput_KeyDown(object sender, KeyEventArgs e) 192 { 193 ///按下回车事件 194 if (e.KeyCode == Keys.Enter) 195 { 196 this.SendMessage(); 197 } 198 } 199 200 201 #endregion 202 } 203 } FrmTalk APP.comfig -------------------- 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using Chat1.bll; 10 11 namespace Chat1 12 { 13 public partial class Form1 : Form 14 { 15 P2PServer serverObj = new P2PServer(); 16 public Form1() 17 { 18 InitializeComponent(); 19 } 20 21 private void Form1_Load(object sender, EventArgs e) 22 { 23 try 24 { 25 serverObj.ConnectEvent += serverObj_ConnectEnent; 26 //订阅连接事件 27 serverObj.ReceiveEvent += serverObj_ReceiveEnent; 28 //订阅接收数据事件 29 serverObj.Listen(Convert.ToInt32(textBox1.Text)); //启动监听 30 } 31 catch (Exception ex) 32 { 33 MessageBox.Show("加载失败:" + ex.Message); 34 } 35 } 36 37 private void serverObj_ConnectEnent() 38 { 39 try 40 { 41 if (InvokeRequired) 42 { 43 P2PServer.ConnectDelegate update = serverObj_ConnectEnent; 44 45 Invoke(update); 46 } 47 else 48 { 49 listBox1.Items.Add("连接成功"); 50 } 51 } 52 catch (Exception ex) 53 { 54 MessageBox.Show("处理连接事件方法错误:" + ex.Message); 55 } 56 } 57 58 private void serverObj_ReceiveEnent(string message) 59 { 60 try 61 { 62 if (InvokeRequired) 63 { 64 P2PServer.ReceiveDelegate update = serverObj_ReceiveEnent; 65 66 Invoke(update, new object[] { message }); 67 } 68 else 69 { 70 listBox1.Items.Add(message);//添加到显示栏 71 72 serverObj.Send(message); 73 } 74 } 75 catch (Exception ex) 76 { 77 MessageBox.Show(@"处理事件方法错误:" + ex.Message); 78 } 79 } 80 81 //private void Form1_FormClosing(object sender, FormClosingEventArgs e) 82 //{ 83 // Close(); 84 //} 85 86 } 87 } 服务器Form1 1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Linq; 5 using System.Net; 6 using System.Net.Sockets; 7 using System.Text; 8 using System.Threading; 9 10 namespace Chat1.bll 11 { 12 class P2PServer 13 { 14 public TcpListener ListenObj; 15 public Dictionary clientMem = new Dictionary(); //客户列表一定要初始化,new 16 17 private Thread listenThread; 18 19 public delegate void ConnectDelegate(); //连接成功后处理事件的方法类型 20 public event ConnectDelegate ConnectEvent;//连接事件 21 22 public delegate void ReceiveDelegate(string message); //连接数据后处理事件方法类型 23 public event ReceiveDelegate ReceiveEvent; //接受数据事件 24 25 public void Listen(int port) //监听方法,启动监线程 26 { 27 IPAddress[] localIp = Dns.GetHostAddresses(Dns.GetHostName()); //通过主机名获得本地IP 28 ListenObj = new TcpListener(localIp[1], port);//监听对象 29 listenThread = new Thread(ListenClient); //监听客户 30 listenThread.Start(); 31 } 32 33 private void ListenClient() 34 { 35 while (true) //一直监听,可以有多个客户端请求连接 36 { 37 ListenObj.Start(); 38 TcpClient acceptClientObj = ListenObj.AcceptTcpClient(); //接收挂起的连接请求,这是一个组色的方法 39 this.ConnectEvent();//触发连接事件 40 41 Thread receiveThread = new Thread(Receiver); //这个线程处理接受的数据 42 string connectTime = DateTime.Now.ToString(); 43 receiveThread.Name = connectTime; 44 clientMem.Add(connectTime, acceptClientObj); //将客户添加到列表 45 46 receiveThread.Start();// 47 } 48 } 49 50 private void Receiver() 51 { //所有的 52 while (true) 53 { 54 NetworkStream ns = clientMem[Thread.CurrentThread.Name].GetStream(); 55 56 StreamReader sr = new StreamReader(ns); 57 58 string message = sr.ReadLine(); 59 60 ReceiveEvent(message);// 接收过数据 就触发接收信息的事件 61 } 62 } 63 64 public void Send(string message) 65 { 66 foreach (KeyValuePair keyValuePair in clientMem) //向所有客户发送数据 67 { 68 if (keyValuePair.Value == null || keyValuePair.Value.Connected == false) 69 { 70 clientMem.Remove(keyValuePair.Key); // 删除断开的连接 71 72 } 73 if (keyValuePair.Value != null) 74 { 75 NetworkStream ns = keyValuePair.Value.GetStream(); //得到网路流 76 StreamWriter sw = new StreamWriter(ns); 77 sw.WriteLine(message); 78 79 sw.Flush(); //刷新数据流 80 ns.Flush(); 81 } 82 } 83 } 84 } 85 } P2PServer 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using Chat.bll; 10 11 namespace Chat 12 { 13 public partial class Form1 : Form 14 { 15 private P2PClient ClientObj = new P2PClient();//客户对象 一定要new 16 public Form1() 17 { 18 InitializeComponent(); 19 } 20 21 private void Form1_Load(object sender, EventArgs e) 22 { 23 Sendbutton.Enabled = false; //没有连接不允许发送数据 24 this.AcceptButton = Sendbutton; 25 } 26 27 private void Clientbutton_Click(object sender, EventArgs e) 28 { 29 string nickName = tbName.Text; 30 string ip = tbIP.Text; 31 string port = tbPort.Text; 32 33 if (string.IsNullOrEmpty(nickName) || string.IsNullOrEmpty(ip) || string.IsNullOrEmpty(port)) 34 { 35 MessageBox.Show("请讲昵称、IP填写完整"); 36 return; 37 } 38 try 39 { 40 ClientObj.SendConnection(ip, Convert.ToInt32(port)); //连接 41 ClientObj.receiveEvent += new P2PClient.receiveDelegate(ClientObj_receiveEvent); 42 ClientObj.Send(tbName.Text + "登陆成功!"); 43 44 Sendbutton.Enabled = true; 45 Clientbutton.Enabled = false; 46 } 47 catch (Exception ex) 48 { 49 MessageBox.Show("连接时出错:" + ex.Message); 50 return; 51 52 } 53 } 54 55 private void ClientObj_receiveEvent(string receiveData) 56 { 57 try 58 { 59 if (this.InvokeRequired) //指示是否需要在这个线程上调用方法 60 { 61 P2PClient.receiveDelegate update = new P2PClient. 62 receiveDelegate(ClientObj_receiveEvent); //把信息传送给控件线程是重复调用该方法就会调用else 63 this.Invoke(update, new object[] { receiveData }); 64 } 65 else 66 { 67 listBox1.Items.Add(receiveData); //添加数据 68 } 69 } 70 catch (Exception ex) 71 { 72 MessageBox.Show("接收数据错误:" + ex.Message); 73 return; 74 } 75 } 76 77 private void Sendbutton_Click(object sender, EventArgs e) 78 { 79 try 80 { 81 if (string.IsNullOrEmpty(textBox1.Text)) 82 { 83 return; 84 } 85 ClientObj.Send(tbName.Text + "说: \n"); //发送信息 86 ClientObj.Send(" " + textBox1.Text); 87 textBox1.Clear(); 88 } 89 catch (Exception ex) 90 { 91 MessageBox.Show("发送数据错误" + ex.Message); 92 return; 93 } 94 } 95 96 //private void Form1_FormClosing(object sender, FormClosingEventArgs e) 97 //{ 98 // this.Close(); 99 //} 100 101 } 102 } 客户端Form1 1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Linq; 5 using System.Net; 6 using System.Net.Sockets; 7 using System.Text; 8 using System.Threading; 9 10 namespace Chat.bll 11 { 12 class P2PClient 13 {//下载于51aspx.com 14 public TcpClient tcpClientObj; //收发数据 15 private Thread receivetThread; //接收数据的线程 16 17 public delegate void receiveDelegate(string receiveData); //处理接收数据事件的方法类型 18 19 public event receiveDelegate receiveEvent; 20 21 public void SendConnection(string ip, int port) 22 { 23 IPAddress ipAddress = IPAddress.Parse(ip);//转为Ip地址后在连接会加快速度 24 tcpClientObj = new TcpClient(); //连接客户端 25 tcpClientObj.Connect(ipAddress, port); //连接 26 27 receivetThread = new Thread(Reveiver); //启动线程 28 receivetThread.Start(); 29 } 30 31 private void Reveiver() 32 {//下载于51aspx.com 33 while (true) 34 { 35 NetworkStream ns = this.tcpClientObj.GetStream(); 36 37 StreamReader sr = new StreamReader(ns); 38 string receivedata = sr.ReadLine(); 39 receiveEvent(receivedata); //触发事件 40 } 41 } 42 43 public void Send(string message) //发送信息 44 { 45 if (tcpClientObj == null) 46 { 47 return; 48 } 49 NetworkStream ns = this.tcpClientObj.GetStream(); //得到网络流 50 51 StreamWriter sw = new StreamWriter(ns); 52 sw.WriteLine(message); 53 sw.Flush(); //是所有的缓冲数据写入基础数据 54 ns.Flush(); 55 } 56 } 57 } P2PClient -------------------------------   广域网:   TCP例子: 1 using System; 2 using System.IO; 3 using System.Net; 4 using System.Net.Sockets; 5 using System.Text; 6 using System.Threading; 7 using System.Windows.Forms; 8 9 namespace MySocket 10 { 11 public partial class Form1 : Form 12 { 13 //服务器监听套接字 14 private ClientManager clientManage; 15 private bool isWatching = true; 16 private Socket sokServer; 17 //监听线程 18 private Thread thrServer; 19 20 public Form1() 21 { 22 InitializeComponent(); 23 //防止新线程调用主线程卡死 24 CheckForIllegalCrossThreadCalls = false; 25 } 26 27 //创建客户端管理类对象 28 29 private void btnSerStar_Click(object sender, EventArgs e) 30 { 31 //网络操作是很容易断开连接,所以一般要try catch 32 try 33 { 34 //创建一个Socket实例 35 //第一个参数表示使用ipv4 36 //第二个参数表示发送的是数据流 37 //第三个参数表示使用的协议是Tcp协议 38 sokServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 39 //获取ip地址局域网使用 ipv4,广域网使用外网IP 40 IPAddress ip = IPAddress.Parse(txtSerIP.Text.Trim()); 41 //创建一个网络通信节点,这个通信节点包含了ip地址,跟端口号。 42 //这里的端口我们设置为1029,这里设置大于1024,为什么自己查一下端口号范围使用说明。我默认为51111 43 var point = new IPEndPoint(ip, int.Parse(txtSerPort.Text.Trim())); 44 //Socket绑定网路通信节点 45 sokServer.Bind(point); 46 // //设置监听队列 同一时间最多连几个 (对应界面的系统负荷) 47 sokServer.Listen(Convert.ToInt32(txtSerMax.Text)); 48 //sokServer.Accept()这个接收消息的方法会使线程卡死,所以要开启新线程 49 thrServer = new Thread(WatchConnection); 50 //设置为后台线程防止卡死 51 thrServer.IsBackground = true; 52 thrServer.Start(); 53 //管理类对象实例化传入好友列表ListBox控件和消息委托 54 clientManage = new ClientManager(listChat, AppendMsg); 55 AppendMsg("开始监听~~~~!" + DateTime.Now); 56 } 57 catch (Exception ex) 58 { 59 MessageBox.Show("异常:" + ex.Message + DateTime.Now); 60 AppendMsg("异常:" + ex.Message + "1" + DateTime.Now); 61 } 62 } 63 64 private void WatchConnection() 65 { 66 try 67 { 68 while (isWatching) //注意该循环,服务端要持续监听,要不然一个客户端链接过后就无法链接第二个客户端了。 69 { 70 //开始监听客户端的消息 71 //负责通信的socket 72 Socket sokMsg = sokServer.Accept(); 73 //加入通信管理类 74 // MessageBox.Show(sokMsg.RemoteEndPoint.ToString()); 75 clientManage.AddClient(sokMsg); 76 } 77 } 78 catch (SocketException ex1) 79 { 80 AppendMsg("异常:" + ex1.Message + "2" + DateTime.Now); 81 } 82 catch (Exception ex) 83 { 84 AppendMsg("异常:" + ex.Source + "@" + ex.Message + "@" + ex.TargetSite + "@" + ex.InnerException + "@" + 85 ex.Data + "3" + DateTime.Now); 86 } 87 } 88 89 private void AppendMsg(string strMsg) 90 { 91 txtSetting.AppendText(strMsg + "\r\n"); 92 } 93 94 private void btnSerClose_Click(object sender, EventArgs e) 95 { 96 isWatching = false; 97 sokServer.Close(); 98 } 99 100 private void Form1_Load(object sender, EventArgs e) 101 { 102 CheckForIllegalCrossThreadCalls = false; 103 int x = Convert.ToInt32(txtSerMax.Text); 104 } 105 106 private void btnSetClear_Click(object sender, EventArgs e) 107 { 108 txtSetting.Text = ""; 109 } 110 111 private void btnSetSave_Click(object sender, EventArgs e) 112 { 113 using ( 114 var fsm = new FileStream(Path.GetDirectoryName(Application.ExecutablePath) + "\\" + "服务器日志.txt", 115 FileMode.OpenOrCreate, FileAccess.ReadWrite)) 116 { 117 byte[] newbuffer = Encoding.UTF8.GetBytes(txtSetting.Text); 118 fsm.Write(newbuffer, 0, newbuffer.Length); 119 } 120 } 121 //刷新在线人数 122 private void timer1_Tick(object sender, EventArgs e) 123 { 124 txtOnlineCount.Text = listChat.Items.Count.ToString(); 125 } 126 } 127 } Form1 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace MySocket 8 { 9 /// 10 /// 全局消息委托 11 /// 12 /// 消息 13 public delegate void DGSendMsg(string strMsg); 14 } DGSendMsg 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Net.Sockets; 5 using System.Text; 6 using System.Threading; 7 using System.Threading.Tasks; 8 9 namespace MySocket 10 { 11 /// 12 /// 客户端管理 13 /// 14 public class ClientManager 15 { 16 //好友列表控件 17 private System.Windows.Forms.ListBox listClient; 18 //在服务器上显示消息的委托(全局) 19 DGSendMsg dgSendMsg; 20 //通信套接字key :客户端ip value :对应的通信套接字 21 private Dictionary ClientSocket; 22 // 通信套接字key :客户端ip value :用户名(由于没有添加数据库我们暂时这么模拟,下篇我会讲到) 23 private Dictionary UserSocket; 24 public ClientManager() 25 { } 26 /// 27 /// 客户端管理类 28 /// 29 /// 列表控件 30 /// 显示消息 31 public ClientManager(System.Windows.Forms.ListBox lb, DGSendMsg dgSendMsg) 32 { 33 //用户列表 34 this.listClient = lb; 35 //消息委托 36 this.dgSendMsg = dgSendMsg; 37 //通信字典 38 ClientSocket = new Dictionary(); 39 //用户字典 40 UserSocket = new Dictionary(); 41 } 42 #region 添加客户端通信套接字+ public void AddClient(Socket sokMsg) 43 /// 44 /// 添加通信套接字 45 /// 46 /// 负责通信的socket 47 public void AddClient(Socket sokMsg) 48 { 49 //获取客户端通信套接字 50 string strEndPoint = sokMsg.RemoteEndPoint.ToString(); 51 //通信套接字加入字典 52 ClientSocket.Add(strEndPoint, sokMsg); 53 //sokServer.Accept()这个接收消息的方法会使线程卡死,所以要开启新线程 54 Thread thrMag = new Thread(ReciveMsg); 55 //设置为后台线程防止卡死 56 thrMag.IsBackground = true; 57 //开启线程 为新线程传入通信套接字参数 58 thrMag.Start(sokMsg); 59 dgSendMsg(strEndPoint + "成功连接上服务端~~~!" + DateTime.Now.ToString()); 60 } 61 #endregion 62 bool isReceing = true; 63 #region void ReciveMsg(object sokMsgObj) 服务接收客户端消息 64 /// 65 /// 服务接收客户端消息 66 /// 67 /// 客户端Scoket 68 void ReciveMsg(object sokMsgObj) 69 { 70 Socket sokMsg = null; 71 try 72 { 73 //sokMsg接收消息的socket 74 sokMsg = sokMsgObj as Socket; 75 //创建接收消息的缓冲区 默认5M 76 byte[] arrMsg = new byte[5 * 1024 * 1024]; 77 //循环接收 78 while (isReceing) 79 { 80 //接收到的真实消息存入缓冲区 并保存消息的真实长度(因为5M缓冲区不会全部用掉) 81 int realLength = sokMsg.Receive(arrMsg); 82 //将缓冲区的真实数据转成字符串 83 string strMsg = Encoding.UTF8.GetString(arrMsg, 0, realLength); 84 //dgSendMsg(strMsg); 85 86 string[] msgTxt = strMsg.Split('|'); 87 // msgTxt.Length == 2说明用户第一次连入,我们必须记录他的IP并与用户对应起来,如果局域网聊天 88 //IP在同一网段两个客户端还可以互相找到, 如果广域网下只有通过服务器转接才能找到 89 if (msgTxt.Length == 2) 90 { 91 //如果用户名已登录则强制下线 92 if (UserSocket.Values.Contains(msgTxt[0])) 93 { 94 sokMsg.Close(); 95 return; 96 } 97 UserSocket.Add(sokMsg.RemoteEndPoint.ToString(), msgTxt[0]); 98 //显示列表 99 listClient.Items.Add(sokMsg.RemoteEndPoint + "---" + msgTxt[0] + @"---上线~\(≧▽≦)/~啦啦啦"); 100 continue; 101 } 102 SendMsgToClient(strMsg); 103 } 104 } 105 catch 106 { 107 //连接出错说明客户端连接断开 108 RemoveClient(sokMsg.RemoteEndPoint.ToString()); 109 } 110 } 111 #endregion 112 /// 113 /// 移除下线用户信息 114 /// 115 /// IP:端口 116 public void RemoveClient(string strClientID) 117 { 118 //要移除的用户名 119 string name = UserSocket[strClientID]; 120 // 要移除的在线管理的项 121 string onlineStr = strClientID + "---" + UserSocket[strClientID] + @"---上线~\(≧▽≦)/~啦啦啦"; 122 123 //移除在线管理listClient 集合 124 if (listClient.Items.Contains(onlineStr)) 125 { 126 listClient.Items.Remove(onlineStr); 127 } 128 //断开socket连接 129 if (ClientSocket.Keys.Contains(strClientID)) 130 { 131 ClientSocket[strClientID].Close(); 132 ClientSocket.Remove(strClientID); 133 UserSocket.Remove(strClientID); 134 } 135 dgSendMsg(strClientID + "---" + name + @"---下线_" + DateTime.Now); 136 } 137 public void SendMsgToClient(string Msg) 138 { 139 //解析发来的数据 140 141 string[] msgTxt = Msg.Split('|'); 142 143 //不可解析数据 144 if (msgTxt.Length < 2) 145 { 146 return; 147 } 148 // 解析收消息的用户名 149 string strTo = msgTxt[1]; 150 //获得当前发送的用户 151 var nowtouser = UserSocket.Where(w => w.Value == strTo).FirstOrDefault(); 152 if (nowtouser.Key != null) 153 { 154 byte[] buffer = System.Text.Encoding.UTF8.GetBytes(Msg); 155 Socket conn = ClientSocket[nowtouser.Key]; 156 conn.Send(buffer); 157 } 158 159 } 160 } 161 } ClientManager   1 using System; 2 using System.Collections.Generic; 3 using System.Drawing; 4 using System.Net; 5 using System.Net.Sockets; 6 using System.Text; 7 using System.Threading; 8 using System.Windows.Forms; 9 10 namespace Client 11 { 12 public partial class Client : Form 13 { 14 //客户端通信套接字 15 private Socket clientSocket; 16 //新线程 17 private Thread thread; 18 //当前登录的用户 19 private string userName = ""; 20 public Client() 21 { 22 InitializeComponent(); 23 //防止新线程调用主线程卡死 24 CheckForIllegalCrossThreadCalls = false; 25 } 26 27 //通过IP地址与端口号与服务端建立链接 28 private void btnToServer_Click(object sender, EventArgs e) 29 { 30 //连接服务器前先选择用户 31 if (cmbUser.Text == null) 32 { 33 MessageBox.Show("请选择登录用户"); 34 return; 35 } 36 userName = cmbUser.Text.ToString(); 37 this.Text = "当前用户:" + userName; 38 //登录后禁止切换用户 39 cmbUser.Enabled = false; 40 //加载好友列表 41 foreach (object item in cmbUser.Items) 42 { 43 if (item != cmbUser.SelectedItem) 44 { 45 lbFriends.Items.Add(item); 46 } 47 } 48 //新建通信套接字 49 clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 50 //这里的ip地址,端口号都是服务端绑定的相关数据。 51 IPAddress ip = IPAddress.Parse(txtIP.Text); 52 var endpoint = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text)); 53 try 54 { 55 clientSocket.Connect(endpoint); //链接有端口号与IP地址确定服务端. 56 //登录时给服务器发送登录消息 57 string str = userName + "|" + " "; 58 byte[] buffer = Encoding.UTF8.GetBytes(str); 59 clientSocket.Send(buffer); 60 } 61 catch 62 { 63 MessageBox.Show("与服务器连接失败"); 64 lbFriends.Items.Clear(); 65 } 66 //客户端在接受服务端发送过来的数据是通过Socket 中的Receive方法, 67 //该方法会阻断线程,所以我们自己为该方法创建了一个线程 68 thread = new Thread(ReceMsg); 69 thread.IsBackground = true; //设置后台线程 70 thread.Start(); 71 } 72 73 public void ReceMsg() 74 { 75 while (true) 76 { 77 78 try 79 { 80 var buffer = new byte[1024 * 1024 * 2]; 81 int dateLength = clientSocket.Receive(buffer); //接收服务端发送过来的数据 82 //把接收到的字节数组转成字符串显示在文本框中。 83 string ReceiveMsg = Encoding.UTF8.GetString(buffer, 0, dateLength); 84 string[] msgTxt = ReceiveMsg.Split('|'); 85 string newstr = " " + msgTxt[0] + ":我" + "\r\n" + " " + msgTxt[2] + " ____[" + DateTime.Now + "]" + "\r\n" + "\r\n"; 86 ShowSmsg(newstr); 87 } 88 catch 89 { 90 91 } 92 } 93 } 94 95 private void btnSend_Click(object sender, EventArgs e) 96 { 97 if (lbFriends.SelectedItems.Count != 1) 98 { 99 MessageBox.Show("请选择好友"); 100 return; 101 } 102 string friend = lbFriends.SelectedItem.ToString(); 103 try 104 { 105 //界面显示消息 106 string newstr = "我" + ":" + friend + "\r\n" + txtMsg.Text.Trim() + " ____[" + DateTime.Now + 107 "]" + "\r\n" + "\r\n"; 108 ShowSmsg(newstr); 109 //发往服务器的消息 格式为 (发送者|接收者|信息) 110 string str = userName + "|" + friend + "|" + txtMsg.Text.Trim(); 111 //将消息转化为字节数据传输 112 byte[] buffer = Encoding.UTF8.GetBytes(str); 113 clientSocket.Send(buffer); 114 txtMsg.Text = ""; 115 } 116 catch 117 { 118 MessageBox.Show("与服务器连接失败"); 119 } 120 } 121 //展示消息 122 private void ShowSmsg(string newStr) 123 { 124 txtChat.AppendText(newStr); 125 } 126 private void btnCloseSer_Click(object sender, EventArgs e) 127 { 128 clientSocket.Close(); 129 } 130 } 131 } Client --------------------------------------------- UDP打洞例子: 记住:在执行文件目录下有个ip.ini文件,内容是你的ip地址(假如是内网电脑就写内网ip,公网就写公网ip) 公共文件vjsdn.net.library:   1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Net; 5 using System.Net.Sockets; 6 using System.Threading; 7 using vjsdn.net.library; 8 using System.Windows.Forms; 9 using System.IO; 10 11 namespace vjsdn.net.library 12 { 13 /// 14 /// 客户端业务类 15 /// 16 public class Client : IDisposable 17 { 18 //private const int MAX_RETRY_SEND_MSG = 1; //打洞时连接次数,正常情况下一次就能成功 19 20 private UdpClient _client;//客户端监听器 21 private IPEndPoint _hostPoint; //主机IP 22 private IPEndPoint _remotePoint; //接收任何远程机器的数据 23 private UserCollection _userList;//在线用户列表 24 private Thread _listenThread; //监听线程 25 private string _LocalUserName; //本地用户名 26 //private bool _HoleAccepted = false; //A->B,接收到B用户的确认消息 27 28 private WriteLogHandle _OnWriteMessage; 29 public WriteLogHandle OnWriteMessage 30 { 31 get { return _OnWriteMessage; } 32 set { _OnWriteMessage = value; } 33 } 34 35 private UserChangedHandle _UserChangedHandle = null; 36 public UserChangedHandle OnUserChanged 37 { 38 get { return _UserChangedHandle; } 39 set { _UserChangedHandle = value; } 40 } 41 42 /// 43 ///显示跟踪记录 44 /// 45 /// 46 private void DoWriteLog(string log) 47 { 48 if (_OnWriteMessage != null) 49 (_OnWriteMessage.Target as Control).Invoke(_OnWriteMessage, log); 50 } 51 52 /// 53 /// 构造器 54 /// 55 /// 56 public Client() 57 { 58 string serverIP = this.GetServerIP(); 59 _remotePoint = new IPEndPoint(IPAddress.Any, 0); //任何与本地连接的用户IP地址。 60 _hostPoint = new IPEndPoint(IPAddress.Parse(serverIP), Globals.SERVER_PORT); //服务器地址 61 _client = new UdpClient();//不指定端口,系统自动分配 62 _userList = new UserCollection(); 63 _listenThread = new Thread(new ThreadStart(Run)); 64 } 65 66 /// 67 /// 获取服务器IP,INI文件内设置 68 /// 69 /// 70 private string GetServerIP() 71 { 72 string file = Application.StartupPath + "\\ip.ini"; 73 string ip = File.ReadAllText(file); 74 return ip.Trim(); 75 } 76 77 /// 78 /// 启动客户端 79 /// 80 public void Start() 81 { 82 if (this._listenThread.ThreadState == ThreadState.Unstarted) 83 { 84 this._listenThread.Start(); 85 } 86 } 87 88 /// 89 /// 客户登录 90 /// 91 public void Login(string userName, string password) 92 { 93 _LocalUserName = userName; 94 95 // 发送登录消息到服务器 96 C2S_LoginMessage loginMsg = new C2S_LoginMessage(userName, password); 97 this.SendMessage(loginMsg, _hostPoint); 98 } 99 100 /// 101 /// 登出 102 /// 103 public void Logout() 104 { 105 C2S_LogoutMessage lgoutMsg = new C2S_LogoutMessage(_LocalUserName); 106 this.SendMessage(lgoutMsg, _hostPoint); 107 108 this.Dispose(); 109 System.Environment.Exit(0); 110 } 111 112 /// 113 /// 发送请求获取用户列表 114 /// 115 public void DownloadUserList() 116 { 117 C2S_GetUsersMessage getUserMsg = new C2S_GetUsersMessage(_LocalUserName); 118 this.SendMessage(getUserMsg, _hostPoint); 119 } 120 121 /// 122 /// 显示在线用户 123 /// 124 /// 125 private void DisplayUsers(UserCollection users) 126 { 127 if (_UserChangedHandle != null) 128 (_UserChangedHandle.Target as Control).Invoke(_UserChangedHandle, users); 129 } 130 131 //运行线程 132 private void Run() 133 { 134 try 135 { 136 byte[] buffer;//接受数据用 137 while (true) 138 { 139 buffer = _client.Receive(ref _remotePoint);//_remotePoint变量返回当前连接的用户IP地址 140 141 object msgObj = ObjectSerializer.Deserialize(buffer); 142 Type msgType = msgObj.GetType(); 143 DoWriteLog("接收到消息:" + msgType.ToString() + " From:" + _remotePoint.ToString()); 144 145 if (msgType == typeof(S2C_UserListMessage)) 146 { 147 // 更新用户列表 148 S2C_UserListMessage usersMsg = (S2C_UserListMessage)msgObj; 149 _userList.Clear(); 150 151 foreach (User user in usersMsg.UserList) 152 _userList.Add(user); 153 154 this.DisplayUsers(_userList); 155 } 156 else if (msgType == typeof(S2C_UserAction)) 157 { 158 //用户动作,新用户登录/用户登出 159 S2C_UserAction msgAction = (S2C_UserAction)msgObj; 160 if (msgAction.Action == UserAction.Login) 161 { 162 _userList.Add(msgAction.User); 163 this.DisplayUsers(_userList); 164 } 165 else if (msgAction.Action == UserAction.Logout) 166 { 167 User user = _userList.Find(msgAction.User.UserName); 168 if (user != null) _userList.Remove(user); 169 this.DisplayUsers(_userList); 170 } 171 } 172 else if (msgType == typeof(S2C_HolePunchingMessage)) 173 { 174 //接受到服务器的打洞命令 175 S2C_HolePunchingMessage msgHolePunching = (S2C_HolePunchingMessage)msgObj; 176 177 //NAT-B的用户给NAT-A的用户发送消息,此时UDP包肯定会被NAT-A丢弃, 178 //因为NAT-A上并没有A->NAT-B的合法Session, 但是现在NAT-B上就建立了有B->NAT-A的合法session了! 179 P2P_HolePunchingTestMessage msgTest = new P2P_HolePunchingTestMessage(_LocalUserName); 180 this.SendMessage(msgTest, msgHolePunching.RemotePoint); 181 } 182 else if (msgType == typeof(P2P_HolePunchingTestMessage)) 183 { 184 //UDP打洞测试消息 185 //_HoleAccepted = true; 186 P2P_HolePunchingTestMessage msgTest = (P2P_HolePunchingTestMessage)msgObj; 187 UpdateConnection(msgTest.UserName, _remotePoint); 188 189 //发送确认消息 190 P2P_HolePunchingResponse response = new P2P_HolePunchingResponse(_LocalUserName); 191 this.SendMessage(response, _remotePoint); 192 } 193 else if (msgType == typeof(P2P_HolePunchingResponse)) 194 { 195 //_HoleAccepted = true;//打洞成功 196 P2P_HolePunchingResponse msg = msgObj as P2P_HolePunchingResponse; 197 UpdateConnection(msg.UserName, _remotePoint); 198 199 } 200 else if (msgType == typeof(P2P_TalkMessage)) 201 { 202 //用户间对话消息 203 P2P_TalkMessage workMsg = (P2P_TalkMessage)msgObj; 204 DoWriteLog(workMsg.Message); 205 } 206 else 207 { 208 DoWriteLog("收到未知消息!"); 209 } 210 } 211 } 212 catch (Exception ex) { DoWriteLog(ex.Message); } 213 } 214 215 private void UpdateConnection(string user, IPEndPoint ep) 216 { 217 User remoteUser = _userList.Find(user); 218 if (remoteUser != null) 219 { 220 remoteUser.NetPoint = ep;//保存此次连接的IP及端口 221 remoteUser.IsConnected = true; 222 DoWriteLog(string.Format("您已经与{0}建立通信通道,IP:{1}!", 223 remoteUser.UserName, remoteUser.NetPoint.ToString())); 224 this.DisplayUsers(_userList); 225 } 226 } 227 228 #region IDisposable 成员 229 230 public void Dispose() 231 { 232 try 233 { 234 this._listenThread.Abort(); 235 this._client.Close(); 236 } 237 catch 238 { 239 240 } 241 } 242 243 #endregion 244 245 public void SendMessage(MessageBase msg, User user) 246 { 247 this.SendMessage(msg, user.NetPoint); 248 } 249 250 public void SendMessage(MessageBase msg, IPEndPoint remoteIP) 251 { 252 if (msg == null) return; 253 DoWriteLog("正在发送消息给->" + remoteIP.ToString() + ",内容:" + msg.ToString()); 254 byte[] buffer = ObjectSerializer.Serialize(msg); 255 _client.Send(buffer, buffer.Length, remoteIP); 256 DoWriteLog("消息已发送."); 257 } 258 259 /// 260 /// UDP打洞过程 261 /// 假设A想连接B.首先A发送打洞消息给Server,让Server告诉B有人想与你建立通话通道,Server将A的IP信息转发给B 262 /// B收到命令后向A发一个UDP包,此时B的NAT会建立一个与A通讯的Session. 然后A再次向B发送UDP包B就能收到了 263 /// 264 public void HolePunching(User user) 265 { 266 //A:自己; B:参数user 267 //A发送打洞消息给服务器,请求与B打洞 268 C2S_HolePunchingRequestMessage msg = new C2S_HolePunchingRequestMessage(_LocalUserName, user.UserName); 269 this.SendMessage(msg, _hostPoint); 270 271 Thread.Sleep(2000);//等待对方发送UDP包并建立Session 272 273 //再向对方发送确认消息,如果对方收到会发送一个P2P_HolePunchingResponse确认消息,此时打洞成功 274 P2P_HolePunchingTestMessage confirmMessage = new P2P_HolePunchingTestMessage(_LocalUserName); 275 this.SendMessage(confirmMessage, user); 276 } 277 } 278 279 } Client   1 using System; 2 using System.IO; 3 using System.Runtime.Serialization.Formatters.Binary; 4 using System.Net; 5 using System.Collections; 6 7 namespace vjsdn.net.library 8 { 9 /// 10 /// 显示跟踪消息的事件委托 11 /// 12 public delegate void WriteLogHandle(string msg); 13 14 /// 15 /// 刷新在线用户的事件委托 16 /// 17 public delegate void UserChangedHandle(UserCollection users); 18 19 public class Globals 20 { 21 /// 22 /// 服务器侦听端口 23 /// 24 public const int SERVER_PORT = 21134; 25 26 /// 27 /// 本地侦听端口 28 /// 29 public const int LOCAL_PORT = 19786; 30 } 31 32 /// 33 /// User 的摘要说明。 34 /// 35 [Serializable] 36 public class User 37 { 38 protected string _userName; 39 protected IPEndPoint _netPoint; 40 protected bool _conntected; 41 42 public User(string UserName, IPEndPoint NetPoint) 43 { 44 this._userName = UserName; 45 this._netPoint = NetPoint; 46 } 47 48 public string UserName 49 { 50 get { return _userName; } 51 } 52 53 public IPEndPoint NetPoint 54 { 55 get { return _netPoint; } 56 set { _netPoint = value; } 57 } 58 59 public bool IsConnected //打洞标记 60 { 61 get { return _conntected; } 62 set { _conntected = value; } 63 } 64 65 public string FullName { get { return this.ToString(); } } 66 67 public override string ToString() 68 { 69 return _userName + "- [" + _netPoint.Address.ToString() + ":" + _netPoint.Port.ToString() + "] " + (_conntected ? "Y" : "N"); 70 } 71 } 72 73 /// 74 /// 在线用户列表 75 /// 76 [Serializable] 77 public class UserCollection : CollectionBase 78 { 79 public void Add(User user) 80 { 81 InnerList.Add(user); 82 } 83 84 public void Remove(User user) 85 { 86 InnerList.Remove(user); 87 } 88 89 public User this[int index] 90 { 91 get { return (User)InnerList[index]; } 92 } 93 94 public User Find(string userName) 95 { 96 foreach (User user in this) 97 { 98 if (string.Compare(userName, user.UserName, true) == 0) 99 { 100 return user; 101 } 102 } 103 return null; 104 } 105 } 106 107 /// 108 /// 序列化反序列化对象 109 /// 110 public class ObjectSerializer 111 { 112 public static byte[] Serialize(object obj) 113 { 114 BinaryFormatter binaryF = new BinaryFormatter(); 115 MemoryStream ms = new MemoryStream(1024 * 10); 116 binaryF.Serialize(ms, obj); 117 ms.Seek(0, SeekOrigin.Begin); 118 byte[] buffer = new byte[(int)ms.Length]; 119 ms.Read(buffer, 0, buffer.Length); 120 ms.Close(); 121 return buffer; 122 } 123 124 public static object Deserialize(byte[] buffer) 125 { 126 BinaryFormatter binaryF = new BinaryFormatter(); 127 MemoryStream ms = new MemoryStream(buffer, 0, buffer.Length, false); 128 object obj = binaryF.Deserialize(ms); 129 ms.Close(); 130 return obj; 131 } 132 } 133 134 135 } Common 1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 5 namespace vjsdn.net.library 6 { 7 8 /// 9 /// 消息基类,抽象类 10 /// 11 [Serializable] 12 public abstract class MessageBase 13 { 14 //消息基类 15 } 16 17 18 #region 客户端发送到服务器的消息 19 20 /// 21 /// 客户端发送到服务器的消息基类 22 /// 23 [Serializable] 24 public abstract class C2S_MessageBase : MessageBase 25 { 26 private string _fromUserName; 27 28 protected C2S_MessageBase(string fromUserName) 29 { 30 _fromUserName = fromUserName; 31 } 32 33 public string FromUserName 34 { 35 get { return _fromUserName; } 36 } 37 } 38 39 /// 40 /// 用户登录消息 41 /// 42 [Serializable] 43 public class C2S_LoginMessage : C2S_MessageBase 44 { 45 private string _password; 46 47 public C2S_LoginMessage(string userName, string password) 48 : base(userName) 49 { 50 this._password = password; 51 } 52 53 public string Password 54 { 55 get { return _password; } 56 } 57 } 58 59 /// 60 /// 用户登出消息 61 /// 62 [Serializable] 63 public class C2S_LogoutMessage : C2S_MessageBase 64 { 65 66 public C2S_LogoutMessage(string userName) 67 : base(userName) 68 { } 69 } 70 71 /// 72 /// 请求用户列表消息 73 /// 74 [Serializable] 75 public class C2S_GetUsersMessage : C2S_MessageBase 76 { 77 public C2S_GetUsersMessage(string userName) 78 : base(userName) 79 { } 80 } 81 82 /// 83 /// 请求Purch Hole消息 84 /// 85 [Serializable] 86 public class C2S_HolePunchingRequestMessage : C2S_MessageBase 87 { 88 protected string toUserName; 89 90 public C2S_HolePunchingRequestMessage(string fromUserName, string toUserName) 91 : base(fromUserName) 92 { 93 this.toUserName = toUserName; 94 } 95 96 public string ToUserName 97 { 98 get { return this.toUserName; } 99 } 100 } 101 102 #endregion 103 104 #region 点对点消息 105 106 /// 107 /// 点对点消息基类 108 /// 109 [Serializable] 110 public abstract class P2P_MessageBase : MessageBase 111 { 112 // 113 } 114 115 /// 116 /// 聊天消息 117 /// 118 [Serializable] 119 public class P2P_TalkMessage : P2P_MessageBase 120 { 121 private string message; 122 123 public P2P_TalkMessage(string msg) 124 { 125 message = msg; 126 } 127 128 public string Message 129 { 130 get { return message; } 131 } 132 } 133 134 /// 135 /// UDP打洞测试消息 136 /// 137 [Serializable] 138 public class P2P_HolePunchingTestMessage : P2P_MessageBase 139 { 140 private string _UserName; 141 142 public P2P_HolePunchingTestMessage(string userName) 143 { 144 _UserName = userName; 145 } 146 147 public string UserName 148 { 149 get { return _UserName; } 150 } 151 } 152 153 /// 154 /// 收到消息的回复确认 155 /// 如A与B想建立通话通道,些命令由B发出确认打洞成功 156 /// 157 [Serializable] 158 public class P2P_HolePunchingResponse : P2P_MessageBase 159 { 160 private string _UserName; 161 162 public P2P_HolePunchingResponse(string userName) 163 { 164 _UserName = userName; 165 } 166 167 public string UserName 168 { 169 get { return _UserName; } 170 } 171 } 172 173 #endregion 174 175 #region 服务器发送到客户端消息 176 177 /// 178 /// 服务器发送到客户端消息基类 179 /// 180 [Serializable] 181 public abstract class S2C_MessageBase : MessageBase 182 { 183 } 184 185 /// 186 /// 请求用户列表应答消息 187 /// 188 [Serializable] 189 public class S2C_UserListMessage : S2C_MessageBase 190 { 191 private UserCollection userList; 192 193 public S2C_UserListMessage(UserCollection users) 194 { 195 this.userList = users; 196 } 197 198 public UserCollection UserList 199 { 200 get { return userList; } 201 } 202 } 203 204 /// 205 /// 转发请求Purch Hole消息 206 /// 207 [Serializable] 208 public class S2C_HolePunchingMessage : S2C_MessageBase 209 { 210 protected System.Net.IPEndPoint _remotePoint; 211 212 public S2C_HolePunchingMessage(System.Net.IPEndPoint point) 213 { 214 this._remotePoint = point; 215 } 216 217 public System.Net.IPEndPoint RemotePoint 218 { 219 get { return _remotePoint; } 220 } 221 } 222 223 /// 224 /// 服务器通知所有在线用户, 225 /// 226 [Serializable] 227 public class S2C_UserAction : S2C_MessageBase 228 { 229 protected User _User; 230 protected UserAction _Action; 231 232 public S2C_UserAction(User user, UserAction action) 233 { 234 _User = user; 235 _Action = action; 236 } 237 238 public User User 239 { 240 get { return _User; } 241 } 242 243 public UserAction Action 244 { 245 get { return _Action; } 246 } 247 } 248 249 #endregion 250 251 /// 252 /// 用户动作 253 /// 254 public enum UserAction 255 { 256 Login, 257 Logout 258 } 259 } Messages 1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Net; 5 using System.Net.Sockets; 6 using System.Threading; 7 using vjsdn.net.library; 8 using System.Windows.Forms; 9 10 namespace vjsdn.net.library 11 { 12 /// 13 /// 服务器端业务类 14 /// 15 public class Server 16 { 17 private UdpClient _server; //服务器端消息监听器 18 private UserCollection _userList; //在线用户列表 19 private Thread _serverThread; 20 private IPEndPoint _remotePoint; //远程用户请求的IP地址及端口 21 22 private WriteLogHandle _WriteLogHandle = null; 23 private UserChangedHandle _UserChangedHandle = null; 24 25 /// 26 /// 显示跟踪消息 27 /// 28 public WriteLogHandle OnWriteLog 29 { 30 get { return _WriteLogHandle; } 31 set { _WriteLogHandle = value; } 32 } 33 34 /// 35 /// 当用户登入/登出时触发此事件 36 /// 37 public UserChangedHandle OnUserChanged 38 { 39 get { return _UserChangedHandle; } 40 set { _UserChangedHandle = value; } 41 } 42 43 /// 44 /// 构造器 45 /// 46 public Server() 47 { 48 _userList = new UserCollection(); 49 _remotePoint = new IPEndPoint(IPAddress.Any, 0); 50 _serverThread = new Thread(new ThreadStart(Run)); 51 } 52 53 /// 54 ///显示跟踪记录 55 /// 56 /// 57 private void DoWriteLog(string log) 58 { 59 if (_WriteLogHandle != null) 60 (_WriteLogHandle.Target as System.Windows.Forms.Control).Invoke(_WriteLogHandle, log); 61 } 62 63 /// 64 /// 刷新用户列表 65 /// 66 /// 用户列表 67 private void DoUserChanged(UserCollection list) 68 { 69 if (_UserChangedHandle != null) 70 (_UserChangedHandle.Target as Control).Invoke(_UserChangedHandle, list); 71 } 72 73 /// 74 /// 开始启动线程 75 /// 76 public void Start() 77 { 78 try 79 { 80 _server = new UdpClient(Globals.SERVER_PORT); 81 _serverThread.Start(); 82 DoWriteLog("服务器已经启动,监听端口:" + Globals.SERVER_PORT.ToString() + ",等待客户连接..."); 83 } 84 catch (Exception ex) 85 { 86 DoWriteLog("启动服务器发生错误: " + ex.Message); 87 throw ex; 88 } 89 } 90 91 /// 92 /// 停止线程 93 /// 94 public void Stop() 95 { 96 DoWriteLog("停止服务器..."); 97 try 98 { 99 _serverThread.Abort(); 100 _server.Close(); 101 DoWriteLog("服务器已停止."); 102 } 103 catch (Exception ex) 104 { 105 DoWriteLog("停止服务器发生错误: " + ex.Message); 106 throw ex; 107 } 108 } 109 110 //线程主方法 111 private void Run() 112 { 113 byte[] msgBuffer = null; 114 115 while (true) 116 { 117 msgBuffer = _server.Receive(ref _remotePoint); //接受消息 118 try 119 { 120 //将消息转换为对象 121 object msgObject = ObjectSerializer.Deserialize(msgBuffer); 122 if (msgObject == null) continue; 123 124 Type msgType = msgObject.GetType(); 125 DoWriteLog("接收到消息:" + msgType.ToString()); 126 DoWriteLog("From:" + _remotePoint.ToString()); 127 128 //新用户登录 129 if (msgType == typeof(C2S_LoginMessage)) 130 { 131 C2S_LoginMessage lginMsg = (C2S_LoginMessage)msgObject; 132 DoWriteLog(string.Format("用户'{0}'已登录!", lginMsg.FromUserName)); 133 134 // 添加用户到列表 135 IPEndPoint userEndPoint = new IPEndPoint(_remotePoint.Address, _remotePoint.Port); 136 User user = new User(lginMsg.FromUserName, userEndPoint); 137 _userList.Add(user); 138 139 this.DoUserChanged(_userList); 140 141 //通知所有人,有新用户登录 142 S2C_UserAction msgNewUser = new S2C_UserAction(user, UserAction.Login); 143 foreach (User u in _userList) 144 { 145 if (u.UserName == user.UserName) //如果是自己,发送所有在线用户列表 146 this.SendMessage(new S2C_UserListMessage(_userList), u.NetPoint); 147 else 148 this.SendMessage(msgNewUser, u.NetPoint); 149 } 150 } 151 else if (msgType == typeof(C2S_LogoutMessage)) 152 { 153 C2S_LogoutMessage lgoutMsg = (C2S_LogoutMessage)msgObject; 154 DoWriteLog(string.Format("用户'{0}'已登出!", lgoutMsg.FromUserName)); 155 156 // 从列表中删除用户 157 User logoutUser = _userList.Find(lgoutMsg.FromUserName); 158 if (logoutUser != null) _userList.Remove(logoutUser); 159 160 this.DoUserChanged(_userList); 161 162 //通知所有人,有用户登出 163 S2C_UserAction msgNewUser = new S2C_UserAction(logoutUser, UserAction.Logout); 164 foreach (User u in _userList) 165 this.SendMessage(msgNewUser, u.NetPoint); 166 } 167 168 else if (msgType == typeof(C2S_HolePunchingRequestMessage)) 169 { 170 //接收到A给B打洞的消息,打洞请求,由客户端发送给服务器端 171 C2S_HolePunchingRequestMessage msgHoleReq = (C2S_HolePunchingRequestMessage)msgObject; 172 173 User userA = _userList.Find(msgHoleReq.FromUserName); 174 User userB = _userList.Find(msgHoleReq.ToUserName); 175 176 // 发送打洞(Punching Hole)消息 177 DoWriteLog(string.Format("用户:[{0} IP:{1}]想与[{2} IP:{3}]建立对话通道.", 178 userA.UserName, userA.NetPoint.ToString(), 179 userB.UserName, userB.NetPoint.ToString())); 180 181 //由Server发送消息给B,将A的IP的IP地址信息告诉B,然后由B发送一个测试消息给A. 182 S2C_HolePunchingMessage msgHolePunching = new S2C_HolePunchingMessage(_remotePoint); 183 this.SendMessage(msgHolePunching, userB.NetPoint); //Server->B 184 } 185 else if (msgType == typeof(C2S_GetUsersMessage)) 186 { 187 // 发送当前用户信息 188 S2C_UserListMessage srvResMsg = new S2C_UserListMessage(_userList); 189 this.SendMessage(srvResMsg, _remotePoint); 190 } 191 } 192 catch (Exception ex) { DoWriteLog(ex.Message); } 193 } 194 } 195 /// 196 /// 发送消息 197 /// 198 public void SendMessage(MessageBase msg, IPEndPoint remoteIP) 199 { 200 DoWriteLog("正在发送消息:" + msg.ToString()); 201 if (msg == null) return; 202 byte[] buffer = ObjectSerializer.Serialize(msg); 203 _server.Send(buffer, buffer.Length, remoteIP); 204 DoWriteLog("消息已发送."); 205 } 206 } 207 } Server server:(vjsdn.net.server)   1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Text; 7 using System.Windows.Forms; 8 using vjsdn.net.library; 9 10 namespace P2PServer 11 { 12 public partial class frmServer : Form 13 { 14 private Server _server; 15 16 public frmServer() 17 { 18 InitializeComponent(); 19 } 20 21 private void button1_Click(object sender, EventArgs e) 22 { 23 _server = new Server(); 24 _server.OnWriteLog += new WriteLogHandle(server_OnWriteLog); 25 _server.OnUserChanged += new UserChangedHandle(OnUserChanged); 26 try 27 { 28 _server.Start(); 29 } 30 catch (Exception ex) 31 { 32 MessageBox.Show(ex.Message); 33 } 34 } 35 36 //刷新用户列表 37 private void OnUserChanged(UserCollection users) 38 { 39 listBox2.DisplayMember = "FullName"; 40 listBox2.DataSource = null; 41 listBox2.DataSource = users; 42 } 43 44 //显示跟踪消息 45 public void server_OnWriteLog(string msg) 46 { 47 listBox1.Items.Add(msg); 48 listBox1.SelectedIndex = listBox1.Items.Count - 1; 49 } 50 51 private void button2_Click(object sender, EventArgs e) 52 { 53 Application.Exit(); 54 } 55 56 private void frmServer_FormClosing(object sender, FormClosingEventArgs e) 57 { 58 if (_server != null) 59 _server.Stop(); 60 } 61 62 private void button3_Click(object sender, EventArgs e) 63 { 64 //发送消息给所有在线用户 65 P2P_TalkMessage msg = new P2P_TalkMessage(textBox1.Text); 66 foreach (object o in listBox2.Items) 67 { 68 User user = o as User; 69 _server.SendMessage(msg, user.NetPoint); 70 } 71 } 72 73 private void button6_Click(object sender, EventArgs e) 74 { 75 listBox1.Items.Clear(); 76 } 77 } 78 } frmServer client:(vjsdn.net.client)  1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Text; 7 using System.Windows.Forms; 8 using System.Net; 9 using System.Net.Sockets; 10 using vjsdn.net.library; 11 12 namespace vjsdn.net 13 { 14 public partial class frmClient : Form 15 { 16 private Client _client; 17 18 public frmClient() 19 { 20 InitializeComponent(); 21 } 22 23 private void frmClient_Load(object sender, EventArgs e) 24 { 25 _client = new Client(); 26 _client.OnWriteMessage = this.WriteLog; 27 _client.OnUserChanged = this.OnUserChanged; 28 } 29 30 private void button1_Click(object sender, EventArgs e) 31 { 32 _client.Login(textBox2.Text, ""); 33 _client.Start(); 34 } 35 36 private void WriteLog(string msg) 37 { 38 listBox2.Items.Add(msg); 39 listBox2.SelectedIndex = listBox2.Items.Count - 1; 40 } 41 42 private void button4_Click(object sender, EventArgs e) 43 { 44 this.Close(); 45 } 46 47 private void button3_Click(object sender, EventArgs e) 48 { 49 if (_client != null) 50 { 51 User user = listBox1.SelectedItem as User; 52 _client.HolePunching(user); 53 } 54 } 55 56 private void button2_Click(object sender, EventArgs e) 57 { 58 if (_client != null) _client.DownloadUserList(); 59 } 60 61 private void frmClient_FormClosing(object sender, FormClosingEventArgs e) 62 { 63 if (_client != null) _client.Logout(); 64 } 65 66 private void OnUserChanged(UserCollection users) 67 { 68 listBox1.DisplayMember = "FullName"; 69 listBox1.DataSource = null; 70 listBox1.DataSource = users; 71 } 72 73 private void button5_Click(object sender, EventArgs e) 74 { 75 P2P_TalkMessage msg = new P2P_TalkMessage(textBox1.Text); 76 User user = listBox1.SelectedItem as User; 77 _client.SendMessage(msg, user); 78 } 79 80 private void button6_Click(object sender, EventArgs e) 81 { 82 listBox2.Items.Clear(); 83 } 84 } 85 } frmClient  --------------------------- 项目:局域网五子棋、远程操控、       数据库 SQL server:  SQL server 一共有4个系统数据库:master:记录SQL server系统的所有系统级信息,例如:登陆账户信息、链接服务器和系统配置设置、记录其他所有数据库的存在、数据文件的位置、SQL server的初始化信息等。如果master数据库不可用、则无法启动SQL servermsdb:用于SQL server代理计划警报和作业。数据库定时执行某些操作、数据库邮件等。model:用作SQL server实例上创建的所有数据库的模板。对model数据库进行的修改(如数据库大小、排序规则、恢复模式和其他数据库选项)将应用于以后创建的所有数据库。在model数据库中创建一张表,则以后每次创建数据库的时候都会有默认的一张同样的表。tempdb:一个工作空间,用于保存临时对象或中间结果集。一个全局资源,可供连接到SQL server实例的所有用户使用。每次启动SQL server是都会重新创建tempdb --创建一个数据库create database MyDatabase;--默认的--删除数据库drop database MyDatabase;--------------------------------------------------------------------create database BBloveDDon primary ( --配置主数据文件的选项 name='BBloveDD',--主数据文件的逻辑名称 建议和数据库名字一样 filename='d:\BBloveDD.mdf',--主数据文件的诗句保存路径 size=5mb, maxsize=150mb, filegrowth=20%)log on --日志文件( --配置日志文件的选项 name='BBloveDD_log',--主数据文件的逻辑名称 filename='d:\BBloveDD_log.ldf',--日志文件的实际保存路径 size=5mb, filegrowth=5mb  )    这时要是想要在D盘删除这两个文件但是删除不了,是因为文件被占用了,下面会讲解分离之后删除。 ------------------------------------------------------------------------------------------- --创建数据库表 use BBloveDD  --将代码环境切换到BBloveDD下   create table bblovedd --表名随便写 ( ID int identity(1,1) primary key,--设置主键 name nvarchar(50) not null --数据类型,输入汉字的要用n 的 )   --删除表 use BBloveDD drop table bblovedd --创建一个员工表 --员工表:员工id,身份证号,姓名,性别,入职日期,年龄,地址,电话,所属部门,Email use BBloveDD create table bblovedd ( bb_id int identity(1,1) primary key, bb_idcard varchar(18) not null, bb_name nvarchar(50) null, bb_cender bit not null, bb_joindata datetime, bb_age int, bb_address nvarchar(300), bb_phone varchar(100), dd_id int not null, bb_Email varchar(100) ) Insert into 数据插入 Insert into 表明(列名,列名...) values(值1,值2...) Insert 语句可以省略表名后的列名,就是全部列都要添加数据 自动编号列不需要手动插入。 主键不能有重复值 插入另一个表的数据(类型要匹配才行): Insert into 表名(列) select 列 from 表 N前缀:字符串的前面加N N‘字符串’ uuse BBloveDD create table bbdd ( ID int identity(1,1) primary key, name nvarchar(50) not null ) insert into bbdd(name) values(N'毕小帅') insert into bbdd(name) values(N'小丹丹') Update 数据更新 --update 表名set 列=新值,列=新值,...where 条件 --update语句如果不加where就是表示表中的所有的数据都进行更改 update bbdd set name = '丹丹' where name = '小丹丹' --where 中可以使用的其他逻辑运算符:(||)or、(&&)and、(!)not、<、 -->、>=、<=、<>(或!=)等 --删除数据语句 --delete from 表名where... --delete语句如果不加where条件,表示将表中所有数据都删除,加where条件后, --会按照where条件进行删除 insert into bbdd(name) values(N'小丹丹')--先添加一行数据 delete from bbdd where name='小丹丹' --delete from bbdd --删除所有数据不会恢复自动编号 --另一种删除全部数据的 truncate table bbdd --如果确实要删除表中全部数据,那么建议使用truncate --truncate特点: --1.truncate语句不能跟where条件(无法根据条件来删除,只能全部删除数据) --2.同时自动编号恢复到初始值。 --3.使用truncate删除表中所有数据要比delete效率高的多。 --数据库约束是为了保证数据的完整性(正确性)而实现的一套机制 --非空约束 --主键约束(PK)primary key 唯一切不能为空 --唯一约束(UQ)unique 唯一,允许为空,但只能出现一次 --默认约束(DF)default 默认值 --检查约束(CK)check 范围以及格式限制 --外键约束(FK)foreign Key 表关系 --增加外键约束时,设置级联更新、级联删除 alter table 表名 drop column 列名 -- 删除一列 alter table 表名 add 列名 nvarchar(100) alter table 表名 alter column 列名 varchar(200)--修改列属性 alter table 表名 add constraint 随意起个约束名-PK_Employees_列名 primary key(列名)--添加主键约束 alter table 表名 alter column 列名 varchar(50) not null--添加非空约束 alter table 表名 add constraint 随意起个约束名-UQ_Employees_列名 unique(列名)--添加唯一约束 alter table 表名 add constraint 随意起个约束名-DF_Employees_列名 default('不男不女') for 列名--添加默认约束 alter table 表名 add constraint 随意起个约束名-CK_Employees_列名 check(列名='男' 列名='女')--添加检查约束 alter table 表名 add constraint 随意起个约束名-PK_Employees_列名 primary key(列名)--设置主键列 alter table 表名 add constraint 随意起个约束名-PK_Employees_列名 foreign key(当前表的列明) references 另一个表表名(另个表的主键列)--增加外键约束 alter table 表名 drop constraint 约束名 --删除约束 --创建表时直接加约束: create table 员工表 ( id int identity(1,1) primary key, name varchar(50) not null unique check(len(name)>2), gender char(2) default('男'), age int check(age>0 and age<100), Email varchar(100) unique, Address varchar(500) not null, bldid int foreign key references bld(id) on delete cascade --外检约束级联 ) create table bld ( id int identity(1,1) primary key, name varchar(50) not null unique ) --select查询 --简单的数据检索:select * from 表名 --指检索需要的列:select 列名可以查看多个列from 表名 --列别名:select name as 姓名,select age as 年龄from 表名 --还可以这样其别名:select 姓名=name ,select 年龄=age from 表名 --使用where检索符合条件的数据:select name from 表名where sex='男' --检索不与任何表关联的数据:select 1+1;select getdate();--直接显示出和当前时间-- 这样就是select也可以不与from一起使用 --distinct关键字,针对已经查询出的结果然后去除重复 --select distinct * from 表名 --order by 列名 select *from 表名 order by 列名 desc --降序排序 select *from 表名 order by 列名 asc --升序排序 select *from 表名 order by 表名 --默认就是升序排序 select top 3 * from 表名 order by 列名 desc --查询前 select top 3 * from 表名 order by 列名 asc --查询后 select top 30 percent * from 表名 order by 列名 desc --查询前% select top 30 percent * from 表名 order by 列名 asc --查询后% --聚合函数聚合函数是聚合行中的数据 --常用的几个聚合函数: --max(最大值)、min(最小值)、avg(平均值)、sum(和)、count(数量:记录的条数) --聚合函数对null不计算 --如果一行的数据都是null,count(*)包含对空值行、重复行的统计 模糊查询 --模糊查询: --通配符:_ % [] ^ -- _ 表示任意的单个字符 select * from 表名 where name like '毕_' --姓毕的两个字名字 select * from 表名 where name like '毕__' --姓毕的三个字名字 -- []筛选范围匹配一个 select *from 表名 where 列名 like '毕[0-9]丹'--毕丹、毕丹..... select *from 表名 where 列名 like '毕[毕a-z]'--毕a丹、毕b丹..... select *from 表名 where 列名 like '毕[a-z0-9]丹'--毕a1丹、毕a2丹、毕b2丹..... -- % select *from 表名 where 列名 like '毕%' --头个一个字是毕的全部查询出来 -- ^非除了sqlserver 其他数据库就是not like select *from 表名 where 列名 like '毕[^0-9]丹'--只要毕丹之间不是-9的数字都会查询出来 --转义符 select * from 表名 where 列名 like '%/[/]%' escape '/'--数据中带有[]的都会查询出来 --replace 替换关键字 update bbdd set name=REPLACE(name ,'毕毕','bb')--将表中name列内数据为毕毕全部替换成bb --null --null值无法使用=或<>来进行比较 select * from 表名 where 列名=null --这是错误的 select * from 表名 where 列名<>null --列名不等于null 这也是错误的 --正确写法: select *from 表名 where 列名 is null select *from 表名 where 列名 is not null --多列排序 select *from 表名 order by 列名 desc,列名 desc 分组: --在使用select查询的时候,有时需要对数据进行分组汇总(即:将现有的数据 --按照某列来汇总统计),这是就需要用到group by语句。 --select语句中可以使用group by子句将行划分成较小的组,然后使用聚组函数返回每一个组的汇总信息。 --分组一般都和聚合函数连用 --group by子句必须放在where语句的之后,group by与order by都是对筛选后的数据进行处理,而where是用来筛选数据的。 --没有出现在group by子句中的列是不能放到select语句后的列名列表中的(聚合函数中除外) --例子: --错误:select id, name from bbdd group by name --正确:select id,count(name)from bbdd group by id --对分组以后的数据进行筛选使用having --having与where都是对数据进行筛选,where是对分组前的每一行数据进行筛选 --而having是对分组后的每一组数据进行筛选 --select --id as ID, --人数=count(*) --from bbdd --group by id --having count(*)>3 --having语句(对组的筛选,哪些组显示哪些组不显示) --1.对表中的数据分组后,会得到一个分组的结果集,如何对该结果集在进行筛选?-->having --2.注意having中不能使用未参与分组的列,having不能替代where。作用不一样,having是对组进行过滤。 --where是对每天记录进行过滤的。 --3.having是group by的条件对分组后的数据进行筛选(与where类似,都是筛选,只不过having是用来筛选分组后的组的。) --4.在where中不能使用聚合函数,必须使用having,having要位于group by之后。 --5.having的使用几乎是与where一样的,也可以用in。 上下是一起的 select 商品名称 sum(销售数量) as 销售数量 from 超市 group by 商品名称 order by 销售数量 desc 分组要和聚合函数一起 上下 select语句的处理顺序: 1.from 2.on 3.join 4.where 5.group by 6.with cube 或 with rollup 7.having 8.select 9.distinct 10.order by 11.top 类型转换: cast(表达式 as 数据类型) convert(数据类型 , 表达式) select 100.0 + CAST('1000' as int) select 100.0 + CONVERT(int , '1000') --两次都是一样的,将字符串'1000'转换成了int型在于.0相加 select '类型转字符串:' + convert(char(1),2)--整形转换成字符 print convert(varchar(10), getdate(), 120)--转换日期格式,是一种格式 联合结果集union(集合运算符) 1.集合运算符是对两个集合操作的,两个集合必须具有相同的列数,列具有相同的数据类型(至少能隐式转换的), 最终输出的集合的列名由第一个集合的列名来确定。(可以用来连接多个结果) 2.联合(union)与连接(join)不一样 3.例子: select id,name from bbdd union select id,name from bld 4.联合:将多个结果集合并成一个结果集.union(去除重复,相当于默认应用了distinct),union all 5.常见应用:底部汇总。使用union all 不去除重复 例子:统计每种商品的销售总价,并且在底部做汇总 select 商品名称 销售总价 = sum(销售价格*销售数量) from 超市 group by 商品名称 union all select '总销售价格',SUM(销售价格*销售数量)from 超市 order by 销售总价 asc 6.union all 向表中插入多条数据 insert into bld select '毕毕love丹丹' union all select '毕毕love丹丹' union all select '毕毕love丹丹' --向表中插入了三条数据,假如不加all那就是一条了,union去除重复 备份: 备份表,但是约束没有拷贝过去,前提表不存在 select * into 备份表bbdd from bbdd 拷贝表结构,不拷贝数据,前提表不存在 select top 0 * into 表结构bbdd from bbdd 假如表已经存在了,还要备份: insert into 存在的备份表bbdd select * from bbdd --!!!!提前要有这个表!!! --常用的字符串函数: --1.len() 计算字符的个数 print len('毕毕love丹丹') --2.datalength()返回锁占用的字节的个数,这个不是字符串函数 print datalength('毕毕love丹丹')---12 print datalength(N'毕毕love丹丹')---16 --3.转换大小写 print upper('毕毕love丹丹')--转换大写 print lower('毕毕LOVE丹丹')--转换小写 --4.去掉两端空格 print '1111'+' 毕毕love丹丹 '+'1111' print '1111'+ltrim(' 毕毕love丹丹 ')+'1111'--去掉左边空格 print '1111'+rtrim(' 毕毕love丹丹 ')+'1111'--去掉右边空格 print '1111'+rtrim(ltrim(' 毕毕love丹丹 '))+'1111'--去掉两边空格 --5.字符串截取 --left()左边截取 print left('毕毕love丹丹',2)--从左边截取两个 --right()右边截取 print right('毕毕love丹丹',2)--从右边截取两个 --时间: --查询入职一年或以上的员工信息 select * from 员工表 --dateadd() 是增加时间 where dateadd(YEAR,1,入职日期)<=GETDATE() --入职日期加上年和当前时间对比 --datediff()计算两个日期的差 select DATEDIFF(YEAR,'2000-01-01',GETDATE()) select DATEDIFF(MONTH,'2000-01-01',GETDATE()) select DATEDIFF(DAY,'2000-01-01',GETDATE()) select DATEDIFF(HOUR,'2000-01-01',GETDATE()) select DATEDIFF(MINUTE,'2000-01-01',GETDATE()) select DATEDIFF(SECOND,'2000-01-01',GETDATE()) --返回日期的某部分,字符串表示形式.datepart()是数字表示形式 print datename(year,getdate())--字符串表示形式 --datepart()数字表示形式 print datepart(year,getdate()) print datepart(month,getdate()) print datepart(day,getdate()) --打印时间 print getdate() print sysdatetime()--中国方式 --计算工龄 select 工龄=DATEDIFF(YEAR,入职日期,GETDATE()) count(*) as 人数 from 员工表 group by datediff(year,入职日期,getdate()) View Code  Ado.net:   1.Ado.net组成: 数据提供程序(常用类) Connection,用来连接数据库 Command,用来执行SQL语句 DataReader只读、只进的结果集,一条一条读取数据(StreamReader、XMLReader微软的类库中这些Reader的使用方式都差不多) DataAdapter,一个封装了上面3个对象的对象。   数据集(DataSet),临时数据库。 断开式数据操作   2.连接数据库的步骤(推荐集成连接方式)   3.插入 4.删除   5.更新   6.查询表记录数量   7.dataReader1   7.dataReader2   7.dataReader3 7.dataReader4 7.dataReader5 7.dataReader6   8.DBnull 另一个文件 1.null 2.dgv数据绑定(反射只认属性不认字段) 3.插入 3.1 4.行获取焦点事件 5.保存数据1   5.保存数据2   6.删除 7.插入后返回编号 --------------------------------------------------------- SQL注入攻击: 1.验证登陆 2.用户名密码判断 3.漏洞 ------------------------------------------------------------------------------- 带参数: 1.带参数 2.依次添加多个参数   3.一次添加多个参数不严谨   -------------------------------------------------------------------- 连接池: 1.连接池 2.禁用连接池 3.连接池总结 需要记住的,到时找地方插入    对sqlserver的延伸: 关于数据库需要延伸的(一定要看!) ------------------ http://www.w3school.com.cn/sql/sql_datatypes.asp http://www.cnblogs.com/fttbfttb/articles/1510386.html   C#  + SQL语句: 在网上下载东西时不知道进入了什么病毒,我准备的小例子都白白牺牲了,我找出来个几个残留的例子,给大家讲解吧。由浅入深….. 查找学生程序 这个小程序是我大学刚毕业进去公司,面试的一道基础小题,输入一个人名会出现他的名字科目成绩。数据库也要自己写,虽然对于现在来说很简单,但是我当时真的很害怕。这个小例子假如看不懂也不重要,只是先给大家一个“感觉”。 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Data.SqlClient; 10 11 namespace 学¡ì生¦¨²成¨¦绩¡§ 12 { 13 public partial class Form1 : Form 14 { 15 public Form1() 16 { 17 InitializeComponent(); 18 } 19 20 private void Form1_Load(object sender, EventArgs e) 21 { 22 button1.Text = "查¨¦看¡ä"; 23 label1.Text = "姓?名?:êo"; 24 label2.Text = "课?程¨¬:êo"; 25 label3.Text = "成¨¦绩¡§:êo"; 26 label4.Text = "选?择?:êo"; 27 } 28 29 private void button1_Click(object sender, EventArgs e) 30 { 31 using (SqlConnection conn = new SqlConnection("server = .;database=LIANXI;uid =sa;pwd=sa123456")) 32 { 33 conn.Open(); 34 35 using (SqlCommand cmd = conn.CreateCommand()) 36 { 37 cmd.CommandText = "select name,Kname,cj from S,K,Cj where(S.Sid = Cj.Sid AND K.Kid = Cj.Kid and name = @name)"; 38 cmd.Parameters.Add(new SqlParameter("@name", comboBox1.Text)); 39 using (SqlDataReader read = cmd.ExecuteReader()) 40 { 41 while (read.Read()) 42 { 43 string sname = read.GetString(0); 44 string kname = read.GetString(1); 45 long cj = read.GetInt64(2); 46 textBox1.Text = sname; 47 textBox2.Text = kname; 48 textBox3.Text = Convert.ToString(cj); 49 } 50 } 51 } 52 } 53 } 54 } 55 } 代码  --------------- 代码损坏了,把图片放在这,年后把代码写下 SQLhelper: 数据的增删改查+dbnull 登录 -------------------- Command存储过程 1 USE [LIANXI] 2 GO 3 /****** Object: StoredProcedure [dbo].[Sql_ku_nm] Script Date: 01/22/2017 15:18:08 ******/ 4 SET ANSI_NULLS ON 5 GO 6 SET QUOTED_IDENTIFIER ON 7 GO 8 ALTER PROCEDURE [dbo].[Sql_ku_nm] 9 ( 10 @name nchar(10) 11 ) 12 AS 13 insert cmd( 14 name 15 ) values( 16 @name 17 ) SQL存储过程 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Data.SqlClient; 10 using System.Threading; 11 12 namespace Command存ä?储ä¡é过y程¨¬ 13 { 14 public partial class Form1 : Form 15 { 16 public Form1() 17 { 18 InitializeComponent(); 19 } 20 21 private void button1_Click(object sender, EventArgs e) 22 { 23 try 24 { 25 using (SqlCommand cmd = conn.CreateCommand()) 26 { 27 //cmd.CommandText = "insert into cmd(name) values('"+textBox1.Text+"')";//Sql语®?句?执¡ä行D的Ì? 28 29 //下?面?用®?存ä?储ä¡é过y程¨¬执¡ä行D 30 cmd.CommandType = CommandType.StoredProcedure; 31 cmd.CommandText = "Sql_ku_nm"; 32 cmd.Parameters.Add(new SqlParameter("@name", textBox1.Text)); 33 //commandTimeout 等̨¨待äy超?时º¡À 34 cmd.CommandTimeout = 30 * 60;//以°?秒?为a单Ì£¤位? 这a是º?半ã?个?小?时º¡À 超?时º¡À发¤¡é生¦¨²异°¨¬常¡ê 35 36 //这a不?懂? 查¨¦查¨¦ 37 //SqlCommandBuilder 38 //SqlConnectionStringBuilder 39 40 cmd.ExecuteNonQuery(); 41 } 42 } 43 catch (Exception ex) 44 { 45 MessageBox.Show(ex.Message); 46 } 47 } 48 49 private void btn_Null_Click(object sender, EventArgs e) 50 { 51 try 52 { 53 using (SqlCommand cmd = conn.CreateCommand()) 54 { 55 cmd.CommandText = "select * from cmd "; 56 SqlDataReader read = cmd.ExecuteReader(); 57 int i = 2; 58 while (read.Read()) 59 { 60 if (!read.IsDBNull(i))//IsDBNull(i) i代䨲表À¨ª判D断?第̨²几?列¢D 61 { 62 string strName = read.GetString(1); 63 string strPwd = read.GetString(2); 64 listBox1.Items.AddRange(new object[] { strName + " " + strPwd }); 65 //listBox1.Items.Add(strName); 66 //listBox1.Items.Add(strPwd); 67 } 68 } 69 } 70 } 71 catch (Exception ex) 72 { 73 MessageBox.Show(ex.Message); 74 } 75 } 76 SqlConnection conn; 77 private void btn_conn_Click(object sender, EventArgs e) 78 { 79 string sqlconn = "server =.;database =LIANXI;uid=sa;pwd=sa123456"; 80 conn = new SqlConnection(sqlconn); 81 conn.Open(); 82 txtConn.Text = conn.State.ToString(); 83 } 84 85 SqlDataReader read; 86 private void btn_jieguoji_Click(object sender, EventArgs e) 87 { 88 listBox2.Items.Clear(); 89 listBox3.Items.Clear(); 90 try 91 { 92 using (SqlCommand cmd = conn.CreateCommand()) 93 { 94 cmd.CommandText = @"select * from cmd 95 select * from b2";//select * from cmd 96 read = cmd.ExecuteReader(); 97 int i = 0; 98 99 do 100 { 101 while (read.Read()) 102 { 103 string strName = read.GetString(1); 104 string strPwd = read.GetString(2); 105 if (i == 0) 106 listBox2.Items.AddRange(new object[] { strName + " " + strPwd }); 107 else 108 listBox3.Items.AddRange(new object[] { strName + " " + strPwd }); 109 } 110 i++; 111 } while (read.NextResult());//进?入¨?下?一°?个?结¨¢果?集¡¥ 112 } 113 } 114 catch (Exception ex) 115 { 116 MessageBox.Show(ex.Message); 117 } 118 finally 119 { 120 if (read != null) 121 read.Close(); 122 } 123 } 124 125 private void btn_index_Click(object sender, EventArgs e) 126 { 127 try 128 { 129 using (SqlCommand cmd = conn.CreateCommand()) 130 { 131 cmd.CommandText = @"select * from cmd"; 132 read = cmd.ExecuteReader(); 133 134 while (read.Read()) 135 { 136 string strName = read.GetString(read.GetOrdinal("name")); 137 string strPwd = read.GetString(read.GetOrdinal("pwd")); 138 listBox4.Items.Add(strName + " " + strPwd); 139 } 140 } 141 } 142 catch (Exception ex) 143 { 144 MessageBox.Show(ex.Message); 145 } 146 finally 147 { 148 if (read != null) 149 read.Close(); 150 } 151 } 152 153 private void button2_Click(object sender, EventArgs e) 154 { 155 try 156 { 157 using (SqlCommand cmd = conn.CreateCommand()) 158 { 159 cmd.CommandText = @"select * from cmd where name = @name"; 160 cmd.Parameters.Add(new SqlParameter("@name", txt_YN.Text)); 161 read = cmd.ExecuteReader(); 162 163 bool isEx = read.HasRows;//判D断?是º?否¤?存ä?在¨²记?录? 164 int count = read.FieldCount;//返¤¦Ì回?多¨¤少¦¨´字Á?段?,多¨¤少¦¨´列¢D 165 if (isEx) 166 { 167 label1.Text = "存ä?在¨²" + " " + count + "个?列¢D"; 168 } 169 else 170 label1.Text = "不?存ä?在¨²"; 171 } 172 } 173 catch (Exception ex) 174 { 175 MessageBox.Show(ex.Message); 176 } 177 finally 178 { 179 if (read != null) 180 read.Close(); 181 } 182 } 183 } 184 } C#存储过程 ------------ 各种显示: 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Data.SqlClient; 10 11 namespace 练¢¡¤习¡ã 12 { 13 public partial class Form1 : Form 14 { 15 public Form1() 16 { 17 InitializeComponent(); 18 } 19 20 private void btnAllData_Click(object sender, EventArgs e) 21 { 22 listData.Items.Clear(); 23 using (SqlConnection conn = new SqlConnection("server=.;database=LIANXI;uid=sa;password=sa123456")) 24 { 25 conn.Open(); 26 using (SqlCommand cmd = conn.CreateCommand()) 27 { 28 cmd.CommandText = "select *from TT"; 29 using (SqlDataReader read = cmd.ExecuteReader()) 30 while (read.Read()) 31 { 32 string str1 = read.GetValue(0).ToString(); 33 string str2 = read.GetValue(1).ToString(); 34 listData.Items.Add(str1 + "\t" + str2); 35 } 36 } 37 } 38 } 39 string connString = @"Data Source=.\SQLEXPRESS;Initial Catalog=Book;Integrated Security=True"; 40 private void btnone_Click(object sender, EventArgs e) 41 { 42 listData.Items.Clear(); 43 using (SqlConnection conn = new SqlConnection("server=.;database=LIANXI;uid=sa;pwd=sa123456")) 44 { 45 conn.Open(); 46 using (SqlCommand cmd = conn.CreateCommand()) 47 { 48 //cmd.CommandText = "select *from TT where id=(select Min(id) from TT)";//这a两¢?种?一°?样¨´ 49 cmd.CommandText = "select top 1 * from TT "; 50 using (SqlDataReader read = cmd.ExecuteReader()) 51 while (read.Read()) 52 { 53 string str1 = read.GetValue(0).ToString(); 54 string str2 = read.GetValue(1).ToString(); 55 listData.Items.Add(str1 + "\t" + str2); 56 } 57 } 58 } 59 } 60 61 private void btnzh_Click(object sender, EventArgs e) 62 { 63 listData.Items.Clear(); 64 using (SqlConnection conn = new SqlConnection("Data Source=.;Initial Catalog=LIANXI;Integrated Security=True")) 65 { 66 conn.Open(); 67 using (SqlCommand cmd = conn.CreateCommand()) 68 { 69 //cmd.CommandText = "select top 1 * from TT order by id desc "; 70 cmd.CommandText = "select *from TT where id = (select Max(id) from TT)"; 71 using (SqlDataReader read = cmd.ExecuteReader()) 72 while (read.Read()) 73 { 74 string str1 = read.GetValue(0).ToString(); 75 string str2 = read.GetValue(1).ToString(); 76 listData.Items.Add(str1 + "\t" + str2); 77 } 78 } 79 } 80 } 81 82 private void btnName_Click(object sender, EventArgs e) 83 { 84 listData.Items.Clear(); 85 using (SqlConnection conn = new SqlConnection("server=.;database =LIANXI;uid = sa;pwd=sa123456")) 86 { 87 conn.Open(); 88 using (SqlCommand cmd = conn.CreateCommand()) 89 { 90 if (txtID.Text != "") 91 { 92 cmd.CommandText = "select name from TT where id =" + txtID.Text; 93 94 using (SqlDataReader read = cmd.ExecuteReader()) 95 while (read.Read()) 96 { 97 listData.Items.Add(read.GetValue(0).ToString()); 98 } 99 } 100 } 101 } 102 } 103 104 private void btnDategrid_Click(object sender, EventArgs e) 105 { 106 dataGridView1.Columns.Clear(); 107 dataGridView1.Columns.Add("name", "时º¡À间?"); 108 dataGridView1.Columns.Add("name", "数ºy字Á?"); 109 110 using (SqlConnection conn = new SqlConnection("server= .;database=LIANXI;uid=sa;pwd=sa123456")) 111 { 112 conn.Open(); 113 using (SqlCommand cmd = conn.CreateCommand()) 114 { 115 cmd.CommandText = "select time from b4 where time>=@datatime"; 116 cmd.Parameters.Add(new SqlParameter("@datatime", "2016-09-26 16:34:41.000")); 117 DateTime datetime = DateTime.Now; 118 int ii = 0; 119 using (SqlDataReader read = cmd.ExecuteReader()) 120 { 121 while (read.Read()) 122 { 123 datetime = read.GetDateTime(0); 124 dataGridView1.Rows.Add(datetime, ii++); 125 } 126 } 127 } 128 } 129 } 130 } 131 } View Code ------------------ 1.常用函数 1 // AVG函¡¥数ºy 求¨®平?均¨´值¦Ì 2 // count函¡¥数ºy 求¨®行D数ºy 3 // first函¡¥数ºy 指?定¡§字Á?段?中D第̨²一°?个?记?录?的Ì?值¦Ì 经-常¡ê用®?order by语®?句?进?行D排?序¨° 4 // last函¡¥数ºy 指?定¡§的Ì?字Á?段?中D最Á?后¨®一°?个?记?录?的Ì?值¦Ì 5 // max函¡¥数ºy 返¤¦Ì回?一°?列¢D中D的Ì?最Á?大䨮值¦Ì null值¦Ì不?包㨹括¤¡§在¨²计?算?机¨²中D 6 // min函¡¥数ºy 返¤¦Ì回?一°?列¢D中D的Ì?最Á?小?值¦Ì null值¦Ì不?包㨹括¤¡§在¨²计?算?机¨²中D 7 // sum函¡¥数ºy 返¤¦Ì回?数ºy值¦Ì列¢D的Ì?总Á¨¹数ºy(ê¡§总Á¨¹额?)ê? 8 // group by语®?句? 永®¨¤远?结¨¢合?合?计?函¡¥数ºy,ê?根¨´据Y一°?个?或¨°多¨¤个?列¢D对?结¨¢果?集¡¥进?行D分¤?组Á¨¦ 9 10 // having 子Á¨®句? where关?键¨¹字Á?无T法¤¡§与®?合?计?函¡¥数ºy一°?起e使º1用®? 11 // 例¤y子Á¨®:êo 12 // 我¨°们?希¡ê望ª?查¨¦找¨°订?单Ì£¤总Á¨¹金e额?少¦¨´于®¨² 2000 的Ì?客¨ª户¡ì。¡ê 13 // 我¨°们?使º1用®?如¨?下? SQL 语®?句?:êo 14 // SELECT Customer,SUM(OrderPrice) FROM Orders 15 // GROUP BY Customer 16 // HAVING SUM(OrderPrice)<2000 17 18 // MID(ê¡§)ê?函¡¥数ºy 从䨮文?本À?字Á?段?中D提¬¨¢取¨?字Á?符¤? 19 // SELECT MID(column_name,start[,length]) FROM table_name参?数ºy 描¨¨述º? 20 // column_name 必À?需¨¨。¡ê要°a提¬¨¢取¨?字Á?符¤?的Ì?字Á?段?。¡ê 21 // start 必À?需¨¨。¡ê规?定¡§开a始º?位?置?(ê¡§起e始º?值¦Ì是º? 1)ê?。¡ê 22 // length 可¨¦选?。¡ê要°a返¤¦Ì回?的Ì?字Á?符¤?数ºy。¡ê如¨?果?省º?略?,ê?则¨° MID() 函¡¥数ºy返¤¦Ì回?剩º¡ê余®¨¤文?本À?。¡ê 23 24 // LEN(ê¡§)ê?函¡¥数ºy 返¤¦Ì回?文?本À?字Á?段?中D值¦Ì的Ì?长¡è度¨¨ 25 // SELECT LEN(column_name) FROM table_name View Code 2.检测要删除的表是否存在,将其删除 1 --检查要删除的表是否存在 2 use 库名 3 go 4 select *from sysobjects --通过系统表 显示出所有存在的表 5 6 --如果存在就将其表删除,否则不执行删除 7 if exists(select *from sysobjects where name = '要删除的表名') 8 drop table ‘要删除的表名’ 9 go View Code 3.索引 http://www.cnblogs.com/knowledgesea/p/3672099.html 1 --create index 索引名 on 表名(列名) 2 --sp_helpindex 表名 3 --drop index 表名.索引名 4 5 create index b1_indexName on b1(age) 6 sp_helpindex b1 --查看表b1内的所有索引 7 drop index b1.b1_indexName --删除索引 8 select b1_indexName from b1 9 10 11 唯一索引: 12 13 唯一索引不允许两行具有相同的索引值。 14 15 如果现有数据中存在重复的键值,则大多数数据库都不允许将新创建的唯一索引与表一起保存。当新数据将使表中的键值重复时,数据库也拒绝接受此数据。例如,如果在stuInfo表中的学员员身份证号(stuID) 列上创建了唯一索引,则所有学员的身份证号不能重复。 16 17 提示:创建了唯一约束,将自动创建唯一索引。尽管唯一索引有助于找到信息,但为了获得最佳性能,建议使用主键约束或唯一约束。 18 19 20 21 主键索引: 22 23 在数据库关系图中为表定义一个主键将自动创建主键索引,主键索引是唯一索引的特殊类型。主键索引要求主键中的每个值是唯一的。当在查询中使用主键索引时,它还允许快速访问数据。 24 25 26 27 聚集索引(clustered index) 28 29 在聚集索引中,表中各行的物理顺序与键值的逻辑(索引)顺序相同。表只能包含一个聚集索引。例如:汉语字(词)典默认按拼音排序编排字典中的每页页码。拼音字母a,b,c,d……x,y,z就是索引的逻辑顺序,而页码1,2,3……就是物理顺序。默认按拼音排序的字典,其索引顺序和逻辑顺序是一致的。即拼音顺序较后的字(词)对应的页码也较大。如拼音“ha”对应的字(词)页码就比拼音“ba” 对应的字(词)页码靠后。 30 31 32 33 非聚集索引(Non-clustered) 34 35 如果不是聚集索引,表中各行的物理顺序与键值的逻辑顺序不匹配。聚集索引比非聚集索引(nonclustered index)有更快的数据访问速度。例如,按笔画排序的索引就是非聚集索引,“1”画的字(词)对应的页码可能比“3”画的字(词)对应的页码大(靠后)。 36 37 提示:SQL Server中,一个表只能创建1个聚集索引,多个非聚集索引。设置某列为主键,该列就默认为聚集索引 38 ----------------------------------------- 39 CREATE [UNIQUE] [CLUSTERED|NONCLUSTERED] 40 INDEX index_name 41 ON table_name (column_name…) 42 [WITH FILLFACTOR=x] 43 -- UNIQUE表示唯一索引,可选 44 -- CLUSTERED、NONCLUSTERED表示聚集索引还是非聚集索引,可选 45 -- with FILLFACTOR表示填充因子,指定一个0到100之间的值,该值指示索引页填满的空间所占的百分比 46 ------------------------------------- 47 USE stuDB 48 Go 49 IF EXISTS (SELECT name FROM sysindexes 50 WHERE name = 'IX_writtenExam') 51 DROP INDEX stuMarks.IX_writtenExam 52 /*--笔试列创建非聚集索引:填充因子为30%--*/ 53 CREATE NONCLUSTERED INDEX IX_writtenExam 54 ON stuMarks(writtenExam) 55 WITH FILLFACTOR= 30 56 GO 57 /*-----指定按索引 IX_writtenExam 查询----*/ 58 SELECT * FROM stuMarks (INDEX=IX_writtenExam) 59 WHERE writtenExam BETWEEN 60 AND 90 60 虽然我们可以指定SQL Server按哪个索引进行数据查询,但一般不需要我们人工指定。SQL Server将会根据我们创建的索引,自动优化查询 。 61 --------------------------- 62 select *from b1 index=PK_b1 View Code 4.约束: 约束类型: 主键约束:要求主键列不能为空,要求主键列为一 非空约束:要求该列不能存在空值 唯一约束:要求该列的值必须是唯一的,允许为空,但只能出一同个空值 检查约束:限制某列取值的范围是否合适 默认约束:设计某列的默认值 外键约束:用于在两表之间建立关系,需要指定引用主表是哪一列 语法: alter table 表明 add constraint 约束名 约束类型 具体的约束说明 约束的取名规则推荐采纳:约束类型_约束列 主键(Primary Key)约束:如 PK_Userld 唯一(Unique Key)约束: 如 UQ_UserCardid 默认(Default Key)约束: 如 DF_UserPasspwd 检查(Check Key)约束: 如CK_Gender 外键(Foreign Key)约束: 如FK_Sortid -------------------------------------------------- use LIANXI go --为用户表b1添加约束 --add constraint 约束名 约束类型 具体的约束说明 alter table b1 add constraint PK_b1 primary key (id), -- 添加主键 constraint UQ_b1 unique(身份证号), -- 添加唯一主键 constraint DF_b1 Default(0) for 性别, --添加默认主键 constraint CK_b1 Check(age>10 and age<55), --添加检查主键 and是代表 和,or代表 或 constraint FK_b1 Foreign key(name) references b2(name) -- 添加外键,引用的b2表中的name列,b2(name)约束了b1(name) go --批处理 --注意1:外键约束 注意类型,长度必须与引用的主键列的类型 长度完全一致 --注意2:外键约束 注意类型引用的表中必须有主键列 --------------------------------------------------------- 向已有数据中添加约束 语法: alter table 表名 with nocheck add constraint 约束名 约束类型 具体约束说明 对表中现有的数据不做检查 只对添加约束之后在录入的数据进行检查 alter table b1 with nocheck add constraint CK_b1身份证号 check(len(身份证号)=18) go ------------------------------------------------------------ 删除约束 语法: use LIANXI alter table 表名 drop constraint 约束名 go --------------------------------------------------------------- 这个不属于约束了 关系图: 主要是图上连线 假如关系图不成功就是用一下系统自带的存储过程 exec sp_changedbowner sa go 约束 5.视图: 个人理解: 和数据表差不多,本质不同,数据表是实际存储记录的地方 视图不保存任何记录 它存储实际上是查询语句,视图源于数据表 --创建视图 create view view_1 as select *from b1 --修改视图 alter view view_1 as select *from b1,b3,b4 --删除视图 drop view view_old --修改视图名称 exec view_1 view_oldname view_newname --查看视图 select name from view_name --显示视图 select *from view_1 注意事项 每个视图中可以使用多个表 与查询相似,一个视图可以嵌套另一个视图,但最好不要超过3层。 视图定义中 select 语句不能包括一下内容 order by 字句,除非在select 语句的选择列中也有一个top字句 into 关键字 引用临时表或表变量 CREATE VIEW view_name AS SELECT title, au_lname, price, pub_id FROM view_old ----------不同服务器上的------------ 视图可用于在多个数据库或 Microsoft? SQL Server? 2000 实例间对数据进行分区。分区视图可用于在整个服务器组内分布数据库处理。服务器组具有与服务器聚集相同的性能优点,并可用于支持最大的 Web 站点或公司数据中心的处理需求。原始表被细分为多个成员表,每个成员表包含原始表的行子集。每个成员表可放置在不同服务器的数据库中。每个服务器也可得到分区视图。分区视图使用 Transact-SQL UNION 运算符,将在所有成员表上选择的结果合并为单个结果集,该结果集的行为与整个原始表的复本完全一样。例如在三个服务器间进行表分区。在第一个服务器上定义如下的分区视图: CREATE VIEW PartitionedView AS SELECT * FROM MyDatabase.dbo.PartitionTable1 UNION ALL SELECT * FROM Server2.MyDatabase.dbo.PartitionTable2 UNION ALL SELECT * FROM Server3.MyDatabase.dbo.PartitionTable3 在其它两个服务器上定义类似的分区视图。利用这三个视图,三个服务器上任何引用 PartitionedView 的 Transact-SQL 语句都将看到与原始表中相同的行为。似乎每个服务器上都存在原始表的复本一样,而实际上每个表只有一个成员表和分区视图。有关更多信息,请参见视图使用方案。 --------------------------------- 视图的优缺点 优点: 简单、 安全、逻辑数据独立 缺点: 性能,修改限制 如果视图中存在函数或涉及复杂的多表查询,那么用户在查询视图的过程中会花费一定的时间。 对于简单的视图可以使用update 语句更新,如果对于复杂的视图可能就不能使用了。删除亦是如此。所以对于视图的用法最好只停留到查询上面。 View Code 6.触发器: create trigger newtrigger1 --创建的表明 on b2 --操作的表明 for update --用到的操作 as if UPDATE(name) --字段内数据是否改动 print('名字改变') update b2 set name = '毕毕' where id >=3 --上面的是触发器代码 --下面更改name字段数据之后 update b2 set name='bccb' where id>=3 drop trigger trigger_name --删除触发器 --查看数据库中已有触发器 use LIANXI go select * from sysobjects where xtype='TR' --修改触发器 alter trigger trigger_name on {table_name | view_name} {for | After | Instead of } [ insert, update,delete ] as sql_statement rollback transaction --回滚﹐避免加入 --下面的这些有点懵 4﹕在Orders表建立一个更新触发器﹐监视Orders表的订单日期(OrderDate)列﹐使其不能手工修改. create trigger orderdateupdate on orders after update as if update(orderdate) begin raiserror(' orderdate cannot be modified',10,1) rollback transaction end 出现下述情况时,事务会回滚 1.手工用rollback tran回滚 2.设置了set xact_abort on后,出错时会自动回滚 3.事务提交前,电脑出现故障,或者sql意外终止,事务会自动回滚 set XACT_ABORT ON ---如果不设置该项为ON,在sql中默认为OFF,那么只只回滚产生错误的 Transact-SQL 语句;设为ON,回滚整个事务 begin tran t1 ---启动一个事务 update [water].[dbo].[ErrorInf] set ErrorMessage='test' where ID=6 insert into [water].[dbo].[ErrorInf]([ID],ErrorMessage,[Description]) Values(1,'test1','test1') commit tran t1 ---提交事务 ---------------------------------- --下面比较麻烦,手动回滚 begin tran insert into dbo.area values('1111') if @@error>0   rollback insert into dbo.area values('2222') if @@error>0   rollback select 1/0 if @@error>0   rollback insert into dbo.area values('333') if @@error>0   rollback commit ------------------------------------------ 事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。 当一个事务的某一个操作发生问题时,整个事务可以回滚掉,就像没有做任何操作一样。这就是事务回滚。 如果一个事务的所有操作均成功,则就可以提交事务,保证事务的完整性。 应用程序主要通过指定事务启动和结束的时间来控制事务。 以MS SQL Server的Transac-SQL语言为例, 事务启动: BEGIN TRAN [ SACTION ] [ transaction_name | @tran_name_variable [ WITH MARK [ 'description' ] ] ] 结束事务 可以使用 COMMIT 或 ROLLBACK 语句结束事务。 1 事务的提交 BEGIN TRANSACTION T1 INSERT tabel1 ... UPDATE table2 ... ... /* 当所有成功操作完成,提交事务 */ COMMIT TRAN T1 2 事务的回滚 BEGIN TRANSACTION T1 INSERT tabel1 ... ... /* 当发生错误或事务被取消, 回滚事务 */ ROLLBACK TRAN T1 ------------------------------- 总结:事务回滚的几种方法 if exists(...) rollback .. ---- set XACT_ABORT ON begin t1 --执行的语句 commit t1 ---------------------------------- --开启、关闭触发器 disable trigger [触发器名] on database --禁用触发器 enable trigger [触发器名] on database --开启触发器 ----------------------------------- 查询当前数据库中有多少触发器,以方便我进行数据库维护,只需要运行: select * from sysobjects where xtype='TR' --------------------------------------- 查看某一个触发器的内容,直接运行: exec sp_helptext [触发器名] ------------------------------------ 更改触发器的话,只需要将开始的create创建变为alter View Code ---------------- 三层架构----还拿登录   App.config:配置文件,链接数据库字符串 App.config Form1:UI应用层 Form1  Model:使数据在三层中传输   1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 三层架构_登录 7 { 8 //添加类:UserInfo Model实体作用:封装数据,使数据在三层中传输 ,更倾向于业务逻辑层 9 public class Model 10 { 11 //定义用户属性 12 public int ID { get; set; } 13 public string UserName { get; set; } 14 public string Password { get; set; } 15 } 16 } Model Sqlhelp:执行数据库命令操作 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Data.SqlClient; 6 using System.Data; 7 using System.Configuration; 8 9 namespace 三层架构_登录 10 { 11 public class Sqlhelp 12 { 13 public static SqlDataReader ExecuteDataTable(SqlConnection conn, string sql, params SqlParameter[] parameters) 14 { 15 SqlDataReader reader; 16 17 SqlCommand cmd = conn.CreateCommand(); 18 cmd.CommandText = sql; 19 cmd.Parameters.AddRange(parameters); 20 reader = cmd.ExecuteReader(); 21 // conn.Close(); 22 return reader; 23 } 24 } 25 } Sqlhelp BLL:业务逻辑层 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 三层架构_登录 7 { 8 public class BLL 9 { 10 public Model UserLogin(string userName, string password) 11 { 12 DAL uDao = new DAL();//实例化DAL层 13 Model user = uDao.SelectUser(userName, password); 14 15 //不需访问数据源直接执行业务逻辑 16 if (user != null) 17 { 18 return user; 19 } 20 else 21 { 22 throw new Exception("登陆失败"); 23 } 24 } 25 } 26 } BLL DAL:数据访问层 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Data.SqlClient; 6 using System.Data; 7 using System.Configuration; 8 9 namespace 三层架构_登录 10 { 11 public class DAL 12 { 13 private static string connStr = ConfigurationManager.ConnectionStrings["conn_sql"].ConnectionString; 14 15 public Model SelectUser(string userName, string Password) 16 { 17 using (SqlConnection conn = new SqlConnection(connStr)) 18 { 19 conn.Open(); 20 SqlDataReader reader = Sqlhelp.ExecuteDataTable(conn, "SELECT ID,UserName,PassWord FROM 登录表 WHERE UserName=@UserName AND PassWord=@Password" 21 , new SqlParameter[] { new SqlParameter("@UserName", userName), new SqlParameter("@PassWord", Password) }); 22 23 Model user = null; 24 25 //读取具体的数据 26 while (reader.Read()) 27 { 28 if (user == null) 29 { 30 user = new Model(); 31 } 32 //读取查询到的数据 33 user.ID = reader.GetInt32(0); 34 user.UserName = reader.GetString(1); 35 user.Password = reader.GetString(2); 36 } 37 reader.Close(); 38 return user; 39 } 40 } 41 } 42 } DAL  数据表:       -------------------------- 网上查的sqlserver大全 1、说明:创建数据库 CREATE DATABASE database-name 2、说明:删除数据库 drop database dbname 3、说明:备份sql server --- 创建 备份数据的 device USE master EXEC sp_addumpdevice 'disk', 'testBack', 'c:\mssql7backup\MyNwind_1.dat' --- 开始 备份 BACKUP DATABASE pubs TO testBack 4、说明:创建新表 create table tabname(col1 type1 [not null] [primary key],col2 type2 [not null],..) 根据已有的表创建新表: A:create table tab_new like tab_old (使用旧表创建新表) B:create table tab_new as select col1,col2… from tab_old definition only 5、说明:删除新表 drop table tabname 6、说明:增加一个列 Alter table tabname add column col type 注:列增加后将不能删除。DB2中列加上后数据类型也不能改变,唯一能改变的是增加varchar类型的长度。 7、说明:添加主键: Alter table tabname add primary key(col) 说明:删除主键: Alter table tabname drop primary key(col) 8、说明:创建索引:create [unique] index idxname on tabname(col….) 删除索引:drop index idxname 注:索引是不可更改的,想更改必须删除重新建。 9、说明:创建视图:create view viewname as select statement 删除视图:drop view viewname 10、说明:几个简单的基本的sql语句 选择:select * from table1 where 范围 插入:insert into table1(field1,field2) values(value1,value2) 删除:delete from table1 where 范围 更新:update table1 set field1=value1 where 范围 查找:select * from table1 where field1 like ’%value1%’ ---like的语法很精妙,查资料! 排序:select * from table1 order by field1,field2 [desc] 总数:select count as totalcount from table1 求和:select sum(field1) as sumvalue from table1 平均:select avg(field1) as avgvalue from table1 最大:select max(field1) as maxvalue from table1 最小:select min(field1) as minvalue from table1 11、说明:几个高级查询运算词 A: UNION 运算符 UNION 运算符通过组合其他两个结果表(例如 TABLE1 和 TABLE2)并消去表中任何重复行而派生出一个结果表。当 ALL 随 UNION 一起使用时(即 UNION ALL),不消除重复行。两种情况下,派生表的每一行不是来自 TABLE1 就是来自 TABLE2。 B: EXCEPT 运算符 EXCEPT运算符通过包括所有在 TABLE1 中但不在 TABLE2 中的行并消除所有重复行而派生出一个结果表。当 ALL 随 EXCEPT 一起使用时 (EXCEPT ALL),不消除重复行。 C: INTERSECT 运算符 INTERSECT运算符通过只包括 TABLE1 和 TABLE2 中都有的行并消除所有重复行而派生出一个结果表。当 ALL随 INTERSECT 一起使用时 (INTERSECT ALL),不消除重复行。 注:使用运算词的几个查询结果行必须是一致的。 12、说明:使用外连接 A、left (outer) join: 左外连接(左连接):结果集几包括连接表的匹配行,也包括左连接表的所有行。 SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c B:right (outer) join: 右外连接(右连接):结果集既包括连接表的匹配连接行,也包括右连接表的所有行。 C:full/cross (outer) join: 全外连接:不仅包括符号连接表的匹配行,还包括两个连接表中的所有记录。 12、分组:Group by: 一张表,一旦分组 完成后,查询后只能得到组相关的信息。 组相关的信息:(统计信息) count,sum,max,min,avg 分组的标准) 在SQLServer中分组时:不能以text,ntext,image类型的字段作为分组依据 在selecte统计函数中的字段,不能和普通的字段放在一起; 13、对数据库进行操作: 分离数据库: sp_detach_db;附加数据库:sp_attach_db 后接表明,附加需要完整的路径名 14.如何修改数据库的名称: sp_renamedb 'old_name', 'new_name'   二、提升 1、说明:复制表(只复制结构,源表名:a 新表名:b) (Access可用) 法一:select * into b from a where 1<>1(仅用于SQlServer) 法二:select top 0 * into b from a 2、说明:拷贝表(拷贝数据,源表名:a 目标表名:b) (Access可用) insert into b(a, b, c) select d,e,f from b; 3、说明:跨数据库之间表的拷贝(具体数据使用绝对路径) (Access可用) insert into b(a, b, c) select d,e,f from b in ‘具体数据库’ where 条件 例子:..from b in '"&Server.MapPath(".")&"\data.mdb" &"' where.. 4、说明:子查询(表名1:a 表名2:b) select a,b,c from a where a IN (select d from b ) 或者: select a,b,c from a where a IN (1,2,3) 5、说明:显示文章、提交人和最后回复时间 select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title) b 6、说明:外连接查询(表名1:a 表名2:b) select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c 7、说明:在线视图查询(表名1:a ) select * from (SELECT a,b,c FROM a) T where t.a > 1; 8、说明:between的用法,between限制查询数据范围时包括了边界值,not between不包括 select * from table1 where time between time1 and time2 select a,b,c, from table1 where a not between 数值1 and 数值2 9、说明:in 的使用方法 select * from table1 where a [not] in (‘值1’,’值2’,’值4’,’值6’) 10、说明:两张关联表,删除主表中已经在副表中没有的信息 delete from table1 where not exists ( select * from table2 where table1.field1=table2.field1 ) 11、说明:四表联查问题: select * from a left inner join b on a.a=b.b right inner join c on a.a=c.c inner join d on a.a=d.d where ..... 12、说明:日程安排提前五分钟提醒 SQL: select * from 日程安排 where datediff('minute',f开始时间,getdate())>5 13、说明:一条sql 语句搞定数据库分页 select top 10 b.* from (select top 20 主键字段,排序字段 from 表名 order by 排序字段 desc) a,表名 b where b.主键字段 = a.主键字段 order by a.排序字段 具体实现: 关于数据库分页: declare @start int,@end int @sql nvarchar(600) set @sql=’select top’+str(@end-@start+1)+’+from T where rid not in(select top’+str(@str-1)+’Rid from T where Rid>-1)’ exec sp_executesql @sql 注意:在top后不能直接跟一个变量,所以在实际应用中只有这样的进行特殊的处理。Rid为一个标识列,如果top后还有具体的字段,这样做是非常有好处的。因为这样可以避免 top的字段如果是逻辑索引的,查询的结果后实际表中的不一致(逻辑索引中的数据有可能和数据表中的不一致,而查询时如果处在索引则首先查询索引) 14、说明:前10条记录 select top 10 * form table1 where 范围 15、说明:选择在每一组b值相同的数据中对应的a最大的记录的所有信息(类似这样的用法可以用于论坛每月排行榜,每月热销产品分析,按科目成绩排名,等等.) select a,b,c from tablename ta where a=(select max(a) from tablename tb where tb.b=ta.b) 16、说明:包括所有在 TableA中但不在 TableB和TableC中的行并消除所有重复行而派生出一个结果表 (select a from tableA ) except (select a from tableB) except (select a from tableC) 17、说明:随机取出10条数据 select top 10 * from tablename order by newid() 18、说明:随机选择记录 select newid() 19、说明:删除重复记录 1),delete from tablename where id not in (select max(id) from tablename group by col1,col2,...) 2),select distinct * into temp from tablename delete from tablename insert into tablename select * from temp 评价: 这种操作牵连大量的数据的移动,这种做法不适合大容量但数据操作 3),例如:在一个外部表中导入数据,由于某些原因第一次只导入了一部分,但很难判断具体位置,这样只有在下一次全部导入,这样也就产生好多重复的字段,怎样删除重复字段 alter table tablename --添加一个自增列 add column_b int identity(1,1) delete from tablename where column_b not in( select max(column_b) from tablename group by column1,column2,...) alter table tablename drop column column_b 20、说明:列出数据库里所有的表名 select name from sysobjects where type='U' // U代表用户 21、说明:列出表里的所有的列名 select name from syscolumns where id=object_id('TableName') 22、说明:列示type、vender、pcs字段,以type字段排列,case可以方便地实现多重选择,类似select 中的case。 select type,sum(case vender when 'A' then pcs else 0 end),sum(case vender when 'C' then pcs else 0 end),sum(case vender when 'B' then pcs else 0 end) FROM tablename group by type 显示结果: type vender pcs 电脑 A 1 电脑 A 1 光盘 B 2 光盘 A 2 手机 B 3 手机 C 3 23、说明:初始化表table1 TRUNCATE TABLE table1 24、说明:选择从10到15的记录 select top 5 * from (select top 15 * from table order by id asc) table_别名 order by id desc 基础部分 1、1=1,1=2的使用,在SQL语句组合时用的较多 “where 1=1” 是表示选择全部    “where 1=2”全部不选, 如: if @strWhere !='' begin set @strSQL = 'select count(*) as Total from [' + @tblName + '] where ' + @strWhere end else begin set @strSQL = 'select count(*) as Total from [' + @tblName + ']' end 我们可以直接写成 错误!未找到目录项。 set @strSQL = 'select count(*) as Total from [' + @tblName + '] where 1=1 安定 '+ @strWhere 2、收缩数据库 --重建索引 DBCC REINDEX DBCC INDEXDEFRAG --收缩数据和日志 DBCC SHRINKDB DBCC SHRINKFILE 3、压缩数据库 dbcc shrinkdatabase(dbname) 4、转移数据库给新用户以已存在用户权限 exec sp_change_users_login 'update_one','newname','oldname' go 5、检查备份集 RESTORE VERIFYONLY from disk='E:\dvbbs.bak' 6、修复数据库 ALTER DATABASE [dvbbs] SET SINGLE_USER GO DBCC CHECKDB('dvbbs',repair_allow_data_loss) WITH TABLOCK GO ALTER DATABASE [dvbbs] SET MULTI_USER GO 7、日志清除 SET NOCOUNT ON DECLARE @LogicalFileName sysname, @MaxMinutes INT, @NewSize INT USE tablename -- 要操作的数据库名 SELECT @LogicalFileName = 'tablename_log', -- 日志文件名 @MaxMinutes = 10, -- Limit on time allowed to wrap log. @NewSize = 1 -- 你想设定的日志文件的大小(M) Setup / initialize DECLARE @OriginalSize int SELECT @OriginalSize = size FROM sysfiles WHERE name = @LogicalFileName SELECT 'Original Size of ' + db_name() + ' LOG is ' + CONVERT(VARCHAR(30),@OriginalSize) + ' 8K pages or ' + CONVERT(VARCHAR(30),(@OriginalSize*8/1024)) + 'MB' FROM sysfiles WHERE name = @LogicalFileName CREATE TABLE DummyTrans (DummyColumn char (8000) not null) DECLARE @Counter    INT, @StartTime DATETIME, @TruncLog   VARCHAR(255) SELECT @StartTime = GETDATE(), @TruncLog = 'BACKUP LOG ' + db_name() + ' WITH TRUNCATE_ONLY' DBCC SHRINKFILE (@LogicalFileName, @NewSize) EXEC (@TruncLog) -- Wrap the log if necessary. WHILE @MaxMinutes > DATEDIFF (mi, @StartTime, GETDATE()) -- time has not expired AND @OriginalSize = (SELECT size FROM sysfiles WHERE name = @LogicalFileName)   AND (@OriginalSize * 8 /1024) > @NewSize   BEGIN -- Outer loop. SELECT @Counter = 0 WHILE   ((@Counter < @OriginalSize / 16) AND (@Counter < 50000)) BEGIN -- update INSERT DummyTrans VALUES ('Fill Log') DELETE DummyTrans SELECT @Counter = @Counter + 1 END EXEC (@TruncLog)   END SELECT 'Final Size of ' + db_name() + ' LOG is ' + CONVERT(VARCHAR(30),size) + ' 8K pages or ' + CONVERT(VARCHAR(30),(size*8/1024)) + 'MB' FROM sysfiles WHERE name = @LogicalFileName DROP TABLE DummyTrans SET NOCOUNT OFF 8、说明:更改某个表 exec sp_changeobjectowner 'tablename','dbo' 9、存储更改全部表 CREATE PROCEDURE dbo.User_ChangeObjectOwnerBatch @OldOwner as NVARCHAR(128), @NewOwner as NVARCHAR(128) AS DECLARE @Name    as NVARCHAR(128) DECLARE @Owner   as NVARCHAR(128) DECLARE @OwnerName   as NVARCHAR(128) DECLARE curObject CURSOR FOR select 'Name'    = name,    'Owner'    = user_name(uid) from sysobjects where user_name(uid)=@OldOwner order by name OPEN   curObject FETCH NEXT FROM curObject INTO @Name, @Owner WHILE(@@FETCH_STATUS=0) BEGIN      if @Owner=@OldOwner begin    set @OwnerName = @OldOwner + '.' + rtrim(@Name)    exec sp_changeobjectowner @OwnerName, @NewOwner end -- select @name,@NewOwner,@OldOwner FETCH NEXT FROM curObject INTO @Name, @Owner END close curObject deallocate curObject GO 10、SQL SERVER中直接循环写入数据 declare @i int set @i=1 while @i<30 begin     insert into test (userid) values(@i)     set @i=@i+1 end 案例: 有如下表,要求就裱中所有沒有及格的成績,在每次增長0.1的基礎上,使他們剛好及格: Name score Zhangshan 80 Lishi 59 Wangwu 50 Songquan 69 while((select min(score) from tb_table)<60) begin update tb_table set score =score*1.01 where score<60 if (select min(score) from tb_table)>60 break else continue end 技巧部分 1.按姓氏笔画排序: Select * From TableName Order By CustomerName Collate Chinese_PRC_Stroke_ci_as //从少到多 2.数据库加密: select encrypt('原始密码') select pwdencrypt('原始密码') select pwdcompare('原始密码','加密后密码') = 1--相同;否则不相同 encrypt('原始密码') select pwdencrypt('原始密码') select pwdcompare('原始密码','加密后密码') = 1--相同;否则不相同 3.取回表中字段: declare @list varchar(1000), @sql nvarchar(1000) select @list=@list+','+b.name from sysobjects a,syscolumns b where a.id=b.id and a.name='表A' set @sql='select '+right(@list,len(@list)-1)+' from 表A' exec (@sql) 4.查看硬盘分区: EXEC master..xp_fixeddrives 5.比较A,B表是否相等: if (select checksum_agg(binary_checksum(*)) from A)      =     (select checksum_agg(binary_checksum(*)) from B) print '相等' else print '不相等' 6.杀掉所有的事件探察器进程: DECLARE hcforeach CURSOR GLOBAL FOR SELECT 'kill '+RTRIM(spid) FROM master.dbo.sysprocesses WHERE program_name IN('SQL profiler',N'SQL 事件探查器') EXEC sp_msforeach_worker '?' 7.记录搜索: 开头到N条记录 Select Top N * From 表 ------------------------------- N到M条记录(要有主索引ID) Select Top M-N * From 表 Where ID in (Select Top M ID From 表) Order by ID   Desc ---------------------------------- N到结尾记录 Select Top N * From 表 Order by ID Desc 案例 例如1:一张表有一万多条记录,表的第一个字段 RecID 是自增长字段, 写一个SQL语句, 找出表的第31到第40个记录。 select top 10 recid from A where recid not in(select top 30 recid from A) 分析:如果这样写会产生某些问题,如果recid在表中存在逻辑索引。 select top 10 recid from A where……是从索引中查找,而后面的select top 30 recid from A则在数据表中查找,这样由于索引中的顺序有可能和数据表中的不一致,这样就导致查询到的不是本来的欲得到的数据。 解决方案 1,用order by select top 30 recid from A order by ricid 如果该字段不是自增长,就会出现问题 2,在那个子查询中也加条件:select top 30 recid from A where recid>-1 例2:查询表中的最后以条记录,并不知道这个表共有多少数据,以及表结构。 set @s = 'select top 1 * from T where pid not in (select top ' + str(@count-1) + ' pid from T)' print @s exec sp_executesql @s 9:获取当前数据库中的所有用户表 select Name from sysobjects where xtype='u' and status>=0 10:获取某一个表的所有字段 select name from syscolumns where id=object_id('表名') select name from syscolumns where id in (select id from sysobjects where type = 'u' and name = '表名') 两种方式的效果相同 11:查看与某一个表相关的视图、存储过程、函数 select a.* from sysobjects a, syscomments b where a.id = b.id and b.text like '%表名%' 12:查看当前数据库中所有存储过程 select name as 存储过程名称 from sysobjects where xtype='P' 13:查询用户创建的所有数据库 select * from master..sysdatabases D where sid not in(select sid from master..syslogins where name='sa') 或者 select dbid, name AS DB_NAME from master..sysdatabases where sid <> 0x01 14:查询某一个表的字段和数据类型 select column_name,data_type from information_schema.columns where table_name = '表名' 15:不同服务器数据库之间的数据操作 --创建链接服务器 exec sp_addlinkedserver 'ITSV ', ' ', 'SQLOLEDB ', '远程服务器名或ip地址 ' exec sp_addlinkedsrvlogin 'ITSV ', 'false ',null, '用户名 ', '密码 ' --查询示例 select * from ITSV.数据库名.dbo.表名 --导入示例 select * into 表 from ITSV.数据库名.dbo.表名 --以后不再使用时删除链接服务器 exec sp_dropserver 'ITSV ', 'droplogins '   --连接远程/局域网数据(openrowset/openquery/opendatasource) --1、openrowset --查询示例 select * from openrowset( 'SQLOLEDB ', 'sql服务器名 '; '用户名 '; '密码 ',数据库名.dbo.表名) --生成本地表 select * into 表 from openrowset( 'SQLOLEDB ', 'sql服务器名 '; '用户名 '; '密码 ',数据库名.dbo.表名)   --把本地表导入远程表 insert openrowset( 'SQLOLEDB ', 'sql服务器名 '; '用户名 '; '密码 ',数据库名.dbo.表名) select *from 本地表 --更新本地表 update b set b.列A=a.列A from openrowset( 'SQLOLEDB ', 'sql服务器名 '; '用户名 '; '密码 ',数据库名.dbo.表名)as a inner join 本地表 b on a.column1=b.column1 --openquery用法需要创建一个连接 --首先创建一个连接创建链接服务器 exec sp_addlinkedserver 'ITSV ', ' ', 'SQLOLEDB ', '远程服务器名或ip地址 ' --查询 select * FROM openquery(ITSV, 'SELECT * FROM 数据库.dbo.表名 ') --把本地表导入远程表 insert openquery(ITSV, 'SELECT * FROM 数据库.dbo.表名 ') select * from 本地表 --更新本地表 update b set b.列B=a.列B FROM openquery(ITSV, 'SELECT * FROM 数据库.dbo.表名 ') as a inner join 本地表 b on a.列A=b.列A   --3、opendatasource/openrowset SELECT * FROM opendatasource( 'SQLOLEDB ', 'Data Source=ip/ServerName;User ID=登陆名;Password=密码 ' ).test.dbo.roy_ta --把本地表导入远程表 insert opendatasource( 'SQLOLEDB ', 'Data Source=ip/ServerName;User ID=登陆名;Password=密码 ').数据库.dbo.表名 select * from 本地表 数据开发部分--经典 1.字符串函数 长度与分析用 1,datalength(Char_expr) 返回字符串包含字符数,但不包含后面的空格 2,substring(expression,start,length) 取子串,字符串的下标是从“1”,start为起始位置,length为字符串长度,实际应用中以len(expression)取得其长度 3,right(char_expr,int_expr) 返回字符串右边第int_expr个字符,还用left于之相反 4,isnull( check_expression , replacement_value )如果check_expression為空,則返回replacement_value的值,不為空,就返回check_expression字符操作类 5,Sp_addtype自定義數據類型 例如:EXEC sp_addtype birthday, datetime, 'NULL' 6,set nocount {on|off} 使返回的结果中不包含有关受 Transact-SQL 语句影响的行数的信息。如果存储过程中包含的一些语句并不返回许多实际的数据,则该设置由于大量减少了网络流量,因此可显著提高性能。SET NOCOUNT 设置是在执行或运行时设置,而不是在分析时设置。SET NOCOUNT 为 ON 时,不返回计数(表示受 Transact-SQL 语句影响的行数)。 SET NOCOUNT 为 OFF 时,返回计数 常识   在SQL查询中:from后最多可以跟多少张表或视图:256在SQL语句中出现 Order by,查询时,先排序,后取在SQL中,一个字段的最大容量是8000,而对于nvarchar(4000),由于nvarchar是Unicode码。 SQLServer2000 同步复制技术实现步骤 一、 预备工作 1.发布服务器,订阅服务器都创建一个同名的windows用户,并设置相同的密码,做为发布快照文件夹的有效访问用户--管理工具--计算机管理--用户和组--右键用户--新建用户--建立一个隶属于administrator组的登陆windows的用户(SynUser)2.在发布服务器上,新建一个共享目录,做为发布的快照文件的存放目录,操作: 我的电脑--D:\ 新建一个目录,名为: PUB --右键这个新建的目录--属性--共享--选择"共享该文件夹"--通过"权限"按纽来设置具体的用户权限,保证第一步中创建的用户(SynUser) 具有对该文件夹的所有权限   --确定3.设置SQL代理(SQLSERVERAGENT)服务的启动用户(发布/订阅服务器均做此设置) 开始--程序--管理工具--服务 --右键SQLSERVERAGENT--属性--登陆--选择"此账户"--输入或者选择第一步中创建的windows登录用户名(SynUser)--"密码"中输入该用户的密码4.设置SQL Server身份验证模式,解决连接时的权限问题(发布/订阅服务器均做此设置) 企业管理器 --右键SQL实例--属性--安全性--身份验证--选择"SQL Server 和 Windows"--确定5.在发布服务器和订阅服务器上互相注册 企业管理器 --右键SQL Server组--新建SQL Server注册...--下一步--可用的服务器中,输入你要注册的远程服务器名 --添加--下一步--连接使用,选择第二个"SQL Server身份验证"--下一步--输入用户名和密码(SynUser)--下一步--选择SQL Server组,也可以创建一个新组--下一步--完成6.对于只能用IP,不能用计算机名的,为其注册服务器别名(此步在实施中没用到) (在连接端配置,比如,在订阅服务器上配置的话,服务器名称中输入的是发布服务器的IP) 开始--程序--Microsoft SQL Server--客户端网络实用工具 --别名--添加--网络库选择"tcp/ip"--服务器别名输入SQL服务器名--连接参数--服务器名称中输入SQL服务器ip地址--如果你修改了SQL的端口,取消选择"动态决定端口",并输入对应的端口号 二、 正式配置 1、配置发布服务器 打开企业管理器,在发布服务器(B、C、D)上执行以下步骤: (1) 从[工具]下拉菜单的[复制]子菜单中选择[配置发布、订阅服务器和分发]出现配置发布和分发向导(2) [下一步] 选择分发服务器 可以选择把发布服务器自己作为分发服务器或者其他sql的服务器(选择自己)(3) [下一步] 设置快照文件夹 采用默认\\servername\Pub (4) [下一步] 自定义配置 可以选择:是,让我设置分发数据库属性启用发布服务器或设置发布设置 否,使用下列默认设置(推荐) (5) [下一步] 设置分发数据库名称和位置 采用默认值(6) [下一步] 启用发布服务器 选择作为发布的服务器(7) [下一步] 选择需要发布的数据库和发布类型(8) [下一步] 选择注册订阅服务器(9) [下一步] 完成配置2、创建出版物 发布服务器B、C、D上 (1)从[工具]菜单的[复制]子菜单中选择[创建和管理发布]命令(2)选择要创建出版物的数据库,然后单击[创建发布](3)在[创建发布向导]的提示对话框中单击[下一步]系统就会弹出一个对话框。对话框上的内容是复制的三个类型。我们现在选第一个也就是默认的快照发布(其他两个大家可以去看看帮助)(4)单击[下一步]系统要求指定可以订阅该发布的数据库服务器类型,SQLSERVER允许在不同的数据库如 orACLE或ACCESS之间进行数据复制。 但是在这里我们选择运行"SQL SERVER 2000"的数据库服务器 (5)单击[下一步]系统就弹出一个定义文章的对话框也就是选择要出版的表 注意: 如果前面选择了事务发布 则再这一步中只能选择带有主键的表 (6)选择发布名称和描述(7)自定义发布属性 向导提供的选择: 是 我将自定义数据筛选,启用匿名订阅和或其他自定义属性 否 根据指定方式创建发布 (建议采用自定义的方式) (8)[下一步] 选择筛选发布的方式(9)[下一步] 可以选择是否允许匿名订阅1)如果选择署名订阅,则需要在发布服务器上添加订阅服务器 方法: [工具]->[复制]->[配置发布、订阅服务器和分发的属性]->[订阅服务器] 中添加 否则在订阅服务器上请求订阅时会出现的提示:改发布不允许匿名订阅 如果仍然需要匿名订阅则用以下解决办法 [企业管理器]->[复制]->[发布内容]->[属性]->[订阅选项] 选择允许匿名请求订阅2)如果选择匿名订阅,则配置订阅服务器时不会出现以上提示(10)[下一步] 设置快照 代理程序调度(11)[下一步] 完成配置 当完成出版物的创建后创建出版物的数据库也就变成了一个共享数据库 有数据 srv1.库名..author有字段:id,name,phone, srv2.库名..author有字段:id,name,telphone,adress   要求: srv1.库名..author增加记录则srv1.库名..author记录增加srv1.库名..author的phone字段更新,则srv1.库名..author对应字段telphone更新 --*/   --大致的处理步骤--1.在 srv1 上创建连接服务器,以便在 srv1 中操作 srv2,实现同步exec sp_addlinkedserver 'srv2','','SQLOLEDB','srv2的sql实例名或ip' exec sp_addlinkedsrvlogin 'srv2','false',null,'用户名','密码' go --2.在 srv1 和 srv2 这两台电脑中,启动 msdtc(分布式事务处理服务),并且设置为自动启动 。我的电脑--控制面板--管理工具--服务--右键 Distributed Transaction Coordinator--属性--启动--并将启动类型设置为自动启动 go     --然后创建一个作业定时调用上面的同步处理存储过程就行了   企业管理器 --管理--SQL Server代理--右键作业--新建作业--"常规"项中输入作业名称--"步骤"项--新建--"步骤名"中输入步骤名--"类型"中选择"Transact-SQL 脚本(TSQL)" --"数据库"选择执行命令的数据库--"命令"中输入要执行的语句: exec p_process --确定--"调度"项--新建调度--"名称"中输入调度名称--"调度类型"中选择你的作业执行安排--如果选择"反复出现" --点"更改"来设置你的时间安排     然后将SQL Agent服务启动,并设置为自动启动,否则你的作业不会被执行   设置方法: 我的电脑--控制面板--管理工具--服务--右键 SQLSERVERAGENT--属性--启动类型--选择"自动启动"--确定.     --3.实现同步处理的方法2,定时同步   --在srv1中创建如下的同步处理存储过程 create proc p_process as --更新修改过的数据 update b set name=i.name,telphone=i.telphone from srv2.库名.dbo.author b,author i where b.id=i.id and (b.name <> i.name or b.telphone <> i.telphone)   --插入新增的数据insert srv2.库名.dbo.author(id,name,telphone) select id,name,telphone from author i where not exists( select * from srv2.库名.dbo.author where id=i.id)   --删除已经删除的数据(如果需要的话) delete b from srv2.库名.dbo.author b where not exists( select * from author where id=b.id) go SQL Server基本函数  --------------------------- C#数据库的项目---管理系统     wpf                                                                    
/template/Home/Zkeys/PC/Static