c# socket TCPClientTCPListener 用法详解.docx
《c# socket TCPClientTCPListener 用法详解.docx》由会员分享,可在线阅读,更多相关《c# socket TCPClientTCPListener 用法详解.docx(16页珍藏版)》请在冰豆网上搜索。
c#socketTCPClientTCPListener用法详解
c#socket、TcpClient、TCPListener用法详解
VisualC#.Net网络程序开发-Socket篇
Microsoft.NetFramework为应用程序访问Internet提供了分层的、可扩展的以及受管辖的网络服务,其名字空间System.Net和System.Net.Sockets包含丰富的类可以开发多种网络应用程序。
.Net类采用的分层结构允许应用程序在不同的控制级别上访问网络,开发人员可以根据需要选择针对不同的级别编制程序,这些级别几乎囊括了Internet的所有需要--从socket套接字到普通的请求/响应,更重要的是,这种分层是可以扩展的,能够适应Internet不断扩展的需要。
抛开ISO/OSI模型的7层构架,单从TCP/IP模型上的逻辑层面上看,.Net类可以视为包含3个层次:
请求/响应层、应用协议层、传输层。
WebReqeust和WebResponse代表了请求/响应层,支持Http、Tcp和Udp的类组成了应用协议层,而Socket类处于传输层。
传输层位于这个结构的最底层,当其上面的应用协议层和请求/响应层不能满足应用程序的特殊需要时,就需要使用这一层进行Socket套接字编程。
而在.Net中,System.Net.Sockets命名空间为需要严密控制网络访问的开发人员提供了WindowsSockets(Winsock)接口的托管实现。
System.Net命名空间中的所有其他网络访问类都建立在该套接字Socket实现之上,如TcpClient、TCPListener和UDPClient类封装有关创建到Internet的TCP和UDP连接的详细信息;NetworkStream类则提供用于网络访问的基础数据流等,常见的许多Internet服务都可以见到Socket的踪影,如Telnet、Http、Email、Echo等,这些服务尽管通讯协议Protocol的定义不同,但是其基础的传输都是采用的Socket。
其实,Socket可以象流Stream一样被视为一个数据通道,这个通道架设在应用程序端(客户端)和远程服务器端之间,而后,数据的读取(接收)和写入(发送)均针对这个通道来进行。
可见,在应用程序端或者服务器端创建了Socket对象之后,就可以使用Send/SentTo方法将数据发送到连接的Socket,或者使用Receive/ReceiveFrom方法接收来自连接Socket的数据;
针对Socket编程,.NET框架的Socket类是Winsock32API提供的套接字服务的托管代码版本。
其中为实现网络编程提供了大量的方法,大多数情况下,Socket类方法只是将数据封送到它们的本机Win32副本中并处理任何必要的安全检查。
如果你熟悉
WinsockAPI函数,那么用Socket类编写网络程序会非常容易,当然,如果你不曾接触过,也不会太困难,跟随下面的解说,你会发觉使用Socket类开发windows网络应用程序原来有规可寻,它们在大多数情况下遵循大致相同的步骤。
在使用之前,你需要首先创建Socket对象的实例,这可以通过Socket类的构造方法来实现:
publicSocket(AddressFamilyaddressFamily,SocketTypesocketType,ProtocolTypeprotocolType);
其中,addressFamily参数指定Socket使用的寻址方案,socketType参数指定Socket的类型,protocolType参数指定Socket使用的协议。
下面的示例语句创建一个Socket,它可用于在基于TCP/IP的网络(如Internet)上通讯。
Sockets=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
若要使用UDP而不是TCP,需要更改协议类型,如下面的示例所示:
Sockets=newSocket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);
一旦创建Socket,在客户端,你将可以通过Connect方法连接到指定的服务器,并通过Send/SendTo方法向远程服务器发送数据,而后可以通过Receive/ReceiveFrom从服务端接收数据;而在服务器端,你需要使用Bind方法绑定所指定的接口使Socket与一个本地终结点相联,并通过Listen方法侦听该接口上的请求,当侦听到用户端的连接时,调用Accept完成连接的操作,创建新的Socket以处理传入的连接请求。
使用完Socket后,记住使用Shutdown方法禁用Socket,并使用Close方法关闭Socket。
其间用到的方法/函数有:
Socket.Connect方法:
建立到远程设备的连接
publicvoidConnect(EndPointremoteEP)(有重载方法)Socket.Send方法:
从数据中的指示位置开始将数据发送到连接的Socket。
publicintSend(byte[],int,SocketFlags);(有重载方法)
Socket.SendTo方法将数据发送到特定终结点。
publicintSendTo(byte[],EndPoint);(有重载方法)
Socket.Receive方法:
将数据从连接的Socket接收到接收缓冲区的特定位置。
publicintReceive(byte[],int,SocketFlags);
Socket.ReceiveFrom方法:
接收数据缓冲区中特定位置的数据并存储终结点。
publicintReceiveFrom(byte[],int,SocketFlags,refEndPoint);
Socket.Bind方法:
使Socket与一个本地终结点相关联:
publicvoidBind(EndPointlocalEP);
Socket.Listen方法:
将Socket置于侦听状态。
publicvoidListen(intbacklog);
Socket.Accept方法:
创建新的Socket以处理传入的连接请求。
publicSocketAccept();
Socket.Shutdown方法:
禁用某Socket上的发送和接收
publicvoidShutdown(SocketShutdownhow);
Socket.Close方法:
强制Socket连接关闭
publicvoidClose();
可以看出,以上许多方法包含EndPoint类型的参数,在Internet中,TCP/IP使用一个网络地址和一个服务端口号来唯一标识设备。
网络地址标识网络上的特定设备;端口号标识要连接到的该设备上的特定服务。
网络地址和服务端口的组合称为终结点,在.NET框架中正是由EndPoint类表示这个终结点,它提供表示网络资源或服务的抽象,用以标志网络地址等信息。
.Net同时也为每个受支持的地址族定义了EndPoint的子代;对于IP地址族,该类为IPEndPoint。
IPEndPoint类包含应用程序连接到主机上的服务所需的主机和端口信息,通过组合服务的主机IP地址和端口号,IPEndPoint类形成到服务的连接点。
用到IPEndPoint类的时候就不可避免地涉及到计算机IP地址,.Net中有两种类可以得到IP地址实例:
IPAddress类:
IPAddress类包含计算机在IP网络上的地址。
其Parse方法可将IP地址字符串转换为IPAddress实例。
下面的语句创建一个IPAddress实例:
IPAddressmyIP=IPAddress.Parse("192.168.1.2");
Dns类:
向使用TCP/IPInternet服务的应用程序提供域名服
务。
其Resolve方法查询DNS服务器以将用户友好的域名(如"")映射到数字形式的Internet地址(如192.168.1.1)。
Resolve方法返回一个IPHostEnty实例,该实例包含所请求名称的地址和别名的列表。
大多数情况下,可以使用AddressList数组中返回的第一个地址。
下面的代码获取一个IPAddress实例,该实例包含服务器的IP地址。
IPHostEntryipHostInfo=Dns.Resolve("");
IPAddressipAddress=ipHostInfo.AddressList[0];
你也可以使用GetHostName方法得到IPHostEntry实例:
IPHosntEntry
hostInfo=Dns.GetHostByName("")在使用以上方法时,你将可能需要处理以下几种异常:
SocketException异常:
访问Socket时操作系统发生错误引发
ArgumentNullException异常:
参数为空引用引发
ObjectDisposedException异常:
Socket已经关闭引发
在掌握上面得知识后,下面的代码将该服务器主机(的IP地址与端口号组合,以便为连接创建远程终结点:
IPEndPointipe=newIPEndPoint(ipAddress,11000);
确定了远程设备的地址并选择了用于连接的端口后,应用程序可以尝试建立与远程设备的连接。
下面的示例使用现有的IPEndPoint实例与远程设备连接,并捕获可能引发的异常:
try{
s.Connect(ipe);//尝试连接
}
//处理参数为空引用异常
catch(ArgumentNullExceptionae){
Console.WriteLine("ArgumentNullException:
{0}",
ae.ToString());
}
//处理操作系统异常
catch(SocketExceptionse){
Console.WriteLine("SocketException:
{0}",se.ToString());
}
catch(Exceptione){
Console.WriteLine("Unexpectedexception:
{0}",e.ToString());
}
需要知道的是:
Socket类支持两种基本模式:
同步和异步。
其区别在于:
在同步模式中,对执行网络操作的函数(如Send和Receive)的调用一直等到操作完成后才将控制返回给调用程序。
在异步模式中,这些调用立即返回。
另外,很多时候,Socket编程视情况不同需要在客户端和服务器端分别予以实现,在客户端编制应用程序向服务端指定端口发送请求,同时编制服务端应用程序处理该请求,这个过程在上面的阐述中已经提及;当然,并非所有的Socket编程都需要你严格编写这两端
程序;视应用情况不同,你可以在客户端构造出请求字符串,服务器相应端口捕获这个请求,交由其公用服务程序进行处理。
以下事例语句中的字符串就向远程主机提出页面请求:
stringGet="GET/HTTP/1.1\r\nHost:
"+server+"\r\nConnection:
Close\r\n\r\n";
远程主机指定端口接受到这一请求后,就可利用其公用服务程序进行处理而不需要另行编制服务器端应用程序。
综合运用以上阐述的使用VisualC#进行Socket网络程序开发的知识,下面的程序段完整地实现了Web页面下载功能。
用户只需在窗体上输入远程主机名(Dns主机名或以点分隔的四部分表示法格式的IP地址)和预保存的本地文件名,并利用专门提供Http服务的80端口,就可以获取远程主机页面并保存在本地机指定文件中。
如果保存格式是.htm格式,你就可以在Internet浏览器中打开该页面。
适当添加代码,你甚至可以实现一个简单的浏览器程序。
实现此功能的主要源代码如下:
//"开始"按钮事件
privatevoidbutton1_Click(objectsender,System.EventArgse){
//取得预保存的文件名
stringfileName=textBox3.Text.Trim();
//远程主机
stringhostName=textBox1.Text.Trim();
//端口
intport=Int32.Parse(textBox2.Text.Trim());
//得到主机信息
IPHostEntryipInfo=Dns.GetHostByName(hostName);
//取得IPAddress[]
IPAddress[]ipAddr=ipInfo.AddressList;
//得到ip
IPAddressip=ipAddr[0];
//组合出远程终结点
IPEndPointhostEP=newIPEndPoint(ip,port);
//创建Socket实例
Socketsocket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
try
//尝试连接
socket.Connect(hostEP);
}
catch(Exceptionse)
{
MessageBox.Show("连接错误"+se.Message,"提示信息
MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//发送给远程主机的请求内容串
stringsendStr="GET/HTTP/1.1\r\nHost:
"+hostName+
"\r\nConnection:
Close\r\n\r\n";
//创建bytes字节数组以转换发送串
byte[]bytesSendStr=newbyte[1024];
//将发送内容字符串转换成字节byte数组
bytesSendStr=Encoding.ASCII.GetBytes(sendStr);
try
{
//向主机发送请求
socket.Send(bytesSendStr,bytesSendStr.Length,0);
catch(Exceptionce)
{
MessageBox.Show("发送错误:
"+ce.Message,"提示信息,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//声明接收返回内容的字符串
stringrecvStr="";
//声明字节数组,一次接收数据的长度为1024字节
byte[]recvBytes=newbyte[1024];
//返回实际接收内容的字节数
intbytes=0;
//循环读取,直到接收完所有数据
while(true)
{
bytes=socket.Receive(recvBytes,recvBytes.Length,0);
//读取完成后退出循环
if(bytes〈=0)
break;
//将读取的字节数转换为字符串
recvStr+=Encoding.ASCII.GetString(recvBytes,0,bytes);
}
//将所读取的字符串转换为字节数组
byte[]content=Encoding.ASCII.GetBytes(recvStr);
try
{
//创建文件流对象实例
FileStreamfs=newFileStream(fileName,FileMode.OpenOrCreate,FileAccess.ReadWrite);
//写入文件
fs.Write(content,0,content.Length);
}
catch(Exceptionfe)
{
MessageBox.Show("文件创建/写入错误:
"+fe.Message,"提示信息",MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);
}
//禁用Socket
socket.Shutdown(SocketShutdown.Both);
//关闭Socket
socket.Close();
}
}
程序在WindowsXP中文版、.NetFrameworkd中文正式版、VisualStudio.Net中文正式版下调试通过
VisualC#.Net网络程序开发-Tcp篇
前一篇《VisualC#.Net网络程序开发-Socket篇》中说到:
支持Http、Tcp和Udp的类组成了TCP/IP三层模型(请求响应层、应用协议层、传输层)的中间层-应用协议层,该层的类比位于最底层的Socket类提供了更高层次的抽象,它们封装TCP和UDP套接字的创建,不需要处理连接的细节,这使得我们在编写套接字级别的协议时,可以更多地尝试使用TcpClient、UDPClient和TcpListener,而不是直接向Socket中写。
它们之间的这种层次关系示意如下:
可见,TcpClient类基于Socket类构建,这是它能够以更高的抽象程度提供TCP服务的基础。
正因为这样,许多应用层上的通
讯协议,比如FTP(FileTransfersProtocol)文件传输协议、HTTP(HypertextTransfersProtocol)超文本传输协议等都直接创建在TcpClient等类之上。
TcpClient类使用TCP从Internet资源请求数据。
TCP协议建立与远程终结点的连接,然后使用此连接发送和接收数据包。
TCP负责确保将数据包发送到终结点并在数据包到达时以正确的顺序对其进行组合。
从名字上就可以看出,TcpClient类专为客户端设计,它为TCP网络服务提供客户端连接。
TcpClient提供了通过网络连接、发送和接收数据的简单方法。
若要建立TCP连接,必须知道承载所需服务的网络设备的地址(IPAddress)以及该服务用于通讯的TCP端口(Port)。
Internet分配号码机构(InternetAssignedNumbersAuthority,IANA)定义公共服务的端口号(你可以访问获得这方面更详细的资料)。
IANA列表中所没有的服务可使用1,024到65,535这一范围中的端口号。
要创建这种连接,你可以选用TcpClient类的三种构造函数之一:
1、publicTcpClient()当使用这种不带任何参数的构造函数时,将使用本机默认的ip地址并将使用默认的通信端口号0。
这样情况下,如果本机不止一个ip地址,将无法选择使用。
以下语句示例了如何使用默认构造函数来创建新的TcpClient:
TcpClientTcpClientC=newTcpClient();
2、publicTcpClient(IPEndPoint)使用本机IPEndPoint创建TcpClient的实例对象。
上一篇介绍过了,IPEndPoint将网络端点表示为IP地址和端口号,在这里它用于指定在建立远程主机连接时所使用的本地网络接口(IP地址)和端口号,这个构造方法为使用本机IPAddress和Port提供了选择余地。
下面的语句示例了如何使用本地终结点创建TcpClient类的实例:
IPHostEntry
ipInfo=Dns.GetHostByName("");//主机信息IPAddressList[]ipList=ipInfo.AddressList;//IP地址数组IPAddressip=ipList[0];//多IP地址时一般用第一个
IPEndPointipEP=newIPEndPoint(ip,4088);//得到网络终结点
try{
TcpClientTcpClientA=newTcpClient(ipLocalEndPoint);
}
catch(Exceptione){
Console.WriteLine(e.ToString());
}
到这里,你可能会感到困惑,客户端要和服务端创建连接,所指定的IP地址及通信端口号应该是远程服务器的呀!
事实上的确如此,使用以上两种构造函数,你所实现的只是TcpClient实例对象与IP地址和Port端口的绑定,要完成连接,你还需要显式指定与远程主机的连接,这可以通过TcpClient类的Connect方法来实现,Connet方法使用指定的主机名和端口号将客户端连接到远程主机:
1)、publicvoidConnect(IPEndPoint);使用指定的远程网络终结点将客户端连接到远程TCP主机。
publicvoidConnect(IPAddress,int);使用指定的IP地址和端口号将客户端连接到TCP主机。
publicvoidConnect(string,int);将客户端连接到指定主机
上的指定端口。
需要指出的是,Connect方法的所有重载形式中的参数IPEndPoint网络终
结点、IPAddress以及表现为string的Dns主机名和int指出的Port端口均指的是远程服务器。
以下示例语句使用主机默认IP和Port端口号0与远程主机建立连接:
TcpClientTcpClient=newTcpClient();//创建TcpClient对象实例
try{
tcpClient.Connect("",11002);//建立连接
}
catch(Exceptione){
Console.WriteLine(e.ToString());
}
3、publicTcpClient(string,int);初始化TcpClient类的新实例并连接到指定主机上的指定端口。
与前两个构造函数不一样,这个构造函数将自动建立连接,你不再需要额外调用Connect方法,其中string类型的参数表示远程主机的Dns名,如:
。
以下示例语句调用这一方法实现与指定主机名和端口号的主机相连:
try{
TcpClientTcpClientB=newTcpClient("",4088);
}
catch(Exceptione){