c#

位置:IT落伍者 >> c# >> 浏览文章

Visual C#.Net 网络程序开发Socket篇


发布日期:2023年02月15日
 
Visual C#.Net 网络程序开发Socket篇
MicrosoftNet Framework为应用程序访问Internet提供分层的可扩展的以及受管辖的网络服务其名字空间SystemNet和SystemNetSockets包含丰富的类可以开发多种网络应用程序

Net类采用的分层结构允许应用程序在不同的控制级别上访问网络开发人员可以根据需要选择针对不同的级别编制程序这些级别几乎囊括了Internet的所有需要从socket套接字到普通的请求/响应更重要的是这种分层是可以扩展的能够适应Internet不断扩展的需要

抛开ISO/OSI模型的层构架单从TCP/IP模型上的逻辑层面上看Net类可以视为包含个层次请求/响应层应用协议层传输层WebReqeust和WebResponse 代表了请求/响应层支持HttpTcp和Udp的类组成了应用协议层而Socket类处于传输层

可见传输层位于这个结构的最底层当其上面的应用协议层和请求/响应层不能满足应用程序的特殊需要时就需要使用这一层进行Socket套接字编程

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

其实Socket可以象流Stream一样被视为一个数据通道这个通道架设在应用程序端(客户端)和远程服务器端之间而后数据的读取(接收)和写入(发送)均针对这个通道来进行

可见在应用程序端或者服务器端创建了Socket对象之后就可以使用Send/SentTo方法将数据发送到连接的Socket或者使用Receive/ReceiveFrom方法接收来自连接Socket的数据

针对Socket编程NET 框架的 Socket 类是 Winsock API 提供的套接字服务的托管代码版本其中为实现网络编程提供了大量的方法大多数情况下Socket 类方法只是将数据封送到它们的本机Win 副本中并处理任何必要的安全检查如果你熟悉Winsock API函数那么用Socket类编写网络程序会非常容易当然如果你不曾接触过也不会太困难跟随下面的解说你会发觉使用Socket类开发windows 网络应用程序原来有规可寻它们在大多数情况下遵循大致相同的步骤

在使用之前你需要首先创建Socket对象的实例这可以通过Socket类的构造方法来实现

public Socket(AddressFamily addressFamilySocketType socketTypeProtocolType protocolType);

其中addressFamily 参数指定 Socket 使用的寻址方案socketType 参数指定 Socket 的类型protocolType 参数指定 Socket 使用的协议

下面的示例语句创建一个Socket它可用于在基于 TCP/IP 的网络(如 Internet)上通讯

Socket s = new Socket(AddressFamilyInterNetwork SocketTypeStream ProtocolTypeTcp);

若要使用 UDP 而不是 TCP需要更改协议类型如下面的示例所示

Socket s = new Socket(AddressFamilyInterNetwork SocketTypeDgram ProtocolTypeUdp);

一旦创建 Socket在客户端你将可以通过Connect方法连接到指定的服务器并通过Send/SendTo方法向远程服务器发送数据而后可以通过Receive/ReceiveFrom从服务端接收数据而在服务器端你需要使用Bind方法绑定所指定的接口使Socket与一个本地终结点相联并通过Listen方法侦听该接口上的请求当侦听到用户端的连接时调用Accept完成连接的操作创建新的Socket以处理传入的连接请求使用完Socket后记住使用 Shutdown 方法禁用Socket并使用 Close 方法关闭 Socket其间用到的方法/函数有

SocketConnect方法:建立到远程设备的连接

public void Connect(EndPoint remoteEP)(有重载方法)

SocketSend 方法:从数据中的指示位置开始将数据发送到连接的 Socket

public int Send(byte[] int SocketFlags);(有重载方法)

SocketSendTo 方法 将数据发送到特定终结点

public int SendTo(byte[] EndPoint);(有重载方法)

SocketReceive方法:将数据从连接的 Socket 接收到接收缓沖区的特定位置

public int Receive(byte[]intSocketFlags);

SocketReceiveFrom方法接收数据缓沖区中特定位置的数据并存储终结点

public int ReceiveFrom(byte[] int SocketFlags ref EndPoint);

SocketBind 方法使 Socket 与一个本地终结点相关联

public void Bind( EndPoint localEP );

SocketListen方法将 Socket 置于侦听状态

public void Listen( int backlog );

SocketAccept方法:创建新的 Socket 以处理传入的连接请求

public Socket Accept();

SocketShutdown方法:禁用某 Socket 上的发送和接收

public void Shutdown( SocketShutdown how );

SocketClose方法:强制 Socket 连接关闭

public void Close();

可以看出以上许多方法包含EndPoint类型的参在Internet中TCP/IP 使用一个网络地址和一个服务端口号来唯一标识设备网络地址标识网络上的特定设备端口号标识要连接到的该设备上的特定服务网络地址和服务端口的组合称为终结点

NET 框架中正是由 EndPoint 类表示这个终结点它提供表示网络资源或服务的抽象用以标志网络地址等信息Net同时也为每个受支持的地址族定义了 EndPoint 的子代对于 IP 地址族该类为 IPEndPointIPEndPoint 类包含应用程序连接到主机上的服务所需的主机和端口信息通过组合服务的主机IP地址和端口号IPEndPoint 类形成到服务的连接点

用到IPEndPoint类的时候就不可避免地涉及到计算机IP地址Net中有两种类可以得到IP地址实例

IPAddress类IPAddress 类包含计算机在 IP 网络上的地址其Parse方法可将 IP 地址字符串转换为 IPAddress 实例下面的语句创建一个 IPAddress 实例

IPAddress myIP = IPAddressParse();

Dns 类向使用 TCP/IP Internet 服务的应用程序提供域名服务其Resolve 方法查询 DNS 服务器以将用户友好的域名(如hostcontosocom)映射到数字形式的 Internet 地址(如 Resolve方法返回一个 IPHostEnty 实例该实例包含所请求名称的地址和别名的列表大多数情况下可以使用 AddressList 数组中返回的第一个地址下面的代码获取一个 IPAddress 实例该实例包含服务器 hostcontosocom 的 IP 地址

IPHostEntry ipHostInfo = DnsResolve(hostcontosocom);

IPAddress ipAddress = ipHostInfoAddressList[];

你也可以使用GetHostName方法得到IPHostEntry实例

IPHosntEntry hostInfo=DnsGetHostByName(hostcontosocom)

在使用以上方法时你将可能需要处理以下几种异常

SocketException异常访问Socket时操作系统发生错误引发

ArgumentNullException异常参数为空引用引发

ObjectDisposedException异常Socket已经关闭引发

在掌握上面得知识后下面的代码将该服务器主机( hostcontosocom的 IP 地址与端口号组合以便为连接创建远程终结点

IPEndPoint ipe = new IPEndPoint(ipAddress);

确定了远程设备的地址并选择了用于连接的端口后应用程序可以尝试建立与远程设备的连接下面的示例使用现有的 IPEndPoint 实例与远程设备连接并捕获可能引发的异常

try {

sConnect(ipe);//尝试连接

}

//处理参数为空引用异常

catch(ArgumentNullException ae) {

ConsoleWriteLine(ArgumentNullException : {} aeToString());

}

//处理操作系统异常

catch(SocketException se) {

ConsoleWriteLine(SocketException : {} seToString());

}

catch(Exception e) {

ConsoleWriteLine(Unexpected exception : {} eToString());

}

需要知道的是Socket 类支持两种基本模式同步和异步其区别在于在同步模式中对执行网络操作的函数(如 Send 和 Receive)的调用一直等到操作完成后才将控制返回给调用程序在异步模式中这些调用立即返回

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

string Get = GET / HTTP/\r\nHost: + server + \r\nConnection: Close\r\n\r\n;

远程主机指定端口接受到这一请求后就可利用其公用服务程序进行处理而不需要另行编制服务器端应用程序

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

实现此功能的主要源代码如下

//开始按钮事件

private void button_Click(object sender SystemEventArgs e) {

//取得预保存的文件名

string fileName=textBoxTextTrim();

//远程主机

string hostName=textBoxTextTrim();

//端口

int port=IntParse(textBoxTextTrim());

//得到主机信息

IPHostEntry ipInfo=DnsGetHostByName(hostName);

//取得IPAddress[]

IPAddress[] ipAddr=ipInfoAddressList;

//得到ip

IPAddress ip=ipAddr[];

//组合出远程终结点

IPEndPoint hostEP=new IPEndPoint(ipport);

//创建Socket 实例

Socket socket=new Socket(AddressFamilyInterNetworkSocketTypeStreamProtocolTypeTcp);

try

{

//尝试连接

socketConnect(hostEP);

}

catch(Exception se)

{

MessageBoxShow(连接错误+seMessage提示信息

MessageBoxButtonsRetryCancelMessageBoxIconInformation);

}

//发送给远程主机的请求内容串

string sendStr=GET / HTTP/\r\nHost: + hostName +

\r\nConnection: Close\r\n\r\n;

//创建bytes字节数组以转换发送串

byte[] bytesSendStr=new byte[];

//将发送内容字符串转换成字节byte数组

bytesSendStr=EncodingASCIIGetBytes(sendStr);

try

{

//向主机发送请求

socketSend(bytesSendStrbytesSendStrLength);

}

catch(Exception ce)

{

MessageBoxShow(发送错误:+ceMessage提示信息

MessageBoxButtonsRetryCancelMessageBoxIconInformation);

}

//声明接收返回内容的字符串

string recvStr=;

//声明字节数组一次接收数据的长度为字节

byte[] recvBytes=new byte[];

//返回实际接收内容的字节数

int bytes=;

//循环读取直到接收完所有数据

while(true)

{

bytes=socketReceive(recvBytesrecvBytesLength);

//读取完成后退出循环

if(bytes<=)

break;

//将读取的字节数转换为字符串

recvStr+=EncodingASCIIGetString(recvBytesbytes);

}

//将所读取的字符串转换为字节数组

byte[] content=EncodingASCIIGetBytes(recvStr);

try

{

//创建文件流对象实例

FileStream fs=new FileStream(fileNameFileModeOpenOrCreateFileAccessReadWrite);

//写入文件

fsWrite(contentcontentLength);

}

catch(Exception fe)

{

MessageBoxShow(文件创建/写入错误:+feMessage提示信息MessageBoxButtonsRetryCancelMessageBoxIconInformation);

}

//禁用Socket

socketShutdown(SocketShutdownBoth);

//关闭Socket

socketClose();

}

}

程序在WindowsXP中文版Net Frameworkd 中文正式版Visual StudioNet中文正式版下调试通过

               

上一篇:.net控件FreeTextBox使用方法

下一篇:在Visual Basic.NET使用ADO访问数据库