WinForms 小型HTML服务器

 最近教学,使用到了Apache和IIS,闲着无聊,有种想自己写个小服务器的冲动。

 在网上找了半天的资料,最后终于搞定了,测试可以访问。效果图如下:

 因为只是处理简单的请求,然后返回请求的页面,所以没有涉及到其他高级语言(php jsp aspx...)的处理。不过还是有点意思的哈,不说了,进入正题:

 开发工具:Visual Studio 2013

 开发环境:.NET Framework 2.0

关键源码如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.IO;
 4 using System.Text;
 5
 6 namespace Guying.Project.MiniServer
 7 {
 8     public class INIHelper
 9     {
10         private StreamReader sr;
11         private string[] strs = new string[255];//该数值也限定了INI文件最多不能超过255行
12         private int LinesOfTxt = 0;
13         //private string FileName;
14
15         public INIHelper(string iniFileName)
16         {
17             //FileName = iniFileName;
18             if (!File.Exists(iniFileName))
19             {
20                 File.CreateText(iniFileName);
21             }
22             sr = new StreamReader((System.IO.Stream)File.OpenRead(iniFileName), Encoding.Default);
23             //Console.WriteLine("Reading ini file: {0}",iniFileName);
24             while (sr.Peek() > -1)
25             {
26                 strs[LinesOfTxt] = sr.ReadLine();
27                 //把空行和以“#”或";"开头的注释行去掉
28                 if (!strs[LinesOfTxt].StartsWith("#") && !strs[LinesOfTxt].StartsWith(";") && (strs[LinesOfTxt] != "")) LinesOfTxt++;
29             }
30             sr.Close();
31         }
32
33         /// <summary>
34         /// 通过给定的value获得INI文件中对应项的值
35         /// </summary>
36         public string ValueOf(string cvalue)
37         {
38             string retn = "";
39             int i = 0, index;
40
41             while (i < LinesOfTxt)
42             {
43                 index = strs[i].IndexOf(cvalue + "=", 0, strs[i].Length);
44                 if (index >= 0)
45                 {
46                     retn = strs[i].Substring(index + cvalue.Length + 1);
47                     break;
48                 }
49                 i++;
50             }
51             return retn;
52         }
53
54     }
55 }

读写INI配置文件的辅助类

这个辅助类是针对这个程序自己编写的,只有简单的读取和写入。

更多功能的ini通用辅助类,就像是DBHelper一样,网上有整套的代码、例如在此分享一个:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Runtime.InteropServices;
  4 using System.Text;
  5 using System.IO;
  6 using System.Windows.Forms;
  7 using System.Diagnostics;
  8
  9 namespace Guying.Project.MiniServer
 10 {
 11     /// <summary>
 12     /// INI文件辅助类
 13     /// </summary>
 14     public class INIHelper_API
 15     {
 16
 17         #region 声明读写INI文件的API函数
 18         [DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileIntA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)]
 19         private static extern int GetPrivateProfileInt(string lpApplicationName, string lpKeyName, int nDefault, string lpFileName);
 20
 21         [DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileSectionsNamesA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)]
 22         private static extern int GetPrivateProfileSectionsNames(byte[] lpszReturnBuffer, int nSize, string lpFileName);
 23
 24         [DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileStringA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)]
 25         private static extern int GetPrivateProfileString(string lpApplicationName, string lpKeyName, string lpDefault, StringBuilder lpReturnedString, int nSize, string lpFileName);
 26
 27         [DllImport("KERNEL32")]
 28         private static extern int GetPrivateProfileString(string lpAppName, string lpszKey, string lpString, Byte[] lpStruct, int uSizeStruct, string lpFileName);
 29
 30         [DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileStructA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)]
 31         private static extern int GetPrivateProfileStruct(string lpszSections, string lpszKey, byte[] lpStruct, int uSizeStruct, string szFile);
 32
 33         [DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileSectionsA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)]
 34         private static extern int WritePrivateProfileSections(string lpAppName, string lpString, string lpFileName);
 35
 36         [DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileStringA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)]
 37         private static extern int WritePrivateProfileString(string lpApplicationName, string lpKeyName, string lpString, string lpFileName);
 38
 39         [DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileStructA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)]
 40         private static extern int WritePrivateProfileStruct(string lpszSections, string lpszKey, byte[] lpStruct, int uSizeStruct, string szFile);
 41         #endregion
 42
 43         private string _INIFilePath = Environment.CurrentDirectory + "\\Configs\\{0}.ls.ini";
 44
 45         #region 属性
 46         private string _FilePath;
 47         public string FilePath { get { return _FilePath; } set { _FilePath = value; } }
 48
 49         private string _ErrorMessage;
 50         public string ErrorMessage { get { return _ErrorMessage; } set { _ErrorMessage = value; } }
 51         #endregion
 52
 53         #region 构造函数
 54         /// <summary>
 55         /// 无参构造函数
 56         /// </summary>
 57         public INIHelper_API() { }
 58         /// <summary>
 59         /// 带参构造函数
 60         /// </summary>
 61         /// <param name="_iniFilePath">INI文件的绝对路径</param>
 62         public INIHelper_API(string _iniFileName)
 63         {
 64             if (File.Exists(string.Format(_INIFilePath, _iniFileName)))
 65             {
 66                 this.FilePath = string.Format(_INIFilePath, _iniFileName);
 67             }
 68             else
 69             {
 70                 this.ErrorMessage = "系统配置文件不存在!";
 71
 72                 this.FilePath = string.Format(_INIFilePath, _iniFileName);
 73                 File.Create(this.FilePath); // 创建配置文件
 74             }
 75         }
 76         #endregion
 77
 78         #region 将指定的值写入INI文件
 79         /// <summary>
 80         /// 将指定的值写入INI文件
 81         /// </summary>
 82         /// <param name="_sectionName">段落节点,格式[]</param>
 83         /// <param name="_key">键</param>
 84         /// <param name="_value">值</param>
 85         public void WriteValue(string _sectionName, string _key, string _value)
 86         {
 87             // 如果当前指定的配置文件路径为空,即不存在
 88             if (this.FilePath == null || string.IsNullOrEmpty(this.FilePath.Trim()) || !File.Exists(this.FilePath))
 89             {
 90                 this.FilePath = string.Format(_INIFilePath, new FileInfo(Application.ExecutablePath).Name);
 91
 92                 File.Create(this.FilePath); // 创建配置文件
 93             }
 94
 95             WritePrivateProfileString(_sectionName, _key, _value, this.FilePath);
 96         }
 97         #endregion
 98
 99         #region 读取INI配置文件信息
100         /// <summary>
101         /// 读取INI配置文件信息
102         /// </summary>
103         /// <param name="_sectionName">段落节点,格式[]</param>
104         /// <param name="_key">键名称</param>
105         /// <returns>返回目标值</returns>
106         public string ReadValue(string _sectionName, string _key)
107         {
108             // 如果当前指定的配置文件路径为空,即不存在
109             if (this.FilePath == null || string.IsNullOrEmpty(this.FilePath.Trim()) || !File.Exists(this.FilePath))
110             {
111                 this.FilePath = string.Format(_INIFilePath, new FileInfo(Application.ExecutablePath).Name);
112
113                 File.Create(this.FilePath); // 创建配置文件
114             }
115
116             StringBuilder result = new StringBuilder(255);
117             GetPrivateProfileString(_sectionName, _key, "", result, 255, this.FilePath);
118             return result.ToString();
119         }
120         #endregion
121
122         #region 读取INI配置文件
123         /// <summary>
124         /// 读取INI配置文件
125         /// </summary>
126         /// <param name="_sectionName">段落节点,格式[]</param>
127         /// <param name="_key">键名称</param>
128         /// <returns>返回byte类型的section组或键值组</returns>
129         public byte[] ReadValues(string _sectionName, string _key)
130         {
131             byte[] result = new byte[255];
132
133             // 如果当前指定的配置文件路径为空,即不存在
134             if (this.FilePath == null || string.IsNullOrEmpty(this.FilePath.Trim()) || !File.Exists(this.FilePath))
135             {
136                 this.FilePath = string.Format(_INIFilePath, new FileInfo(Application.ExecutablePath).Name);
137
138                 File.Create(this.FilePath); // 创建配置文件
139             }
140
141             GetPrivateProfileString(_sectionName, _key, "", result, 255, this.FilePath);
142             return result;
143         }
144         #endregion
145     }
146 }

网上其他的INI通用辅助类

其次就是程序运行需要的公用数据和方法:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.IO;
 4 using System.Text;
 5 using System.Windows.Forms;
 6
 7 namespace Guying.Project.MiniServer
 8 {
 9     /// <summary>
10     /// 提供系统运行时所需的公用数据和方法。
11     /// 该类中的数据成员会被多个线程并发访问,
12     /// 因此要求数据成员为静态的,并且仅在类的初始化时确定,
13     /// 在其他函数或过程中对数据成员赋值是不安全的。
14     /// </summary>
15     public class ServerInfo
16     {
17
18         private static INIHelper _INIHelper = new INIHelper("httpsrv.ini");
19
20         //private static string cgi=_INIHelper.ValueOf("cgi");
21         private static string WWW_ROOT = _INIHelper.ValueOf("wwwroot");
22         private static string NO_PAGE = _INIHelper.ValueOf("nopage");
23         private static string DEFAULT_PAGE = _INIHelper.ValueOf("defaultpage");
24         private static string WRONG_REQUEST_PAGE = _INIHelper.ValueOf("wrongrequest");
25         private static string CACHE_DIR = _INIHelper.ValueOf("cachedir");
26         private static string[] SPP_EXTS = _INIHelper.ValueOf("sppexts").Split(‘,‘);//get the knowed Server Process Page filename‘s extensions
27         private static string[] APP_HANDLE_EXT = new string[SPP_EXTS.Length];//get the application name which handle such ext
28
29         public ServerInfo()
30         {
31             FrmMain.GetInstance().ShowMessage("Loading Server Infomation...");
32
33             for (int i = 0 ;i < sppexts.Length ; i++) AppHandleExt[i] = _INIHelper.ValueOf(sppexts[i]);
34         }
35
36         public string wwwroot
37         {
38             get
39             {
40                 return WWW_ROOT;
41             }
42         }
43
44         public string nopage
45         {
46             get
47             {
48                 return NO_PAGE;
49             }
50         }
51
52         public string defaultpage
53         {
54             get
55             {
56                 return DEFAULT_PAGE;
57             }
58         }
59
60         public string wrongrequestpage
61         {
62             get
63             {
64                 return WRONG_REQUEST_PAGE;
65             }
66         }
67
68         public string cacheDIR
69         {
70             get
71             {
72                 return CACHE_DIR;
73             }
74         }
75
76         public string[] sppexts
77         {
78             get
79             {
80                 return SPP_EXTS;
81             }
82         }
83
84         public string[] AppHandleExt
85         {
86             get
87             {
88                 return APP_HANDLE_EXT;
89             }
90         }
91     }
92 }

程序运行需要的公用数据和方法

然后是读取配置文件,根据请求返回内容:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Diagnostics;
  4 using System.IO;
  5 using System.Net;
  6 using System.Net.Sockets;
  7 using System.Text;
  8
  9 namespace Guying.Project.MiniServer
 10 {
 11     /// <summary>
 12     /// RequestProcessor 中的所有非 static 方法,字段都有可能并发执行
 13     /// </summary>
 14     public class RequestProcessor
 15     {
 16         private static INIHelper _INIHelper = new INIHelper("MIME.ini");//用于getMIMEType()中;
 17
 18         public static ServerInfo _ServerInfo;// = new SrvInfo();//can get from SrvMain
 19
 20         public Socket sockSendData;//Notice: get from ClientSocketThread.s
 21         public string RequestID;//Notice: get from ClientSocketThread.currentThread.Name,now only for log
 22
 23         public RequestProcessor()
 24         {
 25             //Console.WriteLine("Loading \"RequestProcessor MODEL\" ...");
 26         }
 27
 28         private void sendContent(FileStream fs, long start, long length)
 29         {
 30             try
 31             {
 32
 33                 //报文头发送完毕,开始发送正文
 34                 if (length == 0) length = fs.Length - start;
 35                 const int SOCKETWINDOWSIZE = 8192;
 36                 long r = SOCKETWINDOWSIZE;
 37                 int rd = 0;
 38                 Byte[] senddatas = new Byte[SOCKETWINDOWSIZE];
 39                 //MemoryStream sr = new MemoryStream(fs);
 40                 fs.Seek(start, SeekOrigin.Begin);
 41                 do
 42                 {
 43                     r = start + length - fs.Position;
 44                     //fs.BeginRead(s,s,s,s,d) 以后使用的版本,用以提高读取的效率
 45                     if (r >= SOCKETWINDOWSIZE) rd = fs.Read(senddatas, 0, SOCKETWINDOWSIZE);
 46                     else rd = fs.Read(senddatas, 0, (int)r);
 47                     sockSendData.Send(senddatas, 0, rd, SocketFlags.None);
 48                 } while (fs.Position != start + length);
 49
 50                 //if the fs is a temp FileStream then delete the file
 51                 //for it created by server
 52                 //other way, all files in cacheDIR will be deleted
 53                 if (fs.Name.IndexOf(_ServerInfo.cacheDIR) >= 0)
 54                 {
 55                     string s = fs.Name;
 56                     fs.Close();
 57                     File.Delete(s);
 58                 }
 59             }
 60             catch (SocketException e)
 61             {
 62                 if (fs.Name.IndexOf(_ServerInfo.cacheDIR) >= 0)
 63                 {
 64                     string s = fs.Name;
 65                     fs.Close();
 66                     File.Delete(s);
 67                 }
 68                 throw e;
 69             }
 70             catch (IOException e)
 71             {
 72                 throw e;
 73             }
 74         }
 75
 76         //ever used,now unused
 77         private void sendContent(FileStream fs)
 78         {
 79             sendContent(fs, 0, 0);
 80         }
 81
 82         private string getMIMEType(string filename)
 83         {
 84             string fname = filename, typename = "text/html";
 85             if ((filename != "/") && (filename.IndexOf("?") < 0))
 86             {
 87                 int r = fname.LastIndexOf(‘.‘) + 1;
 88                 fname = fname.Remove(0, r);
 89                 if ((typename = _INIHelper.ValueOf(fname)) == "") typename = "application/" + fname;
 90             }
 91             return typename;
 92         }
 93
 94         private FileStream PreProcessAndSendHeader(string filename, long start, long length)
 95         {
 96             Encoding coding = Encoding.Default;
 97             Byte[] sendchars = new Byte[512];
 98             string strSend;
 99             FileStream fs;
100             try
101             {
102                 if (filename == "/") fs = new FileStream(_ServerInfo.wwwroot + _ServerInfo.defaultpage,
103                                          FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
104
105                 else if (isCGIorSPP(filename)) fs = ProcessCGIorSPP(filename);//get a stream that the function returned
106
107                 else fs = new FileStream(_ServerInfo.wwwroot + filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
108
109                 if (start == 0 && length == 0) strSend = "HTTP/1.1 200 OK";
110                 else strSend = "HTTP/1.1 206 Partial Content";
111                 sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray());
112                 sockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None);
113
114             }
115             catch (IOException)// FileNotFoundException)
116             {
117                 Console.WriteLine(FrmMain.strGMTDateTime() + " ERROR ID=[{0}]: {1} Request for file :\"{2}\" But NOT found", RequestID,
118                     ((IPEndPoint)sockSendData.RemoteEndPoint).ToString(), filename);
119
120                 fs = new FileStream(_ServerInfo.wwwroot + _ServerInfo.nopage, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
121                 filename = _ServerInfo.nopage;
122
123                 strSend = "HTTP/1.1 302 Found";
124                 sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray());
125                 sockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None);
126
127                 strSend = "Location: " + "http://" + ((IPEndPoint)sockSendData.LocalEndPoint).ToString() + "/" +
128                     _ServerInfo.nopage; //maybe it‘s danger
129                 sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray());
130                 sockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None);
131             }
132             catch (ArgumentException)//the request is WRONG
133             {
134                 Console.WriteLine(FrmMain.strGMTDateTime() + " ERROR ID=[{0}]: {1} send a WRONG request :\"{2}\" ", RequestID,
135                     ((IPEndPoint)sockSendData.RemoteEndPoint).ToString(), filename);
136
137                 fs = new FileStream(_ServerInfo.wwwroot + _ServerInfo.wrongrequestpage, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
138                 filename = _ServerInfo.wrongrequestpage;
139
140                 strSend = "HTTP/1.1 302 Found";
141                 sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray());
142                 sockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None);
143
144                 strSend = "Location: " + "http://" + ((IPEndPoint)sockSendData.LocalEndPoint).ToString() + "/" +
145                     _ServerInfo.wrongrequestpage; //maybe it‘s danger
146                 sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray());
147                 sockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None);
148             }
149
150
151             strSend = "Date: " + FrmMain.strGMTDateTime();
152             sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray());
153             sockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None);
154
155
156             strSend = "Server: httpsrv/1.0";
157             sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray());
158             sockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None);
159
160             strSend = "MIME-Version: 1.0";
161             sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray());
162             sockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None);
163
164             strSend = "Content-Type: " + getMIMEType(filename);
165             sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray());
166             sockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None); ;
167
168             if (length == 0) length = fs.Length - start;
169
170             strSend = "Content-Range: Bytes " + start.ToString() + "-" + (start + length - 1).ToString() + "/" + fs.Length.ToString();
171             sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray());
172             sockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None);
173
174             strSend = "Content-Length: " + length.ToString();
175             sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray());
176             sockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None);
177
178             //发送一个空行
179             sendchars = coding.GetBytes(("\r\n").ToCharArray());
180             sockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None);
181             return fs;
182         }
183
184         /// <summary>
185         /// About returns:
186         /// in HTTP 1.1 maybe the client need a Keep-Alive connection
187         /// then it send header with a field "Connection: Keep-Alive"
188         /// others who need NOT send header with a field "Connection: Closed"
189         /// or have no this field in lower HTTP version
190         /// Others:
191         /// i dont check the client‘s HTTP version
192         /// and assume it is HTTP 1.1
193         /// </summary>
194         /// <param name="RequestLines"></param>
195         /// <returns>return true only if the client request a Keep-Alive connection</returns>
196
197         public bool ParseRequestAndProcess(string[] RequestLines)
198         {
199             char[] sp = new Char[1] { ‘ ‘ };
200             string[] strs = RequestLines[0].Split(sp);
201             if (strs[0] == "GET")
202             {
203                 long start = 0;
204                 long length = 0;
205                 foreach (string str in RequestLines)
206                 {
207                     if (str.StartsWith("Range:"))
208                     {
209                         string s = str.Substring(str.IndexOf("=") + 1);
210                         string[] ss = s.Split(‘-‘);
211                         start = Convert.ToInt64(ss[0]);
212                         if (ss[1] != "") length = Convert.ToInt64(ss[1]) - start + 1;
213
214                     }
215                 }
216                 if (isRequestSecurity(strs[1]))
217                 {
218                     sendContent(PreProcessAndSendHeader(strs[1], start, length), start, length);
219                 }
220                 else
221                 {
222                     sendContent(PreProcessAndSendHeader(_ServerInfo.wrongrequestpage, 0, 0), 0, 0);
223                     Console.WriteLine(FrmMain.strGMTDateTime() + " WARNING ID=[{0}]: {1} send a WRONG request :\"{2}\" ", RequestID,
224                         ((IPEndPoint)sockSendData.RemoteEndPoint).ToString(),
225                         strs[1]);
226                 }
227             }
228             else
229                 if (strs[0] == "HEAD")
230                 {
231                     if (isRequestSecurity(strs[1]))
232                     {
233                         PreProcessAndSendHeader(strs[1], 0, 0);
234                     }
235                 }
236
237             foreach (string str in RequestLines)
238             {
239                 if (str.StartsWith("Connection:"))
240                 {
241                     string s = str.Substring(12);
242                     if (s == "Keep-Alive") return true;
243                 }
244             }
245             return false;
246         }
247
248         private bool isRequestSecurity(string strRequest)
249         {
250             if (strRequest.IndexOf("..") >= 0) return false;
251             return true;
252         }
253
254         /// <summary>
255         /// SPP is Server-end Process Page such as ASP php etc.
256         /// </summary>
257         private bool isCGIorSPP(string filename)
258         {
259             if (filename.IndexOf("?") >= 0) return true;
260             string ext = filename.Substring(filename.LastIndexOf(".") + 1);
261             for (int i = 0; i < _ServerInfo.sppexts.Length; i++)
262             {
263                 if ((ext == _ServerInfo.sppexts[i]) && (_ServerInfo.AppHandleExt[i] != ""))
264                     return true;
265             }
266             return false;
267         }
268
269         /// <summary>
270         /// return a FileStream get from CGI or SPP
271         /// </summary>
272         private FileStream ProcessCGIorSPP(string filename)
273         {
274             try
275             {
276                 string[] ss = new string[2];
277                 if (filename.IndexOf("?") >= 0)
278                 {
279                     ss[0] = filename.Substring(0, filename.IndexOf("?"));
280                     ss[1] = filename.Substring(filename.IndexOf("?") + 1);
281                 }
282                 else
283                 {
284                     ss[0] = filename; ss[1] = "";
285                 }
286                 while (ss[1].IndexOf("+") >= 0) ss[1] = ss[1].Replace("+", " ");
287                 Process p = new Process();
288                 string ext = "";
289                 if (ss[0].LastIndexOf(".") >= 0) ext = ss[0].Substring(ss[0].LastIndexOf(".") + 1);
290
291                 if ((ext != "") && (ext != "exe"))
292                     for (int i = 0; i < _ServerInfo.sppexts.Length; i++)
293                     {
294                         if (ext == _ServerInfo.sppexts[i])
295                         {
296                             if (_ServerInfo.AppHandleExt[i] == "shell")
297                             {
298                                 p.StartInfo.FileName = _ServerInfo.wwwroot + ss[0].Remove(0, 1);
299                                 p.StartInfo.Arguments = ss[1];
300                             }
301                             else
302                             {
303                                 p.StartInfo.FileName = _ServerInfo.AppHandleExt[i];
304                                 p.StartInfo.Arguments = _ServerInfo.wwwroot + ss[0].Remove(0, 1) + " " + ss[1];
305                             }
306                             break;
307                         }
308                     }
309                 else //ext == ""
310                 {
311                     p.StartInfo.FileName = _ServerInfo.wwwroot + ss[0].Remove(0, 1);
312                     p.StartInfo.Arguments = ss[1];
313                 }
314
315                 p.StartInfo.UseShellExecute = false;
316                 p.StartInfo.CreateNoWindow = true;
317                 p.StartInfo.RedirectStandardOutput = true;
318                 p.StartInfo.WorkingDirectory = p.StartInfo.FileName.Substring(0, p.StartInfo.FileName.LastIndexOf("/") + 1);
319                 p.Start();
320                 string s = p.StandardOutput.ReadToEnd();
321                 if (!Directory.Exists(_ServerInfo.wwwroot + _ServerInfo.cacheDIR))
322                     Directory.CreateDirectory(_ServerInfo.wwwroot + _ServerInfo.cacheDIR);
323                 //ss[0] = ss[0].Substring(ss[0].LastIndexOf("/"));
324                 FileStream fs = new FileStream(_ServerInfo.wwwroot + _ServerInfo.cacheDIR + RequestID + p.Id.ToString(),
325                     FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
326                 fs.Write(Encoding.Default.GetBytes(s), 0, s.Length);
327                 //p.WaitForExit();
328                 return fs;
329             }
330             catch (System.Runtime.InteropServices.ExternalException)
331             {
332                 IOException e = new IOException();
333                 throw e;
334             }
335             catch (System.SystemException)
336             {
337                 ArgumentException e = new ArgumentException();
338                 throw e;
339             }
340
341         }
342
343     }
344 }

读取配置文件,根据请求返回内容

防止程序未响应,自定义多线程处理:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Net;
 4 using System.Net.Sockets;
 5 using System.Text;
 6 using System.Threading;
 7
 8 namespace Guying.Project.MiniServer
 9 {
10     /// <summary>
11     /// ClientSocketThread 的摘要说明。
12     /// </summary>
13     public class ClientSocketThread
14     {
15         public TcpListener tcpl;//Notice: get from SrvMain.tcpl
16
17         private static Encoding ASCII = Encoding.ASCII;
18         //private static int RequestID = 0;
19
20         public void HandleThread()
21         {
22             string strClientIP = "";
23             string strClientPort = "";
24             string strLocalIP = "";
25             string strLocalPort = "";
26
27             Thread currentThread = Thread.CurrentThread;
28
29             try
30             {
31                 // Accept will block until someone connects
32                 Socket s = tcpl.AcceptSocket();
33                 //RequestID++;
34                 strLocalIP = ((IPEndPoint)s.LocalEndPoint).Address.ToString();
35                 strLocalPort = ((IPEndPoint)s.LocalEndPoint).Port.ToString();
36                 strClientIP = ((IPEndPoint)s.RemoteEndPoint).Address.ToString();
37                 strClientPort = ((IPEndPoint)s.RemoteEndPoint).Port.ToString();
38
39                 RequestProcessor aRequestProcessor = new RequestProcessor(); //Notice:
40                 aRequestProcessor.sockSendData = s;//Notice: so that the processor can work
41                 aRequestProcessor.RequestID = currentThread.Name;
42
43                 const int BUFFERSIZE = 4096;//that‘s enough???
44                 Byte[] readclientchar = new Byte[BUFFERSIZE];
45                 char[] sps = new Char[2] { ‘\r‘, ‘\n‘ };
46                 string[] RequestLines = new string[32];
47
48                 do
49                 {
50                     //use BUFFERSIZE contral the receive data size to avoid the BufferOverflow attack
51                     int rc = s.Receive(readclientchar, 0, BUFFERSIZE, SocketFlags.None);
52
53                     string strReceive = ASCII.GetString(readclientchar, 0, rc);
54
55                     RequestLines = strReceive.Split(sps);
56                     Console.WriteLine(FrmMain.strGMTDateTime() + " Request ID=[{0}] {1}->{2} :{3}",
57                         currentThread.Name,
58                         ((IPEndPoint)s.RemoteEndPoint).ToString(),
59                         ((IPEndPoint)s.LocalEndPoint).ToString(),
60                         RequestLines[0]);
61
62                 } while (aRequestProcessor.ParseRequestAndProcess(RequestLines));
63
64                 Console.WriteLine(FrmMain.strGMTDateTime() + " Closed ID=[{0}] {1}->{2} :Server Closed", currentThread.Name,
65                     ((IPEndPoint)s.LocalEndPoint).ToString(), ((IPEndPoint)s.RemoteEndPoint).ToString());
66                 s.Close();
67             }
68             catch (SocketException)
69             {
70                 Console.WriteLine(FrmMain.strGMTDateTime() + " Closed ID=[{0}] {1}:{2}->{3}:{4} :Client Closed", currentThread.Name,
71                     strClientIP, strClientPort, strLocalIP, strLocalPort);
72                 currentThread.Abort();
73             }
74         }
75
76     }
77 }

防止程序未响应,自定义多线程处理

最后,搭建测试窗体如下:

窗体调用实现方法的代码如下:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Diagnostics;
  6 using System.Drawing;
  7 using System.Net;
  8 using System.Net.Sockets;
  9 using System.Text;
 10 using System.Text.RegularExpressions;
 11 using System.Threading;
 12 using System.Windows.Forms;
 13
 14 namespace Guying.Project.MiniServer
 15 {
 16     public partial class FrmMain : Form
 17     {
 18         private static FrmMain _FrmMain = null;
 19         Thread _Thread = null;
 20         Thread _myWorkerThread = null;
 21
 22         private FrmMain()
 23         {
 24             InitializeComponent();
 25         }
 26
 27         public static FrmMain GetInstance()
 28         {
 29             if (_FrmMain == null)
 30             {
 31                 _FrmMain = new FrmMain();
 32             }
 33             return _FrmMain;
 34         }
 35
 36         public void ShowMessage(string _messageStr)
 37         {
 38             this.listBox_Logs.Items.Add(_messageStr);
 39         }
 40
 41         static public string strGMTDateTime()
 42         {
 43             DateTime GMTNow = DateTime.Now.ToUniversalTime();
 44             string month;
 45             switch (GMTNow.Month)
 46             {
 47                 case 1: month = "Jan"; break;
 48                 case 2: month = "Feb"; break;
 49                 case 3: month = "Mar"; break;
 50                 case 4: month = "Apr"; break;
 51                 case 5: month = "May"; break;
 52                 case 6: month = "Jun"; break;
 53                 case 7: month = "Jul"; break;
 54                 case 8: month = "Aug"; break;
 55                 case 9: month = "Sep"; break;
 56                 case 10: month = "Oct"; break;
 57                 case 11: month = "Nov"; break;
 58                 case 12: month = "Dec"; break;
 59                 default: month = "Martian???"; break;
 60             }
 61             return
 62                 GMTNow.DayOfWeek.ToString().Substring(0, 3) + ", " + GMTNow.Day.ToString() + " " + month + " " + GMTNow.Year.ToString() + " " + GMTNow.Hour.ToString() + ":" + GMTNow.Minute.ToString() + ":" + GMTNow.Second.ToString() + ":" + DateTime.Now.Millisecond.ToString() + " " + "GMT";
 63         }
 64
 65         private void btn_Start_Click(object sender, EventArgs e)
 66         {
 67             Start();
 68         }
 69
 70         void Start()
 71         {
 72             try
 73             {
 74                 ServerInfo _ServerInfo = new ServerInfo();
 75                 //i want to block the RequestProcessor when changing  _ServerInfo
 76                 lock (typeof(RequestProcessor))
 77                 {
 78                     RequestProcessor._ServerInfo = _ServerInfo;
 79                 }
 80
 81                 ShowMessage("Starting NetWork listening...");
 82
 83                 this.btn_Stop.Enabled = true;
 84                 this.btn_Start.Enabled = false;
 85
 86                 TcpListener tcpListener;
 87
 88                 try
 89                 {
 90                     tcpListener = new TcpListener(IPAddress.Parse(this.cmb_IPAddresses.Text), int.Parse(this.txt_IPPoint.Text)); // listen on port 80
 91                 }
 92                 catch (Exception)
 93                 {
 94                     ShowMessage("Wrong argument:Using [[IP] (Port)] as the options");
 95                     return;
 96                 }
 97                 tcpListener.Start();
 98
 99                 Console.WriteLine("Listening on {0}", ((IPEndPoint)tcpListener.LocalEndpoint).ToString());
100                 Console.WriteLine("Server now waiting for clients to connect");
101                 Console.WriteLine();
102                 Console.WriteLine(strGMTDateTime() + " Server Start,Start logging");
103                 //Console.WriteLine("Press Ctrl+c to Quit...");
104
105                 int ThreadID = 0;
106
107                 ThreadStart _ThreadStart = new ThreadStart(() =>
108                 {
109                     while (true)
110                     {
111                         while (!tcpListener.Pending())
112                         {
113                             Thread.Sleep(100);
114                         }
115
116                         ClientSocketThread myThreadHandler = new ClientSocketThread();
117                         myThreadHandler.tcpl = tcpListener;//Notice: dont forget do this
118                         ThreadStart myThreadStart = new ThreadStart(myThreadHandler.HandleThread);
119                         _myWorkerThread = new Thread(myThreadStart);
120                         _myWorkerThread.Name = (ThreadID++).ToString();
121                         _myWorkerThread.Start();
122                     }
123                 });
124
125                 _Thread = new Thread(_ThreadStart);
126                 _Thread.Start();
127
128             }
129             catch (SocketException socketError)
130             {
131                 if (socketError.ErrorCode == 10048)
132                 {
133                     ShowMessage("Connection to this port failed.  There is another server is listening on this port.");
134                 }
135             }
136             catch (FormatException)
137             {
138                 ShowMessage("invalid IP Address");
139             }
140             catch (Exception ex)
141             {
142                 ShowMessage("Ah O: " + ex.Message);
143             }
144         }
145
146         private void FrmMain_Load(object sender, EventArgs e)
147         {
148             if (System.IO.File.Exists(Application.StartupPath + "\\Guying.ssk"))
149             {
150                 this.skinEngine.SkinFile = Application.StartupPath + "\\Guying.ssk";
151                 this.skinEngine.SkinAllForm = true;
152             }
153
154             this.btn_Stop.Enabled = false;
155             this.btn_Start.Enabled = true;
156
157             this.cmb_IPAddresses.Items.Add("127.0.0.1");
158
159             try
160             {
161                 this.cmb_IPAddresses.Items.Add(GetOutterIPAddress());
162             }
163             catch (Exception) { }
164
165             IPAddress[] ipAddresses = Dns.GetHostAddresses(Environment.MachineName);
166             foreach (IPAddress ip in ipAddresses)
167             {
168                 if (ip.AddressFamily == AddressFamily.InterNetwork)
169                 {
170                     this.cmb_IPAddresses.Items.Add(ip);
171                 }
172             }
173         }
174
175         private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
176         {
177             Process.GetCurrentProcess().Kill();
178         }
179
180         public string GetOutterIPAddress()
181         {
182             string str = null;
183             //这个负责抓IP的页。第一步先抓取这个html页的全部内容
184             string url = "http://www.ikaka.com/ip/index.asp";
185             WebClient wc = new WebClient();
186             wc.Credentials = CredentialCache.DefaultCredentials;
187             Byte[] pageData = wc.DownloadData(url);
188             string MyUrl = System.Text.Encoding.UTF8.GetString(pageData);
189             //正则找到页面中的IP部分,并输出。
190             Regex regex = new Regex(@"(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))");
191             foreach (Match m in regex.Matches(MyUrl))
192             {
193                 str = m.ToString();
194             }
195             return str;
196
197         }
198
199         private void btn_Stop_Click(object sender, EventArgs e)
200         {
201             _Thread.Abort();
202             _myWorkerThread.Abort();
203
204             this.btn_Start.Enabled = true;
205             this.btn_Stop.Enabled = false;
206         }
207     }
208 }

窗体调用实现方法的代码

最后,设置皮肤样式,呵呵,搞定:

在此,这个小程序就搞定了,希望有大神能帮忙加上php或者aspx等页面的支持。那就完美了。呵呵。

代码上面都有了,如果需要源码的请留言邮箱地址。

【来自:[LonelyShadow 博客] http://www.cnblogs.com/LonelyShadow

WinForms 小型HTML服务器,布布扣,bubuko.com

时间: 2024-10-13 12:20:49

WinForms 小型HTML服务器的相关文章

Unix C语言编写基于进程的小型并发服务器

并发介绍 如果逻辑控制流在时间上是重叠的,那么它们就是并发的,可以出现在计算机系统的不同层面上,硬件异常处理程序.进程和Unix信号处理程序都是并发的.并发可以看作是操作系统内核用来运行多个应用程序的机制,但是并发不局限于内核.它也可以在应用程序中扮演角色.并发的主要作用有:访问慢速IO设备;与人交互的程序;通过推迟工作以降低延迟;服务多个网络客户端的请求.并发通常可以有三种,基于进程.基于IO多路复用.基于线程. 基于进程的并发 进程是一个程序运行的实例.每一个进程都有自己独立的地址空间,一般

Tiny server:小型Web服务器

一.背景 csapp的网络编程粗略的介绍了关于网络编程的一些知识,在最后的一节主要就实现了一个小型的Webserver.这个server名叫Tiny,它是一个小型的可是功能齐全的Webserver.在短短300行左右的代码中,结合了很多思想,比如,进程控制,unix I/O.套接字.HTTP等,令人兴奋的是,它能够为Web浏览器提供静态和动态的内容,也就是说在浏览器中要打开的HTML之类的文件能够直接通过Tiny直接显示在窗体. 我一直想要学习网络编程,这或许就是第一个做成的东西吧,想想都让人兴

Unix C语言编写基于IO多路复用的小型并发服务器

背景介绍 如果服务器要同时处理网络上的套接字连接请求和本地的标准输入命令请求,那么如果我们使用accept来接受连接请求,则无法处理标准输入请求;类似地,如果在read中等待一个输入请求,则无法处理网络连接的请求. 所谓I/O多路复用机制,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作.但 select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而还

NWJS fs, url, http 组合小型的服务器 ( 满足html请求, get, post 传值 )

<script type="text/javascript"> /* * 引入模块 */ var http = require('http'); var url = require('url'); var fs = require('fs'); /* * 获取项目根路径(存放 WEB 项目的根目录) */ var rootPath = global.__dirname + '/Node'; /* * 创建web服务器对象 createServer * http.create

小型云服务器搭建GitLab遇到的坑

云服务商:腾讯云,搞活动买的 3年800块钱,和同时一人一台 配置:1C.1G.50G 用三年,挺划算的 项目中以前一直使用SVN作为代码版本控制,秉着程序员做到老学到老的精神,想尝试一下先进的GIT,所以想搭一套自己的GIT环境. 动手干,首先我用自己的虚拟机跑CentOS 7 试着安装了一下GitLab,一次成功. 开始: 接着就把我自己的腾讯云服务器给重装了,什么WordPress Mysql全部不要了,做了一个自定义镜像给存储起来,万一哪天又想用了呢 是吧! 过程: 好家伙,搞了我1个下

Django框架实战:搭建一个小型的服务器运维网站(一)

本文会遇到很多的代码,如果想要源代码的小伙伴,我已经分享到下方了,想要的可以自己研究一下 本人从事Python开发多年,很多小伙伴说想学一下web中的Django框架,我就用Django框架做一系列的实战项目,欢迎大家订阅.欢迎大家订阅微信公众号:Python从程序猿到程序员,或者加4913.08659,编码:柯西,编码:柯西.有不懂的可以在里面问,同时也是一个学习直播资料的平台 前言 ??不管是运维还是开发抑或是测试,工作中不免会和Linux服务器打交道,常见的操作譬如:查看CPU或内存状态.

django框架,小型简单服务器搭建,获取姓名,html读取和url读取

这个接口就是WSGI:Web Server Gateway Interface.web网关接口. from wsgiref.simple_server import make_server #wsgiref是服务器类似于njax #调用服务器模块下的make_server #这只是模拟服务器,以后肯定是njax或者是apache #定义函数,参数 def application(environ, start_response): #print(environ)#封装请求信息,键值对形式显示请求者

小型web服务器

#pragma once                                                                     #include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/types.h> #include<sys/socket.h> #include<sys/sendfile.h> #include<

Ubuntu16.04最快捷搭建小型局域网Git服务器

导读 使用linux操作系统,不得不提Git版本管理器,这个Linus花了两周时间开发的分布式版本管理器(这就是大神,先膜了个拜...),毫无疑问,Git版本管理器与linux系统有着与生俱来的同一血缘,故而其在linux上的表现也是如鱼得水. 那么在linux上面管理文档.程序.源码等文件最好的就是Git了,而且Git是分布式,不必与中心服务器通讯即可管理本地版本.然而,对于一个协同工作的小型团队来说,仅靠本地版本管理是严重不够的,必须通过信息交互使团队的版本保持一致,方可确保团队的方向一致.