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

 

posted @ 2014-08-11 00:12  张董  阅读(3908)  评论(60编辑  收藏  举报