WinForms 小型HTML服务器
最近教学,使用到了Apache和IIS,闲着无聊,有种想自己写个小服务器的冲动。
在网上找了半天的资料,最后终于搞定了,测试可以访问。效果图如下:
因为只是处理简单的请求,然后返回请求的页面,所以没有涉及到其他高级语言(php jsp aspx...)的处理。不过还是有点意思的哈,不说了,进入正题:
开发工具:Visual Studio 2013
开发环境:.NET Framework 2.0
关键源码如下:
using System; using System.Collections.Generic; using System.IO; using System.Text; namespace Guying.Project.MiniServer { public class INIHelper { private StreamReader sr; ];//该数值也限定了INI文件最多不能超过255行 ; //private string FileName; public INIHelper(string iniFileName) { //FileName = iniFileName; if (!File.Exists(iniFileName)) { File.CreateText(iniFileName); } sr = new StreamReader((System.IO.Stream)File.OpenRead(iniFileName), Encoding.Default); //Console.WriteLine("Reading ini file: {0}",iniFileName); ) { strs[LinesOfTxt] = sr.ReadLine(); //把空行和以“#”或";"开头的注释行去掉 if (!strs[LinesOfTxt].StartsWith("#") && !strs[LinesOfTxt].StartsWith(";") && (strs[LinesOfTxt] != "")) LinesOfTxt++; } sr.Close(); } /// <summary> /// 通过给定的value获得INI文件中对应项的值 /// </summary> public string ValueOf(string cvalue) { string retn = ""; , index; while (i < LinesOfTxt) { index = strs[i].IndexOf(cvalue + , strs[i].Length); ) { retn = strs[i].Substring(index + cvalue.Length + ); break; } i++; } return retn; } } }
读写INI配置文件的辅助类
这个辅助类是针对这个程序自己编写的,只有简单的读取和写入。
更多功能的ini通用辅助类,就像是DBHelper一样,网上有整套的代码、例如在此分享一个:
using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; using System.IO; using System.Windows.Forms; using System.Diagnostics; namespace Guying.Project.MiniServer { /// <summary> /// INI文件辅助类 /// </summary> public class INIHelper_API { #region 声明读写INI文件的API函数 [DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileIntA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)] private static extern int GetPrivateProfileInt(string lpApplicationName, string lpKeyName, int nDefault, string lpFileName); [DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileSectionsNamesA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)] private static extern int GetPrivateProfileSectionsNames(byte[] lpszReturnBuffer, int nSize, string lpFileName); [DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileStringA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)] private static extern int GetPrivateProfileString(string lpApplicationName, string lpKeyName, string lpDefault, StringBuilder lpReturnedString, int nSize, string lpFileName); [DllImport("KERNEL32")] private static extern int GetPrivateProfileString(string lpAppName, string lpszKey, string lpString, Byte[] lpStruct, int uSizeStruct, string lpFileName); [DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileStructA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)] private static extern int GetPrivateProfileStruct(string lpszSections, string lpszKey, byte[] lpStruct, int uSizeStruct, string szFile); [DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileSectionsA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)] private static extern int WritePrivateProfileSections(string lpAppName, string lpString, string lpFileName); [DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileStringA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)] private static extern int WritePrivateProfileString(string lpApplicationName, string lpKeyName, string lpString, string lpFileName); [DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileStructA", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, ExactSpelling = true)] private static extern int WritePrivateProfileStruct(string lpszSections, string lpszKey, byte[] lpStruct, int uSizeStruct, string szFile); #endregion private string _INIFilePath = Environment.CurrentDirectory + "\\Configs\\{0}.ls.ini"; #region 属性 private string _FilePath; public string FilePath { get { return _FilePath; } set { _FilePath = value; } } private string _ErrorMessage; public string ErrorMessage { get { return _ErrorMessage; } set { _ErrorMessage = value; } } #endregion #region 构造函数 /// <summary> /// 无参构造函数 /// </summary> public INIHelper_API() { } /// <summary> /// 带参构造函数 /// </summary> /// <param name="_iniFilePath">INI文件的绝对路径</param> public INIHelper_API(string _iniFileName) { if (File.Exists(string.Format(_INIFilePath, _iniFileName))) { this.FilePath = string.Format(_INIFilePath, _iniFileName); } else { this.ErrorMessage = "系统配置文件不存在!"; this.FilePath = string.Format(_INIFilePath, _iniFileName); File.Create(this.FilePath); // 创建配置文件 } } #endregion #region 将指定的值写入INI文件 /// <summary> /// 将指定的值写入INI文件 /// </summary> /// <param name="_sectionName">段落节点,格式[]</param> /// <param name="_key">键</param> /// <param name="_value">值</param> public void WriteValue(string _sectionName, string _key, string _value) { // 如果当前指定的配置文件路径为空,即不存在 if (this.FilePath == null || string.IsNullOrEmpty(this.FilePath.Trim()) || !File.Exists(this.FilePath)) { this.FilePath = string.Format(_INIFilePath, new FileInfo(Application.ExecutablePath).Name); File.Create(this.FilePath); // 创建配置文件 } WritePrivateProfileString(_sectionName, _key, _value, this.FilePath); } #endregion #region 读取INI配置文件信息 /// <summary> /// 读取INI配置文件信息 /// </summary> /// <param name="_sectionName">段落节点,格式[]</param> /// <param name="_key">键名称</param> /// <returns>返回目标值</returns> public string ReadValue(string _sectionName, string _key) { // 如果当前指定的配置文件路径为空,即不存在 if (this.FilePath == null || string.IsNullOrEmpty(this.FilePath.Trim()) || !File.Exists(this.FilePath)) { this.FilePath = string.Format(_INIFilePath, new FileInfo(Application.ExecutablePath).Name); File.Create(this.FilePath); // 创建配置文件 } StringBuilder result = ); GetPrivateProfileString(_sectionName, _key, , this.FilePath); return result.ToString(); } #endregion #region 读取INI配置文件 /// <summary> /// 读取INI配置文件 /// </summary> /// <param name="_sectionName">段落节点,格式[]</param> /// <param name="_key">键名称</param> /// <returns>返回byte类型的section组或键值组</returns> public byte[] ReadValues(string _sectionName, string _key) { ]; // 如果当前指定的配置文件路径为空,即不存在 if (this.FilePath == null || string.IsNullOrEmpty(this.FilePath.Trim()) || !File.Exists(this.FilePath)) { this.FilePath = string.Format(_INIFilePath, new FileInfo(Application.ExecutablePath).Name); File.Create(this.FilePath); // 创建配置文件 } GetPrivateProfileString(_sectionName, _key, , this.FilePath); return result; } #endregion } }
网上其他的INI通用辅助类
其次就是程序运行需要的公用数据和方法:
using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Windows.Forms; namespace Guying.Project.MiniServer { /// <summary> /// 提供系统运行时所需的公用数据和方法。 /// 该类中的数据成员会被多个线程并发访问, /// 因此要求数据成员为静态的,并且仅在类的初始化时确定, /// 在其他函数或过程中对数据成员赋值是不安全的。 /// </summary> public class ServerInfo { private static INIHelper _INIHelper = new INIHelper("httpsrv.ini"); //private static string cgi=_INIHelper.ValueOf("cgi"); private static string WWW_ROOT = _INIHelper.ValueOf("wwwroot"); private static string NO_PAGE = _INIHelper.ValueOf("nopage"); private static string DEFAULT_PAGE = _INIHelper.ValueOf("defaultpage"); private static string WRONG_REQUEST_PAGE = _INIHelper.ValueOf("wrongrequest"); private static string CACHE_DIR = _INIHelper.ValueOf("cachedir"); private static string[] SPP_EXTS = _INIHelper.ValueOf("sppexts").Split(',');//get the knowed Server Process Page filename's extensions private static string[] APP_HANDLE_EXT = new string[SPP_EXTS.Length];//get the application name which handle such ext public ServerInfo() { FrmMain.GetInstance().ShowMessage("Loading Server Infomation..."); ;i < sppexts.Length ; i++) AppHandleExt[i] = _INIHelper.ValueOf(sppexts[i]); } public string wwwroot { get { return WWW_ROOT; } } public string nopage { get { return NO_PAGE; } } public string defaultpage { get { return DEFAULT_PAGE; } } public string wrongrequestpage { get { return WRONG_REQUEST_PAGE; } } public string cacheDIR { get { return CACHE_DIR; } } public string[] sppexts { get { return SPP_EXTS; } } public string[] AppHandleExt { get { return APP_HANDLE_EXT; } } } }
程序运行需要的公用数据和方法
然后是读取配置文件,根据请求返回内容:
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; namespace Guying.Project.MiniServer { /// <summary> /// RequestProcessor 中的所有非 static 方法,字段都有可能并发执行 /// </summary> public class RequestProcessor { private static INIHelper _INIHelper = new INIHelper("MIME.ini");//用于getMIMEType()中; public static ServerInfo _ServerInfo;// = new SrvInfo();//can get from SrvMain public Socket sockSendData;//Notice: get from ClientSocketThread.s public string RequestID;//Notice: get from ClientSocketThread.currentThread.Name,now only for log public RequestProcessor() { //Console.WriteLine("Loading \"RequestProcessor MODEL\" ..."); } private void sendContent(FileStream fs, long start, long length) { try { //报文头发送完毕,开始发送正文 ) length = fs.Length - start; ; long r = SOCKETWINDOWSIZE; ; Byte[] senddatas = new Byte[SOCKETWINDOWSIZE]; //MemoryStream sr = new MemoryStream(fs); fs.Seek(start, SeekOrigin.Begin); do { r = start + length - fs.Position; //fs.BeginRead(s,s,s,s,d) 以后使用的版本,用以提高读取的效率 , SOCKETWINDOWSIZE); , (int)r); sockSendData.Send(senddatas, , rd, SocketFlags.None); } while (fs.Position != start + length); //if the fs is a temp FileStream then delete the file //for it created by server //other way, all files in cacheDIR will be deleted ) { string s = fs.Name; fs.Close(); File.Delete(s); } } catch (SocketException e) { ) { string s = fs.Name; fs.Close(); File.Delete(s); } throw e; } catch (IOException e) { throw e; } } //ever used,now unused private void sendContent(FileStream fs) { sendContent(fs, , ); } private string getMIMEType(string filename) { string fname = filename, typename = "text/html"; )) { ; fname = fname.Remove(, r); if ((typename = _INIHelper.ValueOf(fname)) == "") typename = "application/" + fname; } return typename; } private FileStream PreProcessAndSendHeader(string filename, long start, long length) { Encoding coding = Encoding.Default; Byte[] sendchars = ]; string strSend; FileStream fs; try { if (filename == "/") fs = new FileStream(_ServerInfo.wwwroot + _ServerInfo.defaultpage, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); else if (isCGIorSPP(filename)) fs = ProcessCGIorSPP(filename);//get a stream that the function returned else fs = new FileStream(_ServerInfo.wwwroot + filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); && length == ) strSend = "HTTP/1.1 200 OK"; else strSend = "HTTP/1.1 206 Partial Content"; sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray()); sockSendData.Send(sendchars, , sendchars.Length, SocketFlags.None); } catch (IOException)// FileNotFoundException) { Console.WriteLine(FrmMain.strGMTDateTime() + " ERROR ID=[{0}]: {1} Request for file :\"{2}\" But NOT found", RequestID, ((IPEndPoint)sockSendData.RemoteEndPoint).ToString(), filename); fs = new FileStream(_ServerInfo.wwwroot + _ServerInfo.nopage, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); filename = _ServerInfo.nopage; strSend = "HTTP/1.1 302 Found"; sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray()); sockSendData.Send(sendchars, , sendchars.Length, SocketFlags.None); strSend = "Location: " + "http://" + ((IPEndPoint)sockSendData.LocalEndPoint).ToString() + "/" + _ServerInfo.nopage; //maybe it's danger sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray()); sockSendData.Send(sendchars, , sendchars.Length, SocketFlags.None); } catch (ArgumentException)//the request is WRONG { Console.WriteLine(FrmMain.strGMTDateTime() + " ERROR ID=[{0}]: {1} send a WRONG request :\"{2}\" ", RequestID, ((IPEndPoint)sockSendData.RemoteEndPoint).ToString(), filename); fs = new FileStream(_ServerInfo.wwwroot + _ServerInfo.wrongrequestpage, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); filename = _ServerInfo.wrongrequestpage; strSend = "HTTP/1.1 302 Found"; sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray()); sockSendData.Send(sendchars, , sendchars.Length, SocketFlags.None); strSend = "Location: " + "http://" + ((IPEndPoint)sockSendData.LocalEndPoint).ToString() + "/" + _ServerInfo.wrongrequestpage; //maybe it's danger sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray()); sockSendData.Send(sendchars, , sendchars.Length, SocketFlags.None); } strSend = "Date: " + FrmMain.strGMTDateTime(); sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray()); sockSendData.Send(sendchars, , sendchars.Length, SocketFlags.None); strSend = "Server: httpsrv/1.0"; sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray()); sockSendData.Send(sendchars, , sendchars.Length, SocketFlags.None); strSend = "MIME-Version: 1.0"; sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray()); sockSendData.Send(sendchars, , sendchars.Length, SocketFlags.None); strSend = "Content-Type: " + getMIMEType(filename); sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray()); sockSendData.Send(sendchars, , sendchars.Length, SocketFlags.None); ; ) length = fs.Length - start; strSend = ).ToString() + "/" + fs.Length.ToString(); sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray()); sockSendData.Send(sendchars, , sendchars.Length, SocketFlags.None); strSend = "Content-Length: " + length.ToString(); sendchars = coding.GetBytes((strSend + "\r\n").ToCharArray()); sockSendData.Send(sendchars, , sendchars.Length, SocketFlags.None); //发送一个空行 sendchars = coding.GetBytes(("\r\n").ToCharArray()); sockSendData.Send(sendchars, , sendchars.Length, SocketFlags.None); return fs; } /// <summary> /// About returns: /// in HTTP 1.1 maybe the client need a Keep-Alive connection /// then it send header with a field "Connection: Keep-Alive" /// others who need NOT send header with a field "Connection: Closed" /// or have no this field in lower HTTP version /// Others: /// i dont check the client's HTTP version /// and assume it is HTTP 1.1 /// </summary> /// <param name="RequestLines"></param> /// <returns>return true only if the client request a Keep-Alive connection</returns> public bool ParseRequestAndProcess(string[] RequestLines) { ] { ' ' }; ].Split(sp); ] == "GET") { ; ; foreach (string str in RequestLines) { if (str.StartsWith("Range:")) { ); string[] ss = s.Split('-'); start = Convert.ToInt64(ss[]); ] != ]) - start + ; } } ])) { sendContent(PreProcessAndSendHeader(strs[], start, length), start, length); } else { sendContent(PreProcessAndSendHeader(_ServerInfo.wrongrequestpage, , ), , ); Console.WriteLine(FrmMain.strGMTDateTime() + " WARNING ID=[{0}]: {1} send a WRONG request :\"{2}\" ", RequestID, ((IPEndPoint)sockSendData.RemoteEndPoint).ToString(), strs[]); } } else ] == "HEAD") { ])) { PreProcessAndSendHeader(strs[], , ); } } foreach (string str in RequestLines) { if (str.StartsWith("Connection:")) { ); if (s == "Keep-Alive") return true; } } return false; } private bool isRequestSecurity(string strRequest) { ) return false; return true; } /// <summary> /// SPP is Server-end Process Page such as ASP php etc. /// </summary> private bool isCGIorSPP(string filename) { ) return true; ); ; i < _ServerInfo.sppexts.Length; i++) { if ((ext == _ServerInfo.sppexts[i]) && (_ServerInfo.AppHandleExt[i] != "")) return true; } return false; } /// <summary> /// return a FileStream get from CGI or SPP /// </summary> private FileStream ProcessCGIorSPP(string filename) { try { ]; ) { ss[] = filename.Substring(, filename.IndexOf("?")); ss[] = filename.Substring(filename.IndexOf(); } else { ss[] = filename; ss[] = ""; } ].IndexOf() ss[] = ss[].Replace("+", " "); Process p = new Process(); string ext = ""; ].LastIndexOf() ext = ss[].Substring(ss[].LastIndexOf(); if ((ext != "") && (ext != "exe")) ; i < _ServerInfo.sppexts.Length; i++) { if (ext == _ServerInfo.sppexts[i]) { if (_ServerInfo.AppHandleExt[i] == "shell") { p.StartInfo.FileName = _ServerInfo.wwwroot + ss[].Remove(, ); p.StartInfo.Arguments = ss[]; } else { p.StartInfo.FileName = _ServerInfo.AppHandleExt[i]; p.StartInfo.Arguments = _ServerInfo.wwwroot + ss[].Remove(, ) + ]; } break; } } else //ext == "" { p.StartInfo.FileName = _ServerInfo.wwwroot + ss[].Remove(, ); p.StartInfo.Arguments = ss[]; } p.StartInfo.UseShellExecute = false; p.StartInfo.CreateNoWindow = true; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.WorkingDirectory = p.StartInfo.FileName.Substring(, p.StartInfo.FileName.LastIndexOf(); p.Start(); string s = p.StandardOutput.ReadToEnd(); if (!Directory.Exists(_ServerInfo.wwwroot + _ServerInfo.cacheDIR)) Directory.CreateDirectory(_ServerInfo.wwwroot + _ServerInfo.cacheDIR); //ss[0] = ss[0].Substring(ss[0].LastIndexOf("/")); FileStream fs = new FileStream(_ServerInfo.wwwroot + _ServerInfo.cacheDIR + RequestID + p.Id.ToString(), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite); fs.Write(Encoding.Default.GetBytes(s), , s.Length); //p.WaitForExit(); return fs; } catch (System.Runtime.InteropServices.ExternalException) { IOException e = new IOException(); throw e; } catch (System.SystemException) { ArgumentException e = new ArgumentException(); throw e; } } } }
读取配置文件,根据请求返回内容
防止程序未响应,自定义多线程处理:
using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; namespace Guying.Project.MiniServer { /// <summary> /// ClientSocketThread 的摘要说明。 /// </summary> public class ClientSocketThread { public TcpListener tcpl;//Notice: get from SrvMain.tcpl private static Encoding ASCII = Encoding.ASCII; //private static int RequestID = 0; public void HandleThread() { string strClientIP = ""; string strClientPort = ""; string strLocalIP = ""; string strLocalPort = ""; Thread currentThread = Thread.CurrentThread; try { // Accept will block until someone connects Socket s = tcpl.AcceptSocket(); //RequestID++; strLocalIP = ((IPEndPoint)s.LocalEndPoint).Address.ToString(); strLocalPort = ((IPEndPoint)s.LocalEndPoint).Port.ToString(); strClientIP = ((IPEndPoint)s.RemoteEndPoint).Address.ToString(); strClientPort = ((IPEndPoint)s.RemoteEndPoint).Port.ToString(); RequestProcessor aRequestProcessor = new RequestProcessor(); //Notice: aRequestProcessor.sockSendData = s;//Notice: so that the processor can work aRequestProcessor.RequestID = currentThread.Name; ;//that's enough??? Byte[] readclientchar = new Byte[BUFFERSIZE]; ] { '\r', '\n' }; ]; do { //use BUFFERSIZE contral the receive data size to avoid the BufferOverflow attack , BUFFERSIZE, SocketFlags.None); , rc); RequestLines = strReceive.Split(sps); Console.WriteLine(FrmMain.strGMTDateTime() + " Request ID=[{0}] {1}->{2} :{3}", currentThread.Name, ((IPEndPoint)s.RemoteEndPoint).ToString(), ((IPEndPoint)s.LocalEndPoint).ToString(), RequestLines[]); } while (aRequestProcessor.ParseRequestAndProcess(RequestLines)); Console.WriteLine(FrmMain.strGMTDateTime() + " Closed ID=[{0}] {1}->{2} :Server Closed", currentThread.Name, ((IPEndPoint)s.LocalEndPoint).ToString(), ((IPEndPoint)s.RemoteEndPoint).ToString()); s.Close(); } catch (SocketException) { Console.WriteLine(FrmMain.strGMTDateTime() + " Closed ID=[{0}] {1}:{2}->{3}:{4} :Client Closed", currentThread.Name, strClientIP, strClientPort, strLocalIP, strLocalPort); currentThread.Abort(); } } } }
防止程序未响应,自定义多线程处理
最后,搭建测试窗体如下:
窗体调用实现方法的代码如下:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Drawing; using System.Net; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Windows.Forms; namespace Guying.Project.MiniServer { public partial class FrmMain : Form { private static FrmMain _FrmMain = null; Thread _Thread = null; Thread _myWorkerThread = null; private FrmMain() { InitializeComponent(); } public static FrmMain GetInstance() { if (_FrmMain == null) { _FrmMain = new FrmMain(); } return _FrmMain; } public void ShowMessage(string _messageStr) { this.listBox_Logs.Items.Add(_messageStr); } static public string strGMTDateTime() { DateTime GMTNow = DateTime.Now.ToUniversalTime(); string month; switch (GMTNow.Month) { : month = "Jan"; break; : month = "Feb"; break; : month = "Mar"; break; : month = "Apr"; break; : month = "May"; break; : month = "Jun"; break; : month = "Jul"; break; : month = "Aug"; break; : month = "Sep"; break; : month = "Oct"; break; : month = "Nov"; break; : month = "Dec"; break; default: month = "Martian???"; break; } return GMTNow.DayOfWeek.ToString().Substring(, ) + ", " + GMTNow.Day.ToString() + " " + month + " " + GMTNow.Year.ToString() + " " + GMTNow.Hour.ToString() + ":" + GMTNow.Minute.ToString() + ":" + GMTNow.Second.ToString() + ":" + DateTime.Now.Millisecond.ToString() + " " + "GMT"; } private void btn_Start_Click(object sender, EventArgs e) { Start(); } void Start() { try { ServerInfo _ServerInfo = new ServerInfo(); //i want to block the RequestProcessor when changing _ServerInfo lock (typeof(RequestProcessor)) { RequestProcessor._ServerInfo = _ServerInfo; } ShowMessage("Starting NetWork listening..."); this.btn_Stop.Enabled = true; this.btn_Start.Enabled = false; TcpListener tcpListener; try { tcpListener = new TcpListener(IPAddress.Parse(this.cmb_IPAddresses.Text), int.Parse(this.txt_IPPoint.Text)); // listen on port 80 } catch (Exception) { ShowMessage("Wrong argument:Using [[IP] (Port)] as the options"); return; } tcpListener.Start(); Console.WriteLine("Listening on {0}", ((IPEndPoint)tcpListener.LocalEndpoint).ToString()); Console.WriteLine("Server now waiting for clients to connect"); Console.WriteLine(); Console.WriteLine(strGMTDateTime() + " Server Start,Start logging"); //Console.WriteLine("Press Ctrl+c to Quit..."); ; ThreadStart _ThreadStart = new ThreadStart(() => { while (true) { while (!tcpListener.Pending()) { Thread.Sleep(); } ClientSocketThread myThreadHandler = new ClientSocketThread(); myThreadHandler.tcpl = tcpListener;//Notice: dont forget do this ThreadStart myThreadStart = new ThreadStart(myThreadHandler.HandleThread); _myWorkerThread = new Thread(myThreadStart); _myWorkerThread.Name = (ThreadID++).ToString(); _myWorkerThread.Start(); } }); _Thread = new Thread(_ThreadStart); _Thread.Start(); } catch (SocketException socketError) { ) { ShowMessage("Connection to this port failed. There is another server is listening on this port."); } } catch (FormatException) { ShowMessage("invalid IP Address"); } catch (Exception ex) { ShowMessage("Ah O: " + ex.Message); } } private void FrmMain_Load(object sender, EventArgs e) { if (System.IO.File.Exists(Application.StartupPath + "\\Guying.ssk")) { this.skinEngine.SkinFile = Application.StartupPath + "\\Guying.ssk"; this.skinEngine.SkinAllForm = true; } this.btn_Stop.Enabled = false; this.btn_Start.Enabled = true; this.cmb_IPAddresses.Items.Add("127.0.0.1"); try { this.cmb_IPAddresses.Items.Add(GetOutterIPAddress()); } catch (Exception) { } IPAddress[] ipAddresses = Dns.GetHostAddresses(Environment.MachineName); foreach (IPAddress ip in ipAddresses) { if (ip.AddressFamily == AddressFamily.InterNetwork) { this.cmb_IPAddresses.Items.Add(ip); } } } private void FrmMain_FormClosing(object sender, FormClosingEventArgs e) { Process.GetCurrentProcess().Kill(); } public string GetOutterIPAddress() { string str = null; //这个负责抓IP的页。第一步先抓取这个html页的全部内容 string url = "http://www.ikaka.com/ip/index.asp"; WebClient wc = new WebClient(); wc.Credentials = CredentialCache.DefaultCredentials; Byte[] pageData = wc.DownloadData(url); string MyUrl = System.Text.Encoding.UTF8.GetString(pageData); //正则找到页面中的IP部分,并输出。 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]))"); foreach (Match m in regex.Matches(MyUrl)) { str = m.ToString(); } return str; } private void btn_Stop_Click(object sender, EventArgs e) { _Thread.Abort(); _myWorkerThread.Abort(); this.btn_Start.Enabled = true; this.btn_Stop.Enabled = false; } } }
窗体调用实现方法的代码
最后,设置皮肤样式,呵呵,搞定:
在此,这个小程序就搞定了,希望有大神能帮忙加上php或者aspx等页面的支持。那就完美了。呵呵。
代码上面都有了,如果需要源码的请留言邮箱地址。
【来自:[LonelyShadow 博客] http://www.cnblogs.com/LonelyShadow】
WinForms 小型HTML服务器的更多相关文章
- 小型Http服务器
HTTP又叫做超文本传输协议,现如今用的最多的版本是1.1版本.HTTP有如下的特点: 支持客户/服务器模式(C/S或B/S) 简单快速:基于请求和响应,请求只需传送请求方法和请求路径 灵活:HTTP ...
- C语言构建小型Web服务器
#include <stdio.h> #include <sys/socket.h> #include <stdlib.h> #include <string ...
- Tiny server:小型Web服务器
一.背景 csapp的网络编程粗略的介绍了关于网络编程的一些知识,在最后的一节主要就实现了一个小型的Webserver.这个server名叫Tiny,它是一个小型的可是功能齐全的Webserver.在 ...
- MINI_httpd移植,构建小型WEB服务器
一.简介 目的:构建小型WEB站,具备SSL. mini_httpd is a small HTTP server. Its performance is not great, but for low ...
- HttpListener 实现小型web服务器
HttpListener 实现web服务器 用于小型服务器,简单.方便.不需要部署. 总共代码量不超过50行. static void Main(string[] args) { //创建HTTP监听 ...
- django搭建一个小型的服务器运维网站-拿来即用的bootstrap模板
目录 项目介绍和源码: 拿来即用的bootstrap模板: 服务器SSH服务配置与python中paramiko的使用: 用户登陆与session; 最简单的实践之修改服务器时间: 查看和修改服务器配 ...
- Node fs, url, http 组合小型的服务器 ( 满足html请求, get, post 传值 )
<script type="text/javascript"> /* * 引入模块 */ var http = require('http'); var url = r ...
- 小型云服务器搭建GitLab遇到的坑
云服务商:腾讯云,搞活动买的 3年800块钱,和同时一人一台 配置:1C.1G.50G 用三年,挺划算的 项目中以前一直使用SVN作为代码版本控制,秉着程序员做到老学到老的精神,想尝试一下先进的GIT ...
- 小型web服务器thttpd的学习总结(下)
1.主函数模块分析 对于主函数而言,概括来说主要做了三点内容,也就是初始化系统,进行系统大循环,退出系统.下面主要简单阐述下在这三个部分,又做了哪些工作呢. 初始化系统 拿出程序的名字(argv[0] ...
随机推荐
- Metadata Lock原理3
http://blog.itpub.net/26515977/viewspace-1208250/ 腾讯工程师 随着5.5.3引入MDL,更多的Query被“Waiting for table ...
- Python学习 之 包和模块
1.rpm -ql python #查看python在计算机中安装了哪些文件 2.模块是一个可以导入的Python脚本文件 包是一堆按目录组织的模块和子包,目录下的__init__.py文件存放了包的 ...
- 1081. Rational Sum (20)
the problem is from PAT,which website is http://pat.zju.edu.cn/contests/pat-a-practise/1081 the code ...
- 称球问题(zt)
下面说的这个问题可能大家都看到过,它是这么描述的: 现在有n(n>=2)个球,n个球外观一模一样,但是重量有区别,其中有且仅有一个球的重量比其它n-1个球要重,现在有一个天平,天平是完好无损的, ...
- 小白日记35:kali渗透测试之Web渗透-手动漏洞挖掘(一)-默认安装引发的漏洞
手动漏洞挖掘 即扫描后,如何对发现的漏洞告警进行验证. #默认安装 流传linux操作系统比windows系统安全的说法,是因为windows系统默认安装后,会开放很多服务和无用的端口,而且未经过严格 ...
- UrlPathEncode与UrlEncode的区别
UrlEncode与UrlPathEncode 的基本作用都是对 URL 字符串进行编码 不同点总结如下: 不同点 UrlEncode UrlPathEncode 处理空格的方式 替换成“+” 替换成 ...
- Android(java)学习笔记93:Android布局详解之一:FrameLayout
FrameLayout是最简单的布局了.所有放在布局里的控件,都按照层次堆叠在屏幕的左上角.后加进来的控件覆盖前面的控件. 在FrameLayout布局里,定义任何空间的位置相关的属性都毫无意义.控件 ...
- 【Irrlicht鬼火引擎】掌握引擎使用流程,入门程序HelloWorld
分析 一.简述使用步骤 一般而言,对于一个简单的程序,Irrlicht引擎的一般使用步骤如下: 预处理:(1)包含 <irrlicht.h> 头文件#include <irrlich ...
- [改善Java代码]异步运算考虑使用Callable接口
多线程有两种实现方式: 一种是实现Runnable接口,另一种是继承Thread类,这两种方式都有缺点,run方法没有返回值,不能抛出异常(这两个缺点归根到底是Runable接口的缺陷,Thread也 ...
- HttpClient(4.3.5) - HTTP Execution Context
Originally HTTP has been designed as a stateless, response-request oriented protocol. However, real ...