c#网络编程学习笔记02_Tcp编程(上)

/*关于多线程的一些知识和概念,会在从此之后的一些联系中讲解*/

/*这一节主要是概念性和相关方法的介绍,在下一篇会使用例子来演示tcp的同步和异步编程*/

一.TCP简介

TCP是一种面向连接的, 可靠的, 给予字节流的传输层通信协议,位于ip层之上,应用层之下。由于应用层之间经常需要可靠的,像管道一样的连接,但是ip层并不提供这样的流机制,故需要由TCP完成传输管道的任务。

1. TCP的工作过程

1.1 连接的建立

用TCP编写的程序,必须先建立TCP连接。这个建立过程称为“三次握手”:

第一次: 建立连接时,客户端发送SYN(不用管他是什么东西)包到服务器,客户端进入SYN_SEND状态,等待确认。

第二次: 服务器收到SYN包,必须确认客户的SYN,同时自己也发送一个SYN, 即SYN+ACK包,同时服务器进入SYN_RECV状态。

第三次: 客户端收到SYN+ ACK包,同时向服务器发送ACK,此包发送完毕,客户端和服务器进入Established状态,完成三次握手。

1.2 传输数据

TCP协议是字节流发送,将字节流按照一定的格式和长度组成多个数据报发送,然后接收方,收到数据报后,按照分解顺序重新组装,恢复数据。

1.3 连接的终止

TCP连接需要三次握手,但是想终止连接,则需要四次握手,这是由于TCP的版关闭造成的。

2. Tcp的主要特点

1.tcp是面向协议的连接。

2. 端到端的通信,只能一对一,不能一对多。

3. 高可靠性,保证数据无差错,不丢失,不重复,能准确到达接收方,并且到达顺序和发出顺序相同。

4.全双工方式传输。

5. 数据以字节流方式传输。

6. 传输的数据没有消息边界(不懂)。

二.同步和异步

不想过多解释。.NET封装好的类,TcpClient类和TcpListener类都有同步与异步相对应的方法。

三. C#中的TCP编程类

TcpLiistener类用于监听客户端的连接请求, TcpClient类用于提供本地主机和远程主机的连接信息。

1.1 TcpListener类

该类用于监听和接受传入的连接请求。该类的构造函数常用的有以下两种重在形式:

1)TcpListener(IPEndPoint iep)

2) TcpListener(IPAddress localAddr, int port)

第一种构造是通过IPEndPoint类型的对象,在指定的Ip地址与端口监听客户端的链接请求,iep包含了本机的ip地址和端口号。

第二种构造是直接指定本机的ip和端口,并通过指定的本机ip和端口号监听传入的连接请求。也可以将本机ip设置为IPAdrress.Any,将本机的端口指定为0,这种形式表示ip地址和端口号都有系统自动分配。


在创建了TcpListener对象后,就可以监听客户端的连接请求了。其工作方式,分为同步和异步。

同步:

1)Start方法。 方法原型为:

public void Start()

public void Start(int backlog)

整形backlog代表了请求队列的最大长度,即最多允许的客户端连接的个数。在调用Start方法后,系统自动将LocalEndPoint和底层套接字绑定,并自动监听来自客户端的请求, 如果接受了一个客户端的请求,则把该请求插入队列,然后继续监听下一个请求,知道调用Stop方法为止。如果超出最大长度,泡醋SocketExceptioni类型异常。

2) Stop方法。方法原型为:

public void Stop()

执行stop方法后,立即停止监听客户端的连接请求,此时队列中的所有为接受的请求全部丢失,导致等待链接的客户端引发SocketException异常,从而使得服务器的AcceptTcpClient方法也会产生异常。但是要注意的是,该方法不会关闭已经接受的连接请求。

Stop方法还会关闭基础Socket, 并未TcpListener创建新的Socket,如果在调用stop之前已经在socket上设置属性,这些属性不会传递到新的socket中。

3) AcceptSocket方法。

用于在 同步阻塞方式下获得并返回一个用来接收和发送数据的Socket对象,同时从传入的连接队列中删除该客户端的连接请求,该套接字包含了本地和远程主机的ip地址和端口号,得到该对象后,就可以通过调用Socke对象的Send和Reseive方法和远程主机通信。

4) AcceptTcpClient方法

该方法用于在同步阻塞方式下获取并且返回一个封装了Socket的TcpClient对象,同时传入的连接队列中删除改客户端的连接请求。得到该对象后,就可以通过该对象的GetStream方法生成NetworkStream对象,通过NetworkStream对象与客户端通信。

如果应用程序仅需要IO,调用AccepteTcpClient方法即可,但是如果进行更加细化的行为控制,需要AcceptSocket方法。

注意: 当程序之执行AcceptTcpClient方法的时候,线程会处于阻塞状态,知道接受客户端向服务器发送的连接请求后,才会继续执行吓一跳语句。

TcpListener类中的常用方法:

AcceptSocket 从端口处接受一个链接并赋予他socket对象

AcceptTcpClient 葱段扣除接受一个连接并赋予他TcpClient对象

Equals 判断两个TcpListener对象是否相等

GetHashCode  特定类型的哈希函数

GetType 得到当前实例类型

Pending 确定是否有挂起的请求

Start 开始侦听传入请求

Stop 关闭侦听器

ToString 呵呵

1.2 TcpClient 类

Tcplient类也位于 System.Net.Socket命名空间下, 该类主要用于客户端编程,而服务器程序是通过TcpListener对象的AcceptTcpClient方法得到的TcpClient对象的。

TcpClient 类的构造函数有以下四种:

1) TcpClient()

自动分配本机(客户端)的ip和端口号,利用此构造函数创建对象后,还必须调用Connet方法与服务器连接,比如:


<span style="white-space:pre">		</span>TcpClient tcpClient = new TcpClient();
		tcpClient.Connet("www.abcd.com", 51888);

2) TcpClient(AddressFamily family)

这个构造方法也能自动分配本机(客户端)的ip和端口号,但是使用AddressFamily枚举指定使用那种网络协议。创建该对象,同样要调用Connet方法与服务器连接。例如:

<span style="white-space:pre">		</span>TcpClient tcpClient =new TcpClient(AddressFamily.InterNetwork);
		tcpClient.Connet("www.abcd.com", 51888);

3) TcpClient(IPEndPoint iep)

改构造函数的参数iep指定了本机(客户端)Ip地址和端口号,当客户端有一个以上的ip地址是,而且程序希望直接指定ip地址和端口号,可以使用这种方式,如果使用这种方式,必须使用Connet方法与服务器对接。例如:

<span style="white-space:pre">		</span>IPAddress[] address = Dns.GetHostAddress(Dns.GetHostName());
		IPEndPoint iep = new IPEndPoint(Address[0],51888);
		TcpClient tcpClient = new TcpClient(iep);
		tcpClient.Connet("www.abcd.com", 51888);
4)TcpClient(string hostname, int port)

这是最方便的一种构造, hostname表示要连接的远程主机的DNS名,port表示要连接的远程主机端口号。该构造会自动分配最适合的本地主机ip和端口号,并对dns进行解析,然后与远程服务器进行连接,例如:

<span style="white-space:pre">		</span>TcpClient tcpClient =new TcpClient("www.abcd.com", 51888);

他相当于:

<span style="white-space:pre">		</span>TcpClient tcpClient = new TcpClient();
		tcpClient.Connet("www.abcd.com", 51888);

一旦创建了TcpClient对象,就可以使用GetStream方法得到NetworkStream对象,然后再利用NetworkStream,向远程主机发送数据,或接收数据。

值得注意的,使用NetworkStream仍然复杂,在之后,我们会用BinaryReader,BinaryWriter,StreamReader, StreamWriter方法进行io处理。

TcpClient类中的常用属性:

Client 获取或设置基础套接字

LingerState 获取或设置套接字保持连接的时间

NoDelay 获取或设置一个值, 该值在发送或接受缓冲区未满时禁用延迟

ReceiveBufferSize 获取或设置Tcp接受缓冲区的大小

ReceiveTimeout 获取或设置套接字接受数据的超时时间

SendBufferSize 获取或设置Tcp发送缓冲区的大小

SendTimeout  获取或设置套接字发送数据的超时时间


TcpClient类中的常用方法

Close 释放TcpClient实例,而不是关闭基础连接

Connet 用指定的主机名和端口号将客户端连接到Tcp主机

BeginConnet 开始一个对远程主机连接的异步请求

EndConnet 异步接收传入的连接尝试

GetStream 获取能够发送和接受数据的NetworkStream对象


四,Tcp基础编程的一般步骤

1. 编写服务器端程序的一般步骤

1)创建一个TcpListener的对象,然后调用Start方法开始监听。

典型代码如下:

	//声明
		private IPAddress localAddress;
		private const int port = 51888;
		private TcpListener tcpListener;

		//初始化
		IPAddress [] listenIP = Dns,GetHosyAddress("");
		localAddress = listenIp[0];
		//创建TcpListener对象,开始监听
		tcpListener = new TcpListener(localAddress, port);
		tcpListener.Start();

2) 再单独的线程中,首先循环调用AcceptTcpClient方法接受客户端的连接请求,从该方法的返回结果中得到与改客户端相对应的TcpClient对象,并利用GetStream方法得到NetworkStream对象,然后再利用该对像得到其他使用更方便的对象如:Binary Reader , BinaryWriter,为进一步通信做准备。典型代码:

//启动一个线程来接受请求
		Thread threadAccept = new Thread(AcceptClientConnet);
		threadAccept.Start();
		//线程执行AcceptClientConnet方法
		private void AcceptClientConnet()
		{
			while(true)
			{
				try{
					tcpClient = tcpListener.AcceptTcpClient();
					if(tcpClient != null)
					{
						statueStripInfo,Invoke(shwStatusInfoCallBack,"接受了一个连接“);
						networkStream = tcpClient.GetStream();
						br = new BinaryReader(networkStream);
						bw = new BinaryWriter(networkStream);
					}
					catch
					{
							...
					}
			}
		}
3)没得到一个新的TcpClient对象,就创建一个与该客户对应的线程,在线程中与对应的客户进行通信。

		read threadReceive  = new Thread (ReceiveMessage);
		threadReceive.Start();

其中ReceiveMessage是接受消息的方法。

4)根据传送信息的情况确定是否关闭和客户的连接

关键代码如下

		(br != null)
		{
			br.close();
		}
		if(bw != null)
		{
			bw.Close();
		}
		if(tcpClient != null)
		{
			tcpClient.Close();
		}
		toolStripStatusInfo,Text = "断开连接";
在连接关闭前,要停止流。

在停止服务后,服务器可以断开监听

	tcpListener.Stop();
2.编写客户端的一般步骤

1)利用TcpClient构造一个对象

		private TcpClient tcpClient;
		tcpClient = new TcpClient();


2) 使用Connet方法进行连接

		tcpClient.Connet(remoteHost.HostName, int,Parse(tbxPort.Text));


3) 利用tcpClient对象的GetStream得到NetworkStream,然后得到更为实用的流方法

		if(tcpClient != null)
		{
			networkStream = tcpClient.GetStream();
			br. = new BinaryReader(networkStream);
			bw = new BinaryWriter(networkStream);
		}


4) 创建一个线程监听指定的端口,循环接受并处理服务器发来的信息

		read threadReceive = new Thread(ReceiveMessage);
		threadReceive.Start();

这里的ReveiveMessage方法是线程执行的,用于循环接受信息,典型代码结构如下:

	private void ReceiveMessage()
		{
			while(true)
			{
				try
				{
					string rcvMsgStr = br.ReadString();//从流中读取信息
					if(rcvMsgStr != null)
					{
						lstbxMsgView,Invoke(shwMsgforViewCallBack, rcvNsgStr);
					}
				}
				catch
				{
					break;
				}

			}
	

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值