何志丹
本程序仅仅是入门级程序。所以不考虑 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) { } } } } 四。主对话框中添加button,按键的对应函数加例如以下代码。 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,并单击主对话框上的button。在浏览器中输入:http://127.0.0.1/ 或http://127.0.0.1:80。
源代码下载: