1C#编程三剑客《C#网络应用编程基础》马骏主编基础知识部分,适用于初学者。《C#网络应用高级编程》马骏、郑逢斌、沈夏炯编著高级编程部分,适用于高级开发者。《C#网络应用编程开发实例与实验指导》马骏编著上机题与项目开发实例(从设计到安装程序制作一网打尽)。2C#网络应用高级编程3第1章进程、线程与网络协议1.1进程和线程1.2IP地址与端口1.3套接字1.4网络流41.1进程和线程进程是对一段静态指令序列(程序)的动态执行过程,是系统进行资源分配和调度的一个基本单位。与进程相关的信息包括:进程的用户标志、正在执行的已经编译好的程序、进程程序和数据在存储器中的位置等。同一个进程又可以划分为若干个独立的执行流,我们称之为线程。线程可以看作是进程的一个实例,是CPU调度和分配的基本单位。在Windows环境下,用户可以同时运行多个应用程序,每个执行的应用程序就是一个进程。51.1进程和线程(续)并行性的主要特点:并发处理在一个单处理器系统中可以通过分时处理来获得并发,系统为每个线程分配一个CPU时间片,每个线程只有在分配的时间片内才拥有对CPU的控制权,其它时间都在等待。61.1.1Process类Process类位于System.Diagnostics名称空间下,它专门用于完成系统进程的管理任务。可以在本地计算机上启动和停止进程,也可以向进程查询特定类型的信息。在远程计算机上,无法启动和停止进程,但可以查询进程的相关信息。在对进程进行操作时,首先要创建Process类的实例,其次还需要设置其对象成员的StartInfo属性,最后调用它的Start方法。7例.启动、停止和观察进程1.新建一个名为ProcessExample的Windows应用程序。2.从工具箱中将Process组件拖放到设计窗体。3.添加名称空间:usingSystem.Diagnostics;usingSystem.Threading;4.添加“启动记事本”、“停止记事本”和“观察所有进程”三个按钮,并添加Click事件代码:privatevoidbuttonStart_Click(objectsender,EventArgse){process1.StartInfo.FileName=notepad.exe;//启动Notepad.exe进程.process1.Start();}8privatevoidbuttonStop_Click(objectsender,EventArgse){//创建新的Process组件的数组,并将它们与指定的进程名称(Notepad)的所有进程资源相关联.Process[]myprocesses;myprocesses=Process.GetProcessesByName(Notepad);foreach(Processinstanceinmyprocesses){//设置终止当前线程前等待1000毫秒instance.WaitForExit(1000);instance.CloseMainWindow();}}privatevoidbuttonView_Click(objectsender,EventArgse){listBox1.Items.Clear();//创建Process类型的数组,并将它们与系统内所有进程相关联9Process[]processes;processes=Process.GetProcesses();foreach(Processpinprocesses){//由于访问Idle的StartTime会出现异常,所以将其排除在外if(p.ProcessName!=Idle){//将每个进程名和进程开始时间加入listBox1中this.listBox1.Items.Add(string.Format({0,-30}{1:h:m:s},p.ProcessName,p.StartTime));}}}101.1.2Thread类在System.Threading名称空间下,Thread类是用于创建和控制线程的,对线程的常用操作有:启动线程、终止线程、合并线程和让线程休眠等。11启动线程在使用线程前,首先要创建一个线程。其一般形式为:Threadt=newThread(enterPoint);其中enterPoint为线程的入口,即线程开始执行的方法。在托管代码中,是通过委托处理线程执行的代码的。例:Threadt=newThread(newThreadStart(methodName));创建线程实例后,就可以调用Start方法启动线程了。121.1.2Thread类(续)终止线程线程启动后,当不需要某个线程继续执行的时候,就需要终止该线程。终止线程调用Thread类的Abort方法。例如:t.Abort();Abort方法没有参数。当调用Abort方法时,CLR可能不会立即终止线程。主线程调用子线程的Abort方法后,结束子线程会占用大量CPU的时间,表面上看就像死机。为解决这个问题,可以在主线程中调用子线程对象的Join方法,并在Join方法中指定主线程等待子线程结束的等待时间。131.1.2Thread类(续)合并线程Join方法用于把两个并行执行的线程合并为一个单个的线程。如果一个线程t1在执行的过程中需要等待另一个线程t2结束后才继续执行,可以在t1中调用t2的join()方法。如:t2.Join();这样t1在执行到t2.join()语句后就会处于组塞状态,直到t2结束后才会继续执行。为了解决假如t2一直不结束的问题,可以在调用t2的Join方法的时候指定一个等待时间。如:t2.Join(100);Join方法通常和Abort一起使用。141.1.2Thread类(续)让线程休眠在多线程应用程序中,有时候并不希望某一个线程继续执行,而是希望该线程停止一段时间,等待其它线程执行之后再接着执行。这时可以调用Thread类的Sleep方法,即让线程休眠。例如:Thread.Sleep(1000);这条语句的功能是让当前线程休眠1000毫秒。注意,调用Sleep方法的是类本身,而不是类的实例。休眠的是该语句所在的线程,而不是其他线程。151.1.2Thread类(续)线程优先级在C#应用程序中,可以对线程设定五个不同的优先级,由高到低分别是Highest、AboveNormal、Normal、BelowNormal和Lowest。在创建线程时如果不指定其优先级,则系统默认为Normal。若想让一些重要的线程优先执行,可以使用下面的方法为其赋予较高的优先级:Threadt=newThread(newThreadStart(enterpoint));t.priority=ThreadPriority.AboveNormal;设置线程的优先级可改变线程的执行顺序,所设置的优先级仅适用于这些线程所属的进程。当把某线程的优先级设置为Highest时,系统正在运行的其它线程都会止。161.1.2Thread类(续)线程池线程池是一种多线程处理形式,线程池为线程生命周期的开销问题和资源不足问题提供了很好的解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。线程池适用于需要多个线程而实际执行时间又不多的场合。但是如果线程要求运行的时间比较长的话,那么此时线程的运行时间比线程的创建时间要长得多,仅靠减少线程的创建时间对系统效率的提高就不是那么明显了,此时就不适合使用线程池技术171.1.2Thread类(续)同步同步指在某一时刻只有一个线程可以访问变量或者对象。线程t1对variable1操作时,为了避免其他线程也对其进行操作,可以使用C#提供的lock语句将variable1锁定。实现代码为:lock(variable1);{variable1+=1;}注意:不要将被同步的对象声明为public。18例.在程序中使用线程1.新建一个名为ThreadExample的Windows应用程序。2.向设计窗体拖放一个Timer组件。3.名称空间:usingSystem.Threading;4.字段声明:StringBuildersb=newStringBuilder();Threadthread1;Threadthread2;5.代码:privatevoidAppendString(strings){lock(sb){19str.Append(s);}}publicvoidMethod1(){while(true){Thread.Sleep(100);//线程休眠100毫秒AppendString(a);}}publicvoidMethod2(){while(true)20{Thread.Sleep(100);//线程休眠100毫秒AppendString(b);}}6.启动线程和终止线程按钮的Click事件中添加代码:privatevoidbuttonStart_Click(objectsender,EventArgse){sb.Remove(0,sb.Length);timer1.Enabled=true;thread1=newThread(newThreadStart(Method1));thread2=newThread(newThreadStart(Method2));thread1.Start();21thread2.Start();}privatevoidbuttonAbort_Click(objectsender,EventArgse){thread1.Abort();thread1.Join(10);thread2.Abort();thread2.Join(10);}7.timer1的Tick事件代码:privatevoidtimer1_Tick(objectsender,EventArgse){if(thread1.IsAlive==true||thread2.IsAlive==true)22{richTextBox1.Text=sb.ToString();}else{timer1.Enabled=false;}}键编译并执行,单击启动线程后,等一会再单击终止线程,查看运行结果。231.1.3在一个线程中操作另一个线程的控件默认情况下,为了防止引起死锁等不安全因素,C#不允许在一个线程中直接操作另一个线程中的控件。但是在Windows应用程序中,为了在窗体上显示线程中处理的信息,我们可能需要经常在一个线程中引用另一个线程中的窗体控件。比较常用的办法是使用委托(delegate)来完成这个工作。例.一个线程操作另一个线程的控件的方法。241.2IP地址与端口IP(InternetProtocol)是internet网络设备之间传输数据的一种协议。本节所讲的端口虽逻辑意义上的端口,是指TCP/IP协议中的端口。这一节我们将对IPAddress、IPHostEntry、IPEndPoint等System.Net命名空间中的几个类进行简单的介绍。251.2.1TCP/IP网络协议网络协议TCP/IPIP地址261.2.2IPAddress类与Dns类IPAddress类提供了对IP地址的转换、处理等功能。其Parse方法可将IP地址字符串转换为IPAddress实例。如:IPAddressip=IPAddress.Parse(“192.168.1.1”);IPAddress类提供了7个只读字段:Any表示本地系统可用的任何IP地址Broadcast表示本地网络的IP广播地址IPv6AnySocket.Bind方法用此字段指出本地系统可用的IP地址IPv6Loopback表示系统的回送地址IPv6None表示系统上没有可用的网络接口Loopback表示系统的回送地址None表示系统上没有可用的网络接口271.2.2IPAddress类与Dns类(续)Dns类提供了一系列静态的方法,用于获取提供本地或远程域名等功能,常用方法有:1)GetHost