生龙活虎1986

导航

(转)DotNet中的Socket编程(C#)

 

(转)DotNet中的Socket编程(C#)

Socket基础介绍

Socket是应用层与TCP/IP协议通信的中间软件抽象层,它是一组接口,它把复杂的TCP/IP协议隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。微软的WinSock是在Socket基础上的封装,使其更加适合Windows平台,当然WinSock是完全兼容Socket的。以前听到Socket编程,觉得它是比较高深的编程知识,但是只要弄清Socket编程的工作原理,神秘的面纱也就揭开了。

其实,Socket可以象Stream一样被视为一个数据通道,这个通道架设在应用程序端(客户端)和远程服务器端之间,而后,数据的读取(接收)和写入(发送)均针对这个通道来进行。可见,在应用程序端或者服务器端创建了Socket对象之后,就可以使用Send/SentTo方法将数据发送到连接的Socket,或者使用Receive/ReceiveFrom方法接收来自连接Socket的数据。
.Net Framework中的网络编程结构

  抛开ISO/OSI模型的7层构架,单从TCP/IP模型上的逻辑层面上看,.Net类可以视为包含3个层次:请求/响应层、应用协议层、传输层。 WebReqeust和WebResponse 代表了请求/响应层,支持Http、Tcp和Udp的类组成了应用协议层。而Socket类处于传输层,位于这个结构的最底层,当其上面的应用协议层和请求/响应层不能满足应用程序的特殊需要时,就需要使用这一层进行Socket套接字编程。

  而在.Net中,System.Net.Sockets命名空间为需要严密控制网络访问的开发人员提供了Windows Sockets(Winsock)接口的托管实现。System.Net命名空间中的所有其他网络访问类都建立在该套接字Socket实现之上,如TCPClient、TCPListener 和UDPClient类封装有关创建到Internet的TCP和UDP连接的详细信息;NetworkStream类则提供用于网络访问的基础数据流等,常见的许多Internet服务都可以见到Socket的踪影,如Telnet、Http、Email等,这些服务尽管通讯协议Protocol的定义不同,但是其基础的传输都是采用的Socket。
.Net中Socket编程的必备基础

  .NET框架的Socket类是Winsock32 API提供的套接字服务的托管代码版本。其中为实现网络编程提供了大量的方法。如果熟悉Winsock API函数,那么用Socket类编写网络程序会非常容易,当然,如果不曾接触过,也不会太困难。主要为Socket类的几个方法:
1、创建一个Socket实例
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
2、建立远程连接
Socket.Connect(EndPoint remoteEP)(有重载方法)
3、从数据中的指示位置开始将数据发送到连接的 Socket
Socket.Send(byte[], int, SocketFlags); (有重载方法)
4、将数据发送到特定终结点
Socket.SendTo(byte[], EndPoint); (有重载方法)
5、将数据从连接的 Socket 接收到接收缓冲区的特定位置
Socket.Receive(byte[],int,SocketFlags);
6、接收数据缓冲区中特定位置的数据并存储终结点
Socket.ReceiveFrom(byte[], int, SocketFlags, ref EndPoint);
7、使 Socket 与一个本地终结点相关联
Socket.Bind( EndPoint localEP );
8、将 Socket 置于侦听状态
Socket.Listen( int backlog );
9、创建新的 Socket 以处理传入的连接请求
Socket.Accept();
10、禁用某 Socket 上的发送和接收
Socket.Shutdown( SocketShutdown how );
11、强制 Socket 连接关闭
Socket.Close();
可以看出,以上许多方法包含EndPoint类型的参数,在Internet中,TCP/IP使用一个网络地址和一个服务端口号来唯一标识设备。网络地址标识网络上的特定设备;端口号标识要连接到的该设备上的特定服务。网络地址和服务端口的组合称为终结点,在.NET 框架中正是由EndPoint类表示这个终结点,它提供表示网络资源或服务的抽象,用以标志网络地址等信息。.Net同时也为每个受支持的地址族定义了 EndPoint 的子代;对于 IP 地址族,该类为IPEndPoint。IPEndPoint类包含应用程序连接到主机上的服务所需的主机和端口信息,通过组合服务的主机IP地址和端口号,IPEndPoint类形成到服务的连接点。
  用到IPEndPoint类的时候就不可避免地涉及到计算机IP地址,.Net中有两种类可以得到IP地址实例:
  一是通过IPAddress类:IPAddress 类包含计算机在 IP 网络上的地址。其Parse方法可将 IP 地址字符串转换为 IPAddress 实例。下面的语句创建一个 IPAddress 实例:IPAddress myIP = IPAddress.Parse("192.168.1.2");
  二是通过Dns 类:向使用 TCP/IP Internet 服务的应用程序提供域名服务。其Resolve方法查询DNS服务器以将用户友好的域名(如"host.contoso.com")映射到数字形式的 Internet 地址(如 192.168.1.1)。Resolve方法 返回一个 IPHostEnty实例,该实例包含所请求名称的地址和别名的列表。大多数情况下,可以使用AddressList数组中返回的第一个地址。下面的代码获取一个IPAddress实例,该实例包含服务器host.contoso.com的 IP 地址。
IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];
也可以使用GetHostName方法得到IPHostEntry实例:
IPHosntEntry hostInfo=Dns.GetHostByName("host.contoso.com")
Socket类工作原理

  首先必须创建一个Socket,一旦创建Socket,在客户端,将可以通过Connect方法连接到指定的服务器,并通过Send/SendTo方法向远程服务器发送数据,而后可以通过Receive/ReceiveFrom从服务端接收数据;而在服务器端,你需要使用Bind方法绑定所指定的接口使Socket与一个本地终结点相联,并通过Listen方法侦听该接口上的请求,当侦听到用户端的连接时,调用Accept完成连接的操作,创建新的Socket以处理传入的连接请求。使用完Socket后,记住使用Shutdown方法禁用Socket,并使用Close方法关闭Socket。
需要知道的是:Socket类支持两种基本模式:同步和异步。其区别在于:在同步模式中,对执行网络操作的函数(如Send和Receive)的调用一直等到操作完成后才将控制返回给调用程序。在异步模式中,这些调用立即返回。

  另外,很多时候Socket编程视情况不同需要在客户端和服务器端分别予以实现,在客户端编制应用程序向服务端指定端口发送请求,同时编制服务端应用程序处理该请求,这个过程在上面的阐述中已经提及;当然,并非所有的Socket编程都需要你严格编写这两端程序;视应用情况不同,你可以在客户端构造出请求字符串,服务器相应端口捕获这个请求,交由其公用服务程序进行处理。以下事例语句中的字符串就向远程主机提出页面请求:
string Get = "GET / HTTP/1.1rnHost: " + server + "rnConnection: Closernrn";

  远程主机指定端口接受到这一请求后,就可利用其公用服务程序进行处理而不需要另行编制服务器端应用程序。
用C#描述的Socket编程实例

  下面的程序段完整地实现了Web页面下载功能。用户只需在窗体上输入远程主机名(Dns主机名或以点分隔的四部分表示法格式的 IP 地址)和预保存的本地文件名,并利用专门提供Http服务的80端口,就可以获取远程主机页面并保存在本地机指定文件中。如果保存格式是.htm格式,你就可以在Internet浏览器中打开该页面。适当添加代码,甚至可以实现一个简单的浏览器程序。源代码如下:

public void TestSocket(){
//取得预保存的文件名
string fileName="C:\abc.tmp"; //TODO:
//远程主机
string hostName="www.sina.com.cn"; // TODO:
//端口
int port= 11000;
//得到主机信息
IPHostEntry ipInfo=Dns.GetHostByName(hostName);
//取得IPAddress[]
IPAddress[] ipAddr=ipInfo.AddressList;
//得到ip
IPAddress ip=ipAddr[0];
//组合出远程终结点
IPEndPoint hostEP=new IPEndPoint(ip,port);
//创建Socket 实例
Socket socket=new Socket(AddressFamily.InterNetwork,
SocketType.Stream,ProtocolType.Tcp);
//尝试连接
try {
socket.Connect(hostEP);
}
catch(Exception ex) {
MessageBox.Show("连接错误" + ex.Message);
}
//发送给远程主机的请求内容串
string sendStr = "GET / HTTP/1.1rnHost: " + hostName +
"rnConnection: Closernrn";
//创建bytes字节数组以转换发送串
byte[] bytesSendStr=new byte[1024];
//将发送内容字符串转换成字节byte数组
bytesSendStr = Encoding.ASCII.GetBytes(sendStr);
//向主机发送请求
try {
socket.Send(bytesSendStr,bytesSendStr.Length,0);
}
catch(Exception ex) {
MessageBox.Show("发送错误:" + ex.Message);
}
//声明接收返回内容的字符串
string recvStr="";
//声明字节数组,一次接收数据的长度为1024字节
byte[] recvBytes=new byte[1024];
//返回实际接收内容的字节数
int bytes=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 {
//创建文件流对象实例
FileStream fs=new FileStream(fileName,
FileMode.OpenOrCreate, FileAccess.ReadWrite);
//写入文件
fs.Write(content,0,content.Length);
}
catch(Exception ex) {
MessageBox.Show("文件创建/写入错误:" + ex.Message);
}
//禁用Socket
socket.Shutdown(SocketShutdown.Both);
//关闭Socket
socket.Close();
}



posted on 2009-12-20 13:08  生龙活虎1986  阅读(685)  评论(2编辑  收藏  举报