《ASP.NET本质》创建简单的Web服务器
来源:互联网 发布:linux查询mysql数据库 编辑:程序博客网 时间:2024/05/18 01:59
在遥远的Unix时代,为了解决传输层的编程问题,从 4.2BSD Unix开始,Unix提供了类似于文件操作的网络操作方式——Socket。通过Socket,程序员就可以像操作文件一样通过打开、写入、读取、关闭等操作完成网络编程。这使得网络编程可以统一到文件操作之下。通过Socket帮助程序员解决网络传输层的问题,而系统中的网络系统负责处理网络内部的复杂操作,这样程序员就可以比较容易地编写网络应用程序。需要注意的是,应用层的协议需要针对网络程序专门处理,Socket不负责应用层的协议,仅仅负责传输层的协议。
当然,网络毕竟不是简单的文件,所以,在使用Socket的时候,程序员还是需要设置一些网络相关的细节问题参数。
当通过Socket开发网络应用程序的时候,首先需要考虑所使用的网络类型,注意包括以下三个方面:
1)Socket类型,使用网络协议的类别,IPv4的类型为PF_INET。
2)数据通信的类型,常见的数据包(SOCK_DGRAM)、数据流(SOCK_STREAM)。
3)使用的网络协议,比如:TCP协议
在同一个网络地址上,为了区分使用相同协议的不同应用程序,可以为不同的应用程序分配一个数字编号,这个编号成为网络端口号(port)。端口号是一个两字节的证书,取值范围从0~65535.IANA(Internet Assgned Number Authority,互联网地址分配机构)维护了一个有端口分配列表,这些端口分为三类,第一类范围是0~1023,称为总所周知的端口,有IANA进行控制和分配,有特定的网络程序使用,例如TCP协议使用80号端口来完成HTTP协议的传输。第二类的范围是1024~49151,称为登记端口,这些端口不由IANA控制,但是IANA维护了一个登记的列表,如果没有在IANA登记的话,也不应该在程序中使用。但是,大多数的系统中,在没有冲突的情况下,也可以由用户程序使用。第三轮的范围是49152~65535,称为动态或者私有端口,这些端口可以由普通用户使用。
对于一个网络应用程序来说,通过地址、协议和端口号可以唯一地确定网络上的一个应用程序。其中地址和端口的组合成为端点(EndPoint)。每个Socket需要绑定到一个端点上与其他端点进行通信。
在.NET中,System.Net命名空间提供了网络编程的大多数数据类型以及常用操作,其中常用的类型如下:
IPAddress类用来表示一个IP地址
IPEndPoint类用来表示一个IP地址和一个端口的组合,成为网络的端点
System.Net.Sockets命名空间中提供了基于Socket编程的数据类型
Socket类封装了Socket的操作。
常用操作如下:
Listen:设置基于连接通信的Socket进入监听状态,并设置等待队列的长度
Accept:等待一个新的连接,当连接到达的时候,返回一个针对新连接的Socket对象。通过这个新的Socket对象,可以与新连接通信
Receive:通过Socket接受字节数据,保存到一个字节数组中,返回实际接受的字节数。
Send:通过Socket发送预先保存在字节数组中的数据。
下面的代码演示了如何通过Socket编程创建一个简单的Web服务器。这个服务器通过49152端口提供访问,向浏览器返回一个固定的静态网页。在这个示例中,请求的消息由浏览器生成,并发送到服务器,这个程序将简单地显示请求的信息。回应的消息由服务器程序生成,通过Socket传输层返回给浏览器。
static void Main(string[] args) { //获得本机的 loopback 网络地址 IPAddress address = IPAddress.Loopback; //创建可以访问的端点 IPEndPoint endPoint = new IPEndPoint(address, 49152); //创建Socket,使用IPv4地址,传输控制协议,双向、可靠、基于链接的字节流 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //将socket绑定到端点上 socket.Bind(endPoint); //设置连接队列的长度 socket.Listen(10); Console.WriteLine("开始监听,端口号:{0}",endPoint.Port); while (true) { //开始监听,这个方法会阻塞线程的执行,直到接受到一个客户端的连接请求 Socket client = socket.Accept(); //输出客户端地址 Console.WriteLine(client.RemoteEndPoint); //准备读取客户端请求的数据读取的数据将保存在一个数组中 byte[] buffer=new byte[4096]; //接受数据并获取数据长度 int length = client.Receive(buffer, 4096, SocketFlags.None); //将请求的数据翻译为UTF-8 System.Text.Encoding utf8 = System.Text.Encoding.UTF8; string requestString = utf8.GetString(buffer, 0, length); //显示请求的消息 Console.WriteLine(requestString); //回应的状态行 string statusLine = "HTTP/1.1 200 OK \r\n"; byte[] statusLineBytes = utf8.GetBytes(statusLine); //准备发送到客户端的网页 string responseBody = "<html><head><title>From Socket Server</title></head><body><h1>Hello,world</h1></body></html>"; byte[] responseBodyBytes = utf8.GetBytes(responseBody); //回应头部 string responseHeader = string.Format("Content-Type:text/html;charset=UTF-8\r\nContent-Length:{0}\r\n",responseBody.Length); byte[] responseHeaderBytes = utf8.GetBytes(responseHeader); //向客户端发送状态信息 client.Send(statusLineBytes); //向客户端发送回应头 client.Send(responseHeaderBytes); //头部与内容的分隔行 client.Send(new byte[] { 13, 10 }); //向客户端发送内容部分 client.Send(responseBodyBytes); //端口与客户端的连接 client.Close(); if (Console.KeyAvailable) break; } //关闭服务器 socket.Close(); }
开启服务,请求localhost:49152 模拟成功
-----------------------------------------------------------------
基于TcpListener的Web服务器
为了简化基于TCP协议的监听程序,.NET在System.Net.Sockets命名空间中提供了TcpListener类,使用它,在构造函数中传递一组网络端点信息就可以准备好监听参数,而不再需要设置使用的网络协议等细节,调用Start方法之后,监听工作就开始了。AcceptTcpClient方法将阻塞进程,直到一个客户端的连接到达监听器,这个方法将返回一个代表客户端连接的代理对象,它的类型为TcpClient,我们可以通过对它与客户端进行通信。
在输入输出部分,通过TcpClient对象可以得到一个用户输入和输出的网络流对性NetworkStream,这是一个派生自Stream对象的字节流对象,对Socket的输入和输出进行了封装,这样,我们可以通过常用的字节流操作来完成网络的输入和输出。
static void Main(string[] args) { //取得本机的loopback网络地址 IPAddress address = IPAddress.Loopback; //创建可以访问的端点 IPEndPoint endPoint = new IPEndPoint(address, 49152); //创建TCP监听器 TcpListener newserver = new TcpListener(endPoint); //启动监听 newserver.Start(); Console.WriteLine("开始监听"); while (true) { //等待客户端连接 TcpClient client = newserver.AcceptTcpClient(); Console.WriteLine("已经建立连接"); //得到一个网络流 NetworkStream ns = client.GetStream(); System.Text.Encoding utf8 = System.Text.Encoding.UTF8; byte[] request=new byte[4096]; int length = ns.Read(request, 0, 4096); //请求信息 string requestString = utf8.GetString(request, 0, length); Console.WriteLine(requestString); //状态行 string statusLine = "HTTP/1.1 200 OK\r\n"; byte[] statusLineBytes = utf8.GetBytes(statusLine); //网页内容 string responseBody = "<html><head><title></title></head><body>Hello<body></html>"; byte[] responseBodyBytes = utf8.GetBytes(responseBody); //回应的头部信息 string responseHeader = string.Format("Content-Type:text/html;charset=UTF-8\r\nContent-Length:{0}\r\n",responseBody.Length); byte[] responseHeaderBytes = utf8.GetBytes(responseHeader); //输入状态行 ns.Write(statusLineBytes, 0, statusLineBytes.Length); //输出回应头部 ns.Write(responseHeaderBytes, 0, responseHeaderBytes.Length); //回应头部与内容之间的空行 ns.Write(new byte[]{13,10},0,2); //输出内容 ns.Write(responseBodyBytes, 0, responseBodyBytes.Length); //关闭客户端连接 client.Close(); if (Console.KeyAvailable) break; } //关闭服务器 newserver.Stop(); }
----------------------------------------------
基于HttpListener的Web服务器
为了进一步简化HTTP协议的监听器,.NET在命名空间System.NET中提供了HttpListener类。伴随这个对象,.NET提供了一系列相关对象封装了HTTP的处理工作。注意,这个类使用Http.sys系统组件完成工作,所以,只有在Windows XP SP2 或者 Server 2003以上的操作系统中才能使用。
HttpListener类进一步简化了监听工作,仅需通过字符串的方法提供监听的地址、端口号以及虚拟路径,就可以开始监听工作。
开始监听后,GetContext方法将阻塞线程,当客户端的请求到达之后,HttpListener返回一个HttpListenerContext对性爱那个最为处理客户端请求的总代理,通过代理对象的Request属性,我们可以得到一个类型为HttpListenerRequest的代表请求参数的对象,这个对象将大多数请求参数进行了对象化,所以,我们可以通过它提供的一系列属性来获取请求参数。例如HttpListenerRequest的HttpMethod属性就提供了请求的方法类型。通过代理的Response属性,可以得到一个类型为HttpListenerResponse的回应处理对象,这个对象将回应的数据和操作进行了封装,使得我们大幅度简化了回应的编程工作了。
static void Main(string[] args) { //检查系统是否支持 if (!HttpListener.IsSupported) { throw new System.InvalidOperationException("使用HttpListener必须为Windows XP SP2 或 Server2003 以上系统"); } //注意前缀必须以 / 正斜杠结尾 string[] prefixes = new string[] {"http://localhost:49152/" }; //创建监听器 HttpListener listener = new HttpListener(); //增加监听的前缀 foreach (string s in prefixes) { listener.Prefixes.Add(s); } //开始监听 listener.Start(); Console.WriteLine("监听中"); while (true) { //注意:GetContext方法将阻塞线程,知道请求到达 HttpListenerContext context = listener.GetContext(); //获取对象 HttpListenerRequest request = context.Request; Console.WriteLine("{0}{1}HTTP/1.1",request.HttpMethod,request.RawUrl); Console.WriteLine("Accept:{0}",string.Join(",",request.AcceptTypes)); Console.WriteLine("Accept-Language:{0}",string.Join(",",request.UserLanguages)); Console.WriteLine("User-Agent:{0}",request.UserAgent); Console.WriteLine("Accept-Encoding:{0}",request.Headers["Accept-Encoding"]); Console.WriteLine("Connection:{0}",request.KeepAlive?"Keep-Alive":"close"); Console.WriteLine("Host:{0}",request.UserHostName); Console.WriteLine("Pragma:{0}",request.Headers["Pragma"]); //取得回应对象 HttpListenerResponse response = context.Response; //构造 回应内容 string responseString = @"<html><head><title>aaa</title></head><body>Hello</body></html>"; response.ContentLength64 = System.Text.Encoding.UTF8.GetByteCount(responseString); response.ContentType = "text/html;charset=UTF-8"; System.Text.Encoding utf8 = System.Text.Encoding.UTF8; System.IO.Stream output = response.OutputStream; byte[] responseStringBytes=utf8.GetBytes(responseString); //输出回应 output.Write(responseStringBytes, 0, responseStringBytes.Length); if (Console.KeyAvailable) break; } listener.Stop(); }
在使用HttpListener的时候,常用的请求和回应参数都变成了对象的属性,大幅度降低了变成的工作量。但是,大多数的参数还是需要通过Headers索引器来访问的,就像上例中的Accept-Encoding请求参数,我们就不能直接通过属性访问。
- 《ASP.NET本质》创建简单的Web服务器
- ASP.net本质论之用控制台应用程序创建Asp.net服务器
- 【简单Web服务器搭建】基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
- 【简单Web服务器搭建】基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
- 通过ASP.NET Web API + JQuery创建一个简单的Web应用
- asp.net运行的本质
- asp.net运行的本质
- asp.net运行的本质
- 动态创建 ASP.NET Web 服务器控件模板
- 使用 ASP.NET 语法创建 Web 服务器控件模板
- ASP.NET Web服务器控件的使用
- 第四。5章 使用ASP.NET服务器控件 创建外观一致的web站点
- 创建一个简单的web服务器
- 开发简单的 ASP.NET 服务器控件
- "创建ASP.NET应用程序遇到的web服务器运行的不是asp.net1.1版"故障的排除
- ASP.NET Web API 学习系列(一)创建与简单的增删改查
- ASP.NET - Web 服务器控件
- ASP.NET - Web 服务器控件
- jsp
- Linux下启动关闭数据库
- linux下解压zip文件
- 读取SQL server数据通过控件teechart显示曲线
- 分离路径
- 《ASP.NET本质》创建简单的Web服务器
- <配置> Ubuntu gcc 安装 使用方法
- Rotating Sentences
- MIPI显示标准
- windows关机命令
- System.IO之使用管道在进程间通信 (System.IO.Pipes使用)
- 安装虚拟机
- 虚拟机编译android源码 需要注意的地方
- 由a+b问题想到的