C#网络编程基础

Eddy 发布于2012-9-21 9:43:46 分类: 程序设计 已浏览loading 网友评论1条 我要评论

 一、进程与线程
当要花费大量时间进行连续的操作时,或者等待网络或其他I/O设备响应时,都可以使用多线程技术。在C#中,有两个专门用于处理进程和线程的类:Process类和Thread类。
Process类位于System.Diagnostics命名空间下,用于完成进程的相关处理任务。在自己的程序中运行其他的应用程序,实际上就是对进程进行管理。如果希望在自己的进程中启动和停止其他进程,首先要创建Process类的实例,并设置对象的StartInfo属性,然后调用该对象的Start方法启动进程。
在System.Threading命名空间下,包含了用于创建和控制线程的Thread类。对线程的常用操作有:启动线程、终止线程、合并线程和让线程休眠等。
在托管代码中,通过委托处理线程执行的代码。
Thread t=new Thread(new ThreadStart(methodName));
1、终止线程:
1.1、事先设置一个布尔变量,在其他线程中通过修改该变量的值作为传递给该线程是否需要终止的判断条件,而在该线程中循环判断该条件,以确定是否退出线程。
1.2、通过调用Thread类的Abort方法强行终止线程。Abort通过抛出异常强行终止结束线程,因此在实际编程中,应该尽量避免采用这种方法。例如: t.Abort();
2、等待子线程结束:
在主线程中调用子线程对象的Join方法,并在Join方法中指定主线程等待子线程结束的等待时间。
Join方法用于把两个并行执行的线程合并为一个单个的线程。。如果一个线程t1在执行的过程中需要等待另一个线程t2结束后才能继续执行,可以在t1的程序模块中调用t2的join()方法。Join方法通常和Abort一起使用。
3、线程休眠:以调用Thread类的Sleep方法。
4、线程优先级:
假如想让一些重要的线程优先执行,可以使用下面的方法为其赋予较高的优先级:
Thread t=new Thread(new ThreadStart(enterpoint)); t.priority=ThreadPriority.AboveNormal;
5、线程池技术(线程池适用于需要多个线程而实际执行时间又不多的场合,比如有些常处于阻塞状态的线程。当一个应用程序服务器接受大量短小线程的请求时,使用线程池技术是非常合适的):
大大减少线程创建和销毁的次数、可限制同一时刻处理的线程数目。
6、线程同步
为了对线程中的同步对象进行操作,C#提供了lock语句锁定需要同步的对象。lock关键字确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻塞),直到该对象被释放。注意,锁定的对象一定要声明为private。
死锁:如果两个不同的线程同时锁定两个不同的变量,而每个线程又都希望在锁定期间访问对方锁定的变量,那么两个线程在得到对方变量的访问权之前都不会释放自己锁定的对象。
7、在一个线程中操作另一个线程的控件
为了区别是否是创建控件的线程访问该控件对象,Windows应用程序中的每一个控件对象都有一个InvokeRequired属性,用于检查是否需要通过调用Invoke方法完成其他线程对该控件的操作,如果该属性为true,说明是其他线程操作该控件,这时可以创建一个委托实例,然后调用控件对象的Invoke方法,并传入需要的参数完成相应操作,否则可以直接对该控件对象进行操作,从而保证了安全代码下线程间的互操作。
delegate void AppendStringDelegate(string str);
private void AppendString(string str) {
if (richTextBox1.InvokeRequired) {
AppendStringDelegate d = new AppendStringDelegate(AppendString);
richTextBox1.Invoke(d, "abc");
}
else {
richTextBox1.Text += str;
}
}

二、IP、端口、套接字
2.1、在System.Net命名空间中,IPAddress类提供了对IP地址的转换、处理等功能。
System.Net命名空间下,还有一个Dns类,该类提供了一系列静态的方法,用于获取提供本地或远程域名等功能。
IPHostEntry类的实例对象中包含了Internet主机的相关信息。常用属性有两个:一个是AddressList属性,另一个是HostName属性。在Dns类中,有一个专门获取IPHostEntry对象的方法,通过IPHostEntry对象,可以获取本地或远程主机的相关IP地址。
listBox1.Items.Add("本机IP地址为:");
ip = Dns.GetHostEntry(Dns.GetHostName()).AddressList;
listBox1.Items.AddRange(ip);
网络地址标识网络上的设备;端口号标识该设备上的特定服务。网络地址和服务端口的组合称为端点。在C#中,使用IPEndPoint类表示这个端点,该类包含了应用程序连接到主机上的服务所需的IP地址和端口信息。
2.2、要通过互联网进行通信,至少需要一对套接字,其中一个运行于客户端,我们称之为ClientSocket,另一个运行于服务器端,我们称之为ServerSocket。根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。
使用套接字处理数据有两种基本模式:同步套接字和异步套接字。同步套接字的特点是在通过Socket进行连接、接收、发送操作时,客户机或服务器在接收到对方响应前会处于阻塞状态,即一直等到接收到对方请求时才继续执行下面的语句;异步套接字在通过Socket进行连接、接收、发送操作时,客户机或服务器不会处于阻塞方式,而是利用callback机制进行连接、接收和发送处理,这样就可以在调用发送或接收的方法后直接返回,并继续执行下面的程序。
2.3、Socket类
Socket类包含在System.Net.Sockets命名空间中。一个Socket实例包含了一个本地或者一个远程端点的套接字信息。
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
在实际应用中,还可以通过调用Socket对象的SetSocketOption方法设置套接字的各种选项。

面向连接的套接字编程:
服务端 客户端
Socket() Socket()
Bind()
Listen()
Accept()<-----------Connect()
Receive()<----------Send()
Send()------------->Receive()
Close() Close()

服务端代码:
IPHostEntry local = Dns.GetHostByName(Dns.GetHostName());
IPEndPoint iep = new IPEndPoint(local.AddressList[0], 80);
Socket localSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); localSocket.Bind(iep);
locatSocket.Listen(10);
Socket clientSocket = localSocket.Accept();//阻塞状态,直到有客户机请求连接

客户端代码:
IPAddress remoteHost = IPAddress.Parse("192.168.0.1");
PEndPoint iep = new IPEndPoint(remoteHost, 80);
Socket localSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); localSocket.Connect(iep);//客户端在与服务器建立连接之前处于阻塞状态

注意通信完成后,必须先用Shutdown方法停止会话,然后关闭Socket实例。
关闭连接的典型用法:
sock.Shutdown(SocketShutdown.Both);
sock.Close();

无连接套接字编程
设备1 设备2
Socket() Socket()
Bind() Bind()
ReceiveFrom()<----SendTo()
SendTo()---------->ReceiveFrom()
Close() Close()
需要接收数据时,必须使用Bind方法将套接字绑定到一个本地地址/端口对上之后才能使用ReceiveFrom方法接收数据,如果只发送而不接收,则不需要使用Bind方法。
为了简化复杂的网络编程,.NET Framework除了提供可以灵活控制的套接字类以外,还在此基础上提供了对套接字封装后的基于不同协议的更易于使用的类。

2.4、流
流(stream)是对串行传输的数据的一种抽象表示,底层的设备可以是文件、外部设备、主存、网络套接字等等。
流有三种基本的操作:写入、读取和查找。
如果数据从内存缓冲区传输到外部源,这样的流叫作“写入流”。
如果数据从外部源传输到内存缓冲区,这样的流叫作“读取流”。
在网络上传输数据时,使用的是网络流(NetworkStream)。网络流的意思是数据在网络的各个位置之间是以连续的形式传输的。为了处理这种流,C#在System.Net.Sockets命名空间中提供了一个专门的NetworkStream类,用于通过网络套接字发送和接收数据。
构造NetworkStream对象的常用形式为:
Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
NetWorkStream networkStream=new NetworkStream(socket);
网络数据传输完成后,不要忘记用Close方法关闭NetworkStream对象。

三、TCP应用编程
利用TCP协议开发应用程序时,.NET框架提供有两种工作方式,一种是同步工作方式,另一种是异步工作方式。
与同步工作方式和异步工作方式相对应,利用Socket类进行编程时系统也都提供有相应的方法,采用相应的方法进行编程分别称为同步套接字编程和异步套接字编程。为了简化套接字编程,.NET框架又专门提供了两个类:TcpClient类与TcpListener类。由于这两个类与套接字一样也分别有各自的同步和异步工作方式及其对应的方法,而我们在编程时,三个类都有可能使用,因此为简化起见,无论使用的是哪个类,我们统统从工作方式上将其称为同步TCP和异步TCP,所以其编程方式也有两种,一种是同步TCP编程,另一种是异步TCP编程。

在同步TCP应用编程中,发送、接收和监听语句均采用阻塞方式工作。使用同步TCP编写服务器端程序的一般步骤为:
1) 创建一个包含采用的网络类型、数据传输类型和协议类型的本地套接字对象,并将其与服务器的IP地址和端口号绑定。这个过程可以通过Socket类或者TcpListener类完成。
2) 在指定的端口进行监听,以便接受客户端连接请求。
3) 一旦接受了客户端的连接请求,就根据客户端发送的连接信息创建与该客户端对应的Socket对象或者TcpClient对象。4) 根据创建的Socket对象或者TcpClient对象,分别与每个连接的客户进行数据传输。
5) 根据传送信息情况确定是否关闭与对方的连接。
使用同步TCP编写客户端程序的一般步骤为:
1) 创建一个包含传输过程中采用的网络类型、数据传输类型和协议类型的Socket对象或者TcpClient对象。
2) 使用Connect方法与远程服务器建立连接。
3) 与服务器进行数据传输。
4) 完成工作后,向服务器发送关闭信息,并关闭与服务器的连接。

在System.Net.Sockets命名空间下,TcpClient类与TcpListener类是两个专门用于TCP协议编程的类。这两个类封装了底层的套接字,并分别提供了对Socket进行封装后的同步和异步操作的方法。

3.1、TcpClient类
TcpClient类归类在System.Net命名空间下。利用TcpClient类提供的方法,可以通过网络进行连接、发送和接收网络数据流。
四种构造函数形式:
TcpClient()
TcpClient(AddressFamily family)
TcpClient(IPEndPoint iep)
TcpClient(string hostname,int port)
创建该对象后,即可用Connect方法与服务器端进行连接。tcpClient.Connect("www.rrgod.com", 5188);
3.2、TcpListener类
TcpListener类用于监听和接收传入的连接请求。该类的构造函数有:
TcpListener(IPEndPoint iep)
TcpListener(IPAddress localAddr, int port)
构造了TcpListener对象后,就可以监听客户端的连接请求了。在同步工作方式下,对应有AcceptTcpClient方法、AcceptSocket方法、Start方法和Stop方法。
Start方法用于启动监听,构造函数为: public void Start(int backlog)
Stop方法用于停止监听请求,构造函数为: public void Stop()
AcceptTcpClient方法用于在同步阻塞方式下获取并返回一个可以用来接收和发送数据的封装了Socket的TcpClient对象。
AcceptSocket方法用于在同步阻塞方式下获取并返回一个用来接收和发送数据的套接字对象。

实际应用中,解决TCP协议消息边界问题的方法有三种:
第一种方法是发送固定长度的消息。。BinaryReader和BinaryWriter对象提供了多种重载方法,发送和接收具有固定长度类型的数据非常方便。
第二种方法是将消息长度与消息一起发送。
第三种方法是使用特殊标记分隔消息。实现这种方法最方便的途径是通过StreamWriter对象和StreamReader对象。

已经有(1)位网友发表了评论,你也评一评吧!
原创文章如转载,请注明:转载自Eddy Blog
原文地址:http://www.rrgod.com/program/839.html     欢迎订阅Eddy Blog

  1. 发表于2013-12-13 16:17:25

    不错 ,对C#的网络编程思想描述的非常到位!

记住我的信息,下次不用再输入 欢迎给Eddy Blog留言