Dass man in .NET C# auch multithreaded HTTP Webserver schreiben kann, dürfte jedem klar sein.
Hier mal ein Klassenbeispiel, das automatisch einen freien Port ermittelt und den NAN_Webserver als Instant Webserver startet.
Wahlweise können PHP, Perl, etc als CGI-ausführende Programme deaklariert werden samt Dateitypen, sowie das Home-Verzeiichnis, und zwar alles direkt aus dem GUI-Designer heraus.
/*
- Erstellt mit SharpDevelop.
- Author: Holger Schadeck
- Web: https://www.schadeck.eu
*/
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Diagnostics;
using System.Threading;
using System.Collections;
using System.IO;
namespace libHSchadeck
{
public class MimeTypeMap {
public string Extension {
get { return extension; }
set { extension = value; }
}
public string MimeType {
get { return mimeType; }
set { mimeType = value; }
}
public bool IsBinary {
get { return isBinary; }
set { isBinary = value; }
}
private string extension;
private string mimeType;
private bool isBinary;
}
public class CgiExtension {
public string Extension {
get { return extension; }
set { extension = value; }
}
public string ServerSubExecPath {
get { return serverSubExecPath; }
set { serverSubExecPath = value; }
}
private string extension;
private string serverSubExecPath;
}
/// <summary>
/// Description of NANWebserver.
/// </summary>
[SerializableAttribute]
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
public class NANWebserver : System.Windows.Forms.UserControl
{
private TcpListener listener;
private IPAddress listenAddress = IPAddress.Parse("127.0.0.1");
private int listenPortBase = 80;
private int listenPort = 80;
private string pathServer = "server";
private string pathServerSubHome = "web";
private string[] clientIps = new string[1];
private string[] listenHosts = new string[2];
private CgiExtension[] cgiExtensions = new CgiExtension[0];
private Hashtable mimeMaps = new Hashtable();
private Process serverProcess;
private bool closeNow = false;
public CgiExtension[] CgiExtensions {
get { return cgiExtensions; }
set { cgiExtensions = value; }
}
public int ListenPortBase {
get { return listenPortBase; }
set { listenPortBase = value; }
}
public string PathServer {
get { return pathServer; }
set { pathServer = value; }
}
public string PathServerSubHome {
get { return pathServerSubHome; }
set { pathServerSubHome = value; }
}
public string[] ClientIps {
get { return clientIps; }
set { clientIps = value; }
}
public string[] ListenHosts {
get { return listenHosts; }
set { listenHosts = value; }
}
public NANWebserver()
{
this.init();
}
private void init() {
this.clientIps[0] = "all";
this.listenHosts[0] = "127.0.0.1";
this.listenHosts[1] = "localhost";
this.addMimeTable("ai:1:application/postscript;aif:1:audio/x-aiff;aiff:1:audio/x-aiff;asc:0:text/plain;au:1:audio/basic;avi:1:video/x-msvideo;bin:1:application/octet-stream;bmp:1:image/bmp;css:1:text/css;doc:1:application/msword;dtd:0:application/xml-dtd;eps:1:application/postscript;exe:1:application/octet-stream;gif:1:image/gif;htm:0:text/html;html:0:text/html;jpe:1:image/jpeg;jpeg:1:image/jpeg;jpg:1:image/jpeg;js:0:application/x-javascript;mid:1:audio/midi;midi:1:audio/midi;mov:1:video/quicktime;mp2:1:audio/mpeg;mp3:1:audio/mpeg;mpe:1:video/mpeg;mpeg:1:video/mpeg;mpg:1:video/mpeg;mpga:1:audio/mpeg;pdf:1:application/pdf;png:1:image/png;ppt:1:application/vnd:ms-powerpoint;ps:1:application/postscript;qt:1:video/quicktime;ra:1:audio/x-realaudio;ram:1:audio/x-pn-realaudio;rm:1:audio/x-pn-realaudio;rpm:1:audio/x-pn-realaudio-plugin;snd:1:audio/basic;svg:1:image/svg:xml;swf:1:application/x-shockwave-flash;tif:1:image/tiff;tiff:1:image/tiff;txt:0:text/plain;wav:1:audio/x-wav;wml:0:text/vnd:wap:wml;xls:1:application/vnd:ms-excel;xml:0:application/xml;xsl:0:application/xml;xslt:0:application/xslt:xml;xul:1:application/vnd:mozilla:xul:xml;zip:1:application/zip");
}
public void addMimeTable(string table) {
foreach(string line in table.Split(';')) {
string[] cols = line.Trim().Split(':');
this.addMimeType(cols[0],cols[2],((cols[1] == "1") ? true : false));
}
}
public bool removeMimeType(string extension) {
if(this.existMimeType(extension)) {
this.mimeMaps.Remove(extension);
return true;
} else {
return false;
}
}
public bool existMimeType(string extension) {
return this.mimeMaps.ContainsKey(extension);
}
public void addMimeType(string extension, string mimeType, bool binary) {
if(this.existMimeType(extension)) {
this.removeMimeType(extension);
}
MimeTypeMap map = new MimeTypeMap();
map.Extension = extension;
map.MimeType = mimeType;
map.IsBinary = binary;
this.mimeMaps.Add(extension, map);
}
private void writeTypes() {
string file = this.pathServer+"types.txt";
if(File.Exists(file)) {
File.Delete(file);
}
string content = "";
foreach(MimeTypeMap map in this.mimeMaps.Values) {
content += map.Extension+"t"+((map.IsBinary == true) ? "1" : "0") + "t" + map.MimeType+Environment.NewLine;
}
File.WriteAllText(file, content);
}
private void writeSettings() {
string file = this.pathServer+"settings.txt";
if(File.Exists(file)) {
File.Delete(file);
}
string content = "";
content += "port="+this.listenPort+Environment.NewLine;
content += "docroot="+this.pathServerSubHome+Environment.NewLine;
foreach(string host in this.listenHosts) {
content += "hostname="+host+Environment.NewLine;
}
foreach(string client in this.clientIps) {
content += "clientip="+client+Environment.NewLine;
}
foreach(CgiExtension ext in this.cgiExtensions) {
content += ext.Extension+"="+ext.ServerSubExecPath+Environment.NewLine;
}
File.WriteAllText(file, content);
}
public int Start() {
int port = this.tryStart(this.listenPortBase);
this.listenPort = port;
if(!this.pathServer.Contains(":")) {
this.pathServer = Environment.CurrentDirectory+""+this.pathServer;
}
this.writeTypes();
this.writeSettings();
this.startServer();
return port;
}
public void Stop() {
this.closeNow = true;
try {
this.serverProcess.Kill();
} catch(Exception e) {
Trace.WriteLine(e.Message);
}
try {
string file = this.pathServer+"settings.txt";
File.Delete(file);
} catch {
}
try {
string file = this.pathServer+"types.txt";
File.Delete(file);
} catch {
}
}
private void startServer() {
if(!this.closeNow) {
this.serverProcess = new Process();
this.initServer();
this.serverProcess.Start();
}
}
private void initServer() {
string server = this.pathServer + "nanweb.exe";
this.serverProcess.StartInfo.WorkingDirectory = this.pathServer;
this.serverProcess.StartInfo.FileName = server;
this.serverProcess.StartInfo.Arguments = "-s -m -bc=0";
this.serverProcess.StartInfo.CreateNoWindow = true;
this.serverProcess.EnableRaisingEvents = true;
this.serverProcess.StartInfo.WindowStyle= ProcessWindowStyle.Hidden;
this.serverProcess.StartInfo.UseShellExecute = false;
this.serverProcess.Disposed+= delegate(object sender, EventArgs e) {
if(!this.closeNow) {
this.startServer();
}
};
this.serverProcess.Exited+= delegate(object sender, EventArgs e) {
if(!this.closeNow) {
this.startServer();
}
};
}
private int tryStart(int port) {
try {
this.listener = new TcpListener(this.listenAddress, port) ;
listener.Start();
listener.Stop();
return port;
} catch {
return this.tryStart(port+1);
}
}
}
}
Nach Zuweisung der Werte im GUI-Editor beispielsweise folgenden Code für Formular einfügen:
void MainFormLoad(object sender, EventArgs e)
{
int port = this.nanWebserver1.Start();
this.webBrowser1.Url = new Uri("http://127.0.0.1:"+port+"/index.php");
}
void MainFormFormClosed(object sender, FormClosedEventArgs e)
{
this.nanWebserver1.Stop();
}