[转载]一步一步打造WebIM(4)——Comet的特殊之处 – 卢春城专栏 – 博客园.
在一步一步打 造WebIM(1)一文中已经使用Comet实现了一个简单的WebIM,那么,Comet究竟和一般的打开网页有何区别,本文将通过编写一个简 单的HTTP服务器来说明两者的区别。
所谓网站,其实可以理解为服务器上的一个应用程序,该应用程序创建了一个Socket并在80端 口(一般是80端口)上监听,并接受和处理浏览器发送过来的HTTP请求。
当你打开网页时,浏览器会发送一个HTTP请求到服务器,之 后浏览器将一直等待知道服务器发送完HTTP回应。当服务器接受到这个http请求后,就会解析HTTP请求的头部,根据报文中的信息向浏览器发送数据 (网页,图片,数据等),当服务器发送完数据后,浏览器就结束等待,显示出网页。关于浏览器和服务器之前的交互,可以阅读这篇博文:
HTTP协议及其POST与GET操作差异 & C#中如何使用POST、GET等
根 据以上叙述的交互过程,可以编写一个简单的http服务器,该服务器在8000端口监听,并且只提供两个网页
http://localhost:8000/index.htm
http://localhost:8000/comet.htm
这两个网页的区别在于,index.htm服务器在接收到HTTP请求后立刻发送网页并结束本次链接,而Comet.htm则不是如此,是将链 接先挂起,并启动一个线程,新的线程将延迟5秒再发送网页。服务器源代码如下:
using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Text.RegularExpressions; using System.Net; using System.Net.Sockets; using System.Threading; namespace http { class Program { static String ResponeFormat = "HTTP/1.1 200 OK\r\n" + "Content-Length: {0}\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "{1}\r\n"; static void Main(string[] args) { Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //在8000端口监听 EndPoint ep = new IPEndPoint(new IPAddress(new byte[] { 127, 0, 0, 1 }), 8000); server.Bind(ep); server.Listen(5); while (true) { Socket client = server.Accept(); //读取HTTP报文头部 String header = String.Empty; while (header.IndexOf("\r\n\r\n") < 0) { byte[] buffer = new byte[1024]; int len = client.Receive(buffer); header += Encoding.ASCII.GetString(buffer, 0, len); } Console.WriteLine("================================================================================"); Console.Write(header); Regex reg = new Regex(@"GET\s+([^\s\r\n]+)\s+HTTP/1.1\s*\r\n", RegexOptions.IgnoreCase); Match match = reg.Match(header); if (match.Success) { String fname = Path.GetFileName(match.Groups[1].Value).ToLower(); if (fname == "index.htm") { String html = String.Format("<span style='color:red; fonst-size:24px;'>{0}</span>", DateTime.Now); String response = String.Format(ResponeFormat, html.Length, html); //发送HTTP回应,本次HTTP请求结束,浏览器将立刻接受到网页 client.Send(Encoding.UTF8.GetBytes(response)); client.Close(); } else if (fname == "comet.htm") { //假设此时数据未准备好,先挂起链接,网页将在另一个线程中发送到浏览器 //在线程没有发送HTTP回应前,浏览器将一直等待,直到启动的线程发送回应 Thread thread = new Thread(new ParameterizedThreadStart(SendThreadEntry)); thread.Start(client); } else { client.Close(); } } } } static void SendThreadEntry(object data) { //等待5s Thread.Sleep(5000); Socket client = data as Socket; String html = String.Format("<span style='color:red; fonst-size:24px;'>{0}</span>", DateTime.Now); String response = String.Format(ResponeFormat, html.Length, html); Console.WriteLine("Send Response"); //发送HTTP回应,本次HTTP请求结束,浏览器此时才接收到网页,在此之前浏览器一直等待 client.Send(Encoding.UTF8.GetBytes(response)); client.Close(); } } }
当然,发送Comet.htm的数据不一定启动一个新的线程,可以先将client保存起来(例如保存到全局变量中),等数据准备好了,调用 client的Send方法发送数据。