简单web服务器学习总结

281 篇文章 6 订阅
150 篇文章 1 订阅

用node.js实现简单的web服务器

http://www.cnblogs.com/loogn/p/3362475.html


http模块已提供了基本功能,所以我主要解决两个问题,1是静态资源的处理,2是动态资源的路由。


静态资源在node.js里的意思是不变的,如图片、前端js、css、html页面等。


动态资源我们一般指aspx页面,ashx页面,asp页面,jsp页面,php页面等,而node.js里其实没动态资


源这一说,它对请求的处理都是由回调方法完成的,在我实现的httserver里,借鉴了ashx的写法,把处


理请求的js文件看作动态资源。


首先实现一个处理静态资源的函数,其实就是对本地文件的读取操作,这个方法已满足了上面说的静态


资源的处理。


//处理静态资源
function staticResHandler(localPath, ext, response) {
    fs.readFile(localPath, "binary", function (error, file) {
        if (error) {
            response.writeHead(500, { "Content-Type": "text/plain" });
            response.end("Server Error:" + error);
        } else {
            response.writeHead(200, { "Content-Type": getContentTypeByExt(ext) });
            response.end(file, "binary");
        }
    });
}
而动态资源肯定不能一个方法搞定,就像你的网站有register.aspx、login.aspx等等,都需要你自己来


写,在我的httpserver里,每个处理请求的js模块都导出processRequest(request,response)即可,比


如实现一个register.js(只输出字符串register)


exports.processRequest = function (request, response) {
    response.writeHead(200, { 'Content-Type': 'text/plain' });
    resp.end("register");
}
现在当请求到来时,我们要做的就是决定怎么处理,即路由。


因为静态资源url指定静态资源大家都很习惯了,所以这里不变,比如


访问http://localhost/img/logo.png  就是访问 web根目录\img\logo.png;


访问http://localhost/js/what.js 就是访问 web根目录\js\what.js;


而动态资源也是一般的js文件,即服务器端js,就比如我实现的这个httpserver.js和上面说的


register.js都是不应该让用户访问的,所以路由的时候要判断,就是一些if、else,简单而强大是我的


最爱,这里只看最后的的判断,


fs.exists(localPath, function (exists) {
    if (exists) {
        if (staticRes) {
            staticResHandler(localPath, ext, response); //静态资源
        } else {
            try {
                <strong><span style="color: #008000;">var handler = require


(localPath);</span></strong>
                if (handler.processRequest && typeof handler.processRequest === 'function') 


{
                    <span style="color: #008000;"><strong>handler.processRequest(request, 


response);</strong></span> //动态资源
                } else {
                    response.writeHead(404, { 'Content-Type': 'text/plain' });
                    response.end('404:Handle Not found');
                }
            } catch (exception) {
                console.log('error::url:' + request.url + 'msg:' + exception);
                response.writeHead(500, { "Content-Type": "text/plain" });
                response.end("Server Error:" + exception);
            }
        }
    } else { //资源不存在
        response.writeHead(404, { 'Content-Type': 'text/plain' });
        response.end('404:File Not found');
    }
});
处理静态资源上面已说过了,请看处理动态资源的那两句,localPath是相对web根目录的后端js的路径


,如果上面register.js在 根目录/src/account文件夹里,那么你的url请求就是


http://localhost/account/register,而这时localPath就是./src/account/register.js,注意这里不


是MVC,只是url没有src路径和.js后缀而已,那么为什么要这样呢?就是为了和前端js文件区分开!


再有,没有配置的程序不是好程序,不过我的配置总是很烂的配置!(你可能感觉我写的很乱,不过没


关系,后面给出完整代码,看一下就清楚了,如果你感觉不错,下载了事例在你电脑上运行了,那我也


倍感荣幸了!)


//配置
var config = {
    port: 80,
    denyAccess: ['./httpserver.js', './src/requirecache.js'],
    localIPs: ['127.0.0.1'],
    srcpath: '/src'
};
./src/requirecache.js这个文件是干什么的呢?这里要说明一下,require这个方法是有缓存机制的,


它把加载过的模块都缓存到require.cache这个对象中,当第二次require的时候就直接回返缓存的模块


了,当然这样是为性能考虑,但是我修改一下register.js是不想重启web服务器的,如果你感觉无所谓


,那这个特殊的动态资源就不需要了,请明白,requirecache.js和register.js是被一样看待的,都是


处理请求的js文件。requirecache.js模块的功能就是删除模板缓存:


var querystring=require('querystring');
var url=require('url');
 
exports.processRequest = function (request, response) {
    response.writeHead(200, { 'Content-Type': 'text/html' });
    var qs= querystring.parse(url.parse(request.url).query);
    if(qs.key){
        delete require.cache[qs.key];
    }
    response.write('<html><head></head><body>');
    for(var key in require.cache){
        response.write('<a href="?key='+key+'">'+key+'</a><br/>');
    }
    response.write('<a href="?">View</a><br/>');
    response.end('</body></html>');   
}
运行了node httpserver.js后,打开http://localhost/requirecache大概是这样:


然后就是删哪个点哪个就可以(对于httpserver.js来说,是一个很实用小功能)。


源代码有两个文件:


httpserver.js(这个是必须的)


 httpserver.js
requirecache.js(这个是很有用的,要放到config.srcpath路径下)


 requirecache.js
完成例子下载 
========

nodejs简单搭建web服务器  

http://blog.163.com/liuyong_xiaxia/blog/#m=0


      nodejs是可以作为前端语言的服务器,采用事件驱动,速度快、性能好。他可以不用像tomcat那


么复杂的配置、直接部署前端应用。
        首先到官网下载相应的版本https://nodejs.org/,安装到本地机器上面。
        然后进入安装目录、把做好的配置文件(server.js)放到安装目录下面。
        再进入命令模式、到安装目录、执行node server,此时它会提示你缺少一些模块,缺少什么安


装什么就可以,npm install XXX -g(表示全局安装,不用-g表示就安装到安装目录modules下)。
         模块安装完成后、再执行node server命令,没任何错误表示启动成功,可以到浏览器直接访


问。


     具体server.js如下,其中connect、serve-static就表示node服务需要依赖的模块。路径表示你前


端应用所在的路径,端口表示node服务要监听的端口。
var connect = require('connect');
var serveStatic = require('serve-static');
var server = connect();
server.use(serveStatic('E:/work/workspace/public'));
server.listen(8080);
========

boa服务器



BOA 服务器是一个小巧高效的web服务器,是一个运行于unix或linux下的,支持CGI的、适合于嵌入式系


统的单任务的http服务器,源代码开放、性能高。
目录
1 Boa
2 CGI
Boa
是一种非常小巧的Web服务器,其可执行代码只有大约60KB左右。作为一种单任务Web服务器,Boa只能依


次完成用户的请求,而不会fork出新的进程来处理并发连接请求。但Boa支持CGI,能够为CGI程序fork出


一个进程来执行。Boa的设计目标是速度和安全。
CGI
在物理上是一段程序,运行在服务器上,提供同客户端HTML页面的接口。即客户端与服务器的接口。
比如留言本的工作流程:先由用户在客户端输入一些信息,如名字之类的东西。接着用户按一下“留言


”(到目前为止工作都在客户端),浏览器把这些信息传送到服务器的CGI目录下特定的cgi程序中,于


是cgi程序在服务器上按照预定的方法进行处理。在本例中就是把用户提交的信息存入指定的文件中。然


后cgi程序给客户端发送一个信息,表示请求的任务已经结束。此时用户在浏览器里将看到“留言结束”


的字样。整个过程结束。
========

最简单的web服务器

http://www.cnblogs.com/qiaoyang/archive/2011/10/26/2225559.html
通过Socket编程创建一个简单的web服务器。这个服务器通过80号端口提供访问,向浏览器返回一个固定


的静态页面。此示例中请求的消息由浏览器生成,并发送到服务器,这个程序将简单地显示请求的消息


。回应的消息由服务器程序生成,通过Socket传输返回给浏览器。


public class SimpleSocketListener
    {
        public void Run()
        {
            // 取得本机的 loopback 网络地址,即 127.0.0.1
            IPAddress address = IPAddress.Loopback;
            // 创建可以访问的端点,8001 表示端口号
            IPEndPoint endPoint = new IPEndPoint(address,8001);
            // 创建一个 socket,使用 IPv4 地址,传输控制协议 TCP,双向、可靠、基于连接的字


节流
            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();
        }
    }
在浏览器的窗口中输入服务器的地址:http://127.0.0.1:8001,则浏览器中可以看到如图所示效果。
========

最简单的Web服务器

http://www.cnblogs.com/wlitsoft/archive/2012/04/24/2467652.html


本案例中应用到得一些类和方法先一一列出来


1.IPAddress类用来表示一个IP地址


  1.1 IPAddress.Parse("192.168.43.104") 将一串ip地址字符串转换为IP地址
  1.2 IPAddress.Loopback  获得本机回环地址 即:127.0.0.1




2.IPEndPoint类(端点) 说白了就是ip地址和端口的组合(IP:Point) 他能唯一确定网络中的一台电


脑的某一个应用程序


  IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("192.168.43.104"), 8080); //创建了


一个ip地址为192.168.43.104端口号位8080的网络端点


    2.1.1 Address 获取或设置终结点的 IP 地址   ex:endPoint.Address  //192.168.43.104
    2.1.2 AddressFamily 获得网络协议              ex:endPoint.AddressFamily  //http
    2.1.3 AddressPoint   获得端口号信息          ex:endPoint.Point   //8080




3.Socket类    位于System.Net.Socket命名空间中    其封装了Socket(套接字)的操作。


  3.1 Listen 设置基于连接通信的socket进入监听状态,并设置等待队列的长度。  ex:


socket.Listen(10)  //只允许10个客户端同时发生请求
  3.2 Accept 等待一个新的连接,当新的连接到达的时候,返回一个针对新连接的Socket对象。即每


个建立连接的客户端都对应有一个Socket对象在服务器端,客户端通过这个对象就可以和服务端进行连


接通信了。    ex:   Socket client = socket.Accept();
  3.3 Receive 通过Socket接受字节数据,保存到一个字节数据中,返回一个int型的数据(实际接收


的字节数)。
    ex://新建一个缓冲区
                byte[] buffer = new byte[2048];
                //接受数据
                int length = client.Receive(buffer,buffer.Length,SocketFlags.None);
  3.4 Send 故名思议 就是发送数据吧 没错  send通过Socket发送预先保存在字节数组中的数据。


下面是完整的事例代码:




//----------------------------------------------------------------------------------------
            IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("192.168.43.104"), 8080);
            //创建socket,使用ipv4地址,传输协议为tcp,双向、可靠、基于连接的字节流
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, 


ProtocolType.Tcp);
            //将其绑定到一个端点上
            socket.Bind(endPoint);
            //设置连接队列的长度
            socket.Listen(10);
            Console.WriteLine("开始监听,端口号:{0}",endPoint.Port);
            while (true)
            { 
                //开始监听。这个方法阻塞线程执行,直到接受到一个客户端的连接请求
                Socket client = socket.Accept();
                //输出客户端地址
                Console.WriteLine("客户端地址:{0}",client.RemoteEndPoint);
                //新建一个缓冲区
                byte[] buffer = new byte[2048];
                //接受数据
                int length = client.Receive(buffer,buffer.Length,SocketFlags.None);
                //将请求的数据转换为utf-8
                //Encoding utf8 = Encoding.UTF8;
                string requestString = Encoding.UTF8.GetString(buffer, 0, length);
                //显示请求的消息
                Console.WriteLine(requestString);


                //回应的状态行
                string statusLine = "HTTP/1.1 200 OK\r\n";
                byte[] statusLineBytes = Encoding.UTF8.GetBytes(statusLine);
                //准备发送到客户端的网页
                string responseBody = "<html><head><title>hello 


world</title></head><body><h2>Hello World</h2></body></html>";
                byte[] responseBodyBytes = Encoding.UTF8.GetBytes(responseBody);
                //回应的头部
                string responseHeader = string.Format("Content-type:text/html;charset=UTF-


8\r\nContent-Length:{0}\r\n", responseBody.Length);
                byte[] responseHeaderBytes = Encoding.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();


因为http协议是无状态连接,所以每完成一次或多次请求服务器会自动与客户端断开连接,保持服务器


的资源


运行结果:


本文源代码点击下载
========

C#建立最简单的web服务,无需IIS

http://blog.csdn.net/he_zhidan/article/details/46820215


本程序只是入门级程序,所以不考虑
1,多线程。
2,安全性。
3,不考虑端点下载文件。
4,Keep-Alive。
5,不考虑head。
6,为了简洁,删掉了catch的内容。


exe的祖父目录必须有wwwroot文件夹,且文件夹有index.htm,内容不限。 
开发环境: WinXP+VS2010C#


一,新建一个项目TestWeb,项目类型:Windows窗口应用程序。
二,新建类RequestProcessor。
 using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Diagnostics;


namespace TestWeb
{
    class RequestProcessor
    {
        public bool ParseRequestAndProcess(string[] RequestLines)//解析内容
        {
            for (int i = 0; i < RequestLines.Length; i++)
                System.Diagnostics.Trace.Write(RequestLines[i]);


            char[] sp = new Char[1] { ' ' };
            string[] strs = RequestLines[0].Split(sp);
            if (strs[0] == "GET")
            {
                Send(strs[1], 0, 0);
            }


            return false;
        }


        void Send(string filename, long start, long length)//发送文件(文件头和文件)
        {
            string strFileName = GetPathFileName(filename);
            FileStream fs = null;
            try
            {
                fs = new FileStream(strFileName, FileMode.Open, FileAccess.Read, 


FileShare.ReadWrite);


            }
            catch (IOException)// FileNotFoundException)
            {//不能将 e.Message,发给浏览器,否则会有安全隐患的
                SendHeadrAndStr("打开文件" + filename + "失败。");
                return;
            }


            if (length == 0)
                length = fs.Length - start;


            SendHeader("text/html", (fs.Length == length), start, length);
            sendContent(fs, start, length);
        }


        public void SendHeadrAndStr(String str)//直接将str的内容发给html
        {
            byte[] sendchars = Encoding.Default.GetBytes((str).ToCharArray());
            SendHeader("text/html", true, 0, sendchars.Length);
            SendStr(Encoding.Default, str);
        }


        private void SendHeader(string fileType, bool bAll, long start, long length)//发送


文件头
        {
            try
            {
                Encoding coding = Encoding.Default;
                string strSend;
                string strState = (bAll) ? "HTTP/1.1 200 OK" : "HTTP/1.1 206 Partial 


Content";
                SendStr(coding, strState + "\r\n");
                SendStr(coding, "Date: \r\n");
                SendStr(coding, "Server: httpsrv/1.0\r\n");
                SendStr(coding, "MIME-Version: 1.0\r\n");
                SendStr(coding, "Content-Type: " + fileType + "\r\n");




                strSend = "Content-Length: " + length.ToString();
                SendStr(coding, strSend + "\r\n");


                //发送一个空行
                SendStr(coding, "\r\n");
            }
            catch (ArgumentException)//the request is WRONG
            {




            }


        }


        private void sendContent(FileStream fs, long start, long length)//发生文件内容
        {
            try
            {


                //报文头发送完毕,开始发送正文
                const int SOCKETWINDOWSIZE = 8192;
                long r = SOCKETWINDOWSIZE;
                int rd = 0;
                Byte[] senddatas = new Byte[SOCKETWINDOWSIZE];
                fs.Seek(start, SeekOrigin.Begin);
                do
                {
                    r = start + length - fs.Position;
                    //fs.BeginRead(s,s,s,s,d) 以后使用的版本,用以提高读取的效率            


    
                    if (r >= SOCKETWINDOWSIZE)
                        rd = fs.Read(senddatas, 0, SOCKETWINDOWSIZE);
                    else
                        rd = fs.Read(senddatas, 0, (int)r);
                    mSockSendData.Send(senddatas, 0, rd, SocketFlags.None);
                } while (fs.Position != start + length);


            }
            catch (SocketException e)
            {
                throw e;
            }
            catch (IOException e)
            {
                throw e;
            }
        }


        public Socket mSockSendData;//Notice: get from ClientSocketThread.s


        private string GetPathFileName(string filename)
        {
            const string strDefaultPage = "index.htm";
            const string strWWWRoot = "..\\..\\wwwroot\\";
            string strFileName = String.Copy(filename);
            if ("/" == strFileName)
                strFileName = strDefaultPage;
            return System.AppDomain.CurrentDomain.BaseDirectory + strWWWRoot + strFileName;
        }


        private void SendStr(Encoding coding, string strSend)//发送一个字符串
        {
            Byte[] sendchars = new Byte[512];
            sendchars = coding.GetBytes((strSend).ToCharArray());
            mSockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None);
        }
    }
}


三,新建类ClientSocketThread。
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;


namespace TestWeb
{
    class ClientSocketThread
    {
        public TcpListener tcpl;//Notice: get from SrvMain.tcpl
        private static Encoding ASCII = Encoding.ASCII;


        public void HandleThread()
        {
            Thread currentThread = Thread.CurrentThread;
            try
            {


                Socket s = tcpl.AcceptSocket();




                RequestProcessor aRequestProcessor = new RequestProcessor(); //Notice:
                aRequestProcessor.mSockSendData = s;//Notice: so that the processor can 


work




                const int BUFFERSIZE = 4096;//that's enough???
                Byte[] readclientchar = new Byte[BUFFERSIZE];
                char[] sps = new Char[2] { '\r', '\n' };
                string[] RequestLines = new string[32];


                do
                {
                    //use BUFFERSIZE contral the receive data size to avoid the 


BufferOverflow attack
                    int rc = s.Receive(readclientchar, 0, BUFFERSIZE, SocketFlags.None);


                    string strReceive = ASCII.GetString(readclientchar, 0, rc);


                    RequestLines = strReceive.Split(sps);




                } while (aRequestProcessor.ParseRequestAndProcess(RequestLines));


                s.Close();
            }
            catch (SocketException)
            {


            }
        }




    }



四,主对话框中增加按钮,按键的相应函数加如下代码。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;


using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;


namespace TestWeb
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }


        private void button1_Click(object sender, EventArgs e)
        {
              try
            {
                //启动监听程序                
                TcpListener tcpl;
                IPAddress LocalIP = Dns.Resolve("localhost").AddressList[0];
                tcpl = new TcpListener(LocalIP, 80); // listen on port 80
                tcpl.Start();
                         


               // int ThreadID = 0;
                while (true)
                {
                    while (!tcpl.Pending())
                    {
                        Thread.Sleep(100);
                    }


                    //启动接受线程
                    ClientSocketThread myThreadHandler = new ClientSocketThread();
                    myThreadHandler.tcpl = tcpl;//Notice: dont forget do this
                    ThreadStart myThreadStart = new ThreadStart


(myThreadHandler.HandleThread);
                    Thread myWorkerThread = new Thread(myThreadStart);      
                    myWorkerThread.Start();
                }
            }
            catch (SocketException )
            {
           
            }
            catch (FormatException)
            {
               
            }
            catch (Exception )
            {
               
            }
            //  Console.Read();
       
        }
    }
}


五,启动TestWeb.exe,并单击主对话框上的按钮。在浏览器中输入:http://127.0.0.1/ 或


http://127.0.0.1:80。


源码下载:
http://download.csdn.net/detail/he_zhidan/8884733
========
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值