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] ...
随机推荐
- MySQL · 物理备份 · Percona XtraBackup 备份原理
http://mysql.taobao.org/monthly/2016/03/07/ 前言 Percona XtraBackup(简称PXB)是 Percona 公司开发的一个用于 MySQL 数据 ...
- 高级I/O之非阻塞I/O
http://www.cnblogs.com/nufangrensheng/p/3515035.html中曾将系统调用分成“低速”系统调用和其他系统调用两类.低速系统调用是可能会使进程永远阻塞的一类系 ...
- Learning Theory
Empiricial Risk Minimization 统计学习理论是整个机器学习到框架.试想我们学习的目的是什么呢?当然是为了具备用合理的方式处理问题的能力.统计学习理论要解决的问题就是基于数据找 ...
- Fedora下载地址
http://fedoraproject.org/zh_CN/get-fedora-all
- java io文件学习笔记
File f = new file("D:"+File.separator+"test.txt"); File.separator跨系统文件分隔符 f.crea ...
- python multiprocess不能完全关闭socket的验证
近日项目有原来的多线程升级成为多进程模型后,但出现了个问题,在持续运行一天左右系统处理能力开始变慢,并不时打印以下信息: too many opened files 修改ulimit中open fil ...
- ASP.NET MVC and jqGrid 学习笔记 2-如何从本地获得数据
上回说到jqgrid的基本配置,同时演示了显示数据的一种方法——datatype: "local".这种方法是从本地获取的,确切地说是在前端页面的javascript里写的硬编码. ...
- ubuntu14_gtk 安装
1:apt-get install build-essential2:apt-get install gnome-devel gnome-devel-docs
- hdu 2852 树状数组
思路:加一个数e就用update(e,1).删除元素e就用update(e,-1).找比a大的第k大的元素就用二分查找. #include<iostream> #include<cs ...
- PhotoShop—剪贴蒙版
工作中发现剪贴蒙版方便好用,所以简单分享下~ 一.剪贴蒙版有什么作用 1.文字上色 2.裁剪图片 3.添加背景 等 二.剪贴蒙版怎么使用 1.新建层,打上字如图 2.文字上面再建层加上效果如渐变 3. ...