首页 > 代码库 > WinForms 小型HTML服务器
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通用辅助类,就像是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 #endregion121 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 #endregion145 }146 }
其次就是程序运行需要的公用数据和方法:
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 ServerInfo16 {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 extensions27 private static string[] APP_HANDLE_EXT = new string[SPP_EXTS.Length];//get the application name which handle such ext28 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 wwwroot37 {38 get 39 {40 return WWW_ROOT;41 }42 }43 44 public string nopage45 {46 get 47 {48 return NO_PAGE;49 }50 }51 52 public string defaultpage53 {54 get 55 {56 return DEFAULT_PAGE;57 }58 }59 60 public string wrongrequestpage61 {62 get 63 {64 return WRONG_REQUEST_PAGE;65 }66 }67 68 public string cacheDIR69 {70 get 71 {72 return CACHE_DIR;73 }74 }75 76 public string[] sppexts77 {78 get 79 {80 return SPP_EXTS;81 }82 }83 84 public string[] AppHandleExt85 {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 try101 {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 returned106 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 danger129 sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray());130 sockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None);131 }132 catch (ArgumentException)//the request is WRONG133 {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 danger146 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 connection187 /// 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 version190 /// Others:191 /// i dont check the client‘s HTTP version192 /// and assume it is HTTP 1.1193 /// </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 else221 {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 else229 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 SPP271 /// </summary>272 private FileStream ProcessCGIorSPP(string filename)273 {274 try275 {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 else283 {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 else302 {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 ClientSocketThread14 {15 public TcpListener tcpl;//Notice: get from SrvMain.tcpl16 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 try30 {31 // Accept will block until someone connects32 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 = http://www.mamicode.com/s;//Notice: so that the processor can work41 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 do49 {50 //use BUFFERSIZE contral the receive data size to avoid the BufferOverflow attack51 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 this118 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 try160 {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 =http://www.mamicode.com/ 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】
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。