一步一步打造WebIM(4)——Comet的特殊之处

云翔在线聊天室提供在线聊天室,WebIM,网盘等服务,WebIM可以免费建群(不限人数),聊天记录在线存储。

WebIM系列文章

一步一步打造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方法发送数据。

源代码下载

如果本文对您有所帮助,可以点一下推荐

posted @ 2010-05-25 20:38 卢春城 阅读(...) 评论(...) 编辑 收藏