C#建立最簡單的web服務,無需IIS
本程序只是入門級程序,所以不考慮
1,多線程。
2,安全性。
3,不考慮端點下載文件。
4,Keep-Alive。
5,不考慮head。
6,為了簡潔,刪掉了catch的內容。
exe的祖父目錄必須有wwwroot文件夾,且文件夾有index.htm,內容不限。
開發環境: WinXP+VS2010C#
一,新建一個項目TestWeb,項目類型:Windows窗口應用程序。
二,新建類RequestProcessor。
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Diagnostics;
namespace TestWeb
{
class RequestProcessor
{
public bool ParseRequestAndProcess(string[] RequestLines)//解析內容
{
for (int i = 0; i < RequestLines.Length; i++)
System.Diagnostics.Trace.Write(RequestLines[i]);
char[] sp = new Char[1] { ' ' };
string[] strs = RequestLines[0].Split(sp);
if (strs[0] == GET)
{
Send(strs[1], 0, 0);
}
return false;
}
void Send(string filename, long start, long length)//發送文件(文件頭和文件)
{
string strFileName = GetPathFileName(filename);
FileStream fs = null;
try
{
fs = new FileStream(strFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
}
catch (IOException)// FileNotFoundException)
{//不能將 e.Message,發給浏覽器,否則會有安全隱患的
SendHeadrAndStr(打開文件 + filename + 失敗。);
return;
}
if (length == 0)
length = fs.Length - start;
SendHeader(text/html, (fs.Length == length), start, length);
sendContent(fs, start, length);
}
public void SendHeadrAndStr(String str)//直接將str的內容發給html
{
byte[] sendchars = Encoding.Default.GetBytes((str).ToCharArray());
SendHeader(text/html, true, 0, sendchars.Length);
SendStr(Encoding.Default, str);
}
private void SendHeader(string fileType, bool bAll, long start, long length)//發送文件頭
{
try
{
Encoding coding = Encoding.Default;
string strSend;
string strState = (bAll) ? HTTP/1.1 200 OK : HTTP/1.1 206 Partial Content;
SendStr(coding, strState + );
SendStr(coding, Date: );
SendStr(coding, Server: httpsrv/1.0 );
SendStr(coding, MIME-Version: 1.0 );
SendStr(coding, Content-Type: + fileType + );
strSend = Content-Length: + length.ToString();
SendStr(coding, strSend + );
//發送一個空行
SendStr(coding, );
}
catch (ArgumentException)//the request is WRONG
{
}
}
private void sendContent(FileStream fs, long start, long length)//發生文件內容
{
try
{
//報文頭發送完畢,開始發送正文
const int SOCKETWINDOWSIZE = 8192;
long r = SOCKETWINDOWSIZE;
int rd = 0;
Byte[] senddatas = new Byte[SOCKETWINDOWSIZE];
fs.Seek(start, SeekOrigin.Begin);
do
{
r = start + length - fs.Position;
//fs.BeginRead(s,s,s,s,d) 以後使用的版本,用以提高讀取的效率
if (r >= SOCKETWINDOWSIZE)
rd = fs.Read(senddatas, 0, SOCKETWINDOWSIZE);
else
rd = fs.Read(senddatas, 0, (int)r);
mSockSendData.Send(senddatas, 0, rd, SocketFlags.None);
} while (fs.Position != start + length);
}
catch (SocketException e)
{
throw e;
}
catch (IOException e)
{
throw e;
}
}
public Socket mSockSendData;//Notice: get from ClientSocketThread.s
private string GetPathFileName(string filename)
{
const string strDefaultPage = index.htm;
const string strWWWRoot = ..\..\wwwroot\;
string strFileName = String.Copy(filename);
if (/ == strFileName)
strFileName = strDefaultPage;
return System.AppDomain.CurrentDomain.BaseDirectory + strWWWRoot + strFileName;
}
private void SendStr(Encoding coding, string strSend)//發送一個字符串
{
Byte[] sendchars = new Byte[512];
sendchars = coding.GetBytes((strSend).ToCharArray());
mSockSendData.Send(sendchars, 0, sendchars.Length, SocketFlags.None);
}
}
}
三,新建類ClientSocketThread。
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace TestWeb
{
class ClientSocketThread
{
public TcpListener tcpl;//Notice: get from SrvMain.tcpl
private static Encoding ASCII = Encoding.ASCII;
public void HandleThread()
{
Thread currentThread = Thread.CurrentThread;
try
{
Socket s = tcpl.AcceptSocket();
RequestProcessor aRequestProcessor = new RequestProcessor(); //Notice:
aRequestProcessor.mSockSendData = s;//Notice: so that the processor can work
const int BUFFERSIZE = 4096;//that's enough???
Byte[] readclientchar = new Byte[BUFFERSIZE];
char[] sps = new Char[2] { ' ', ' ' };
string[] RequestLines = new string[32];
do
{
//use BUFFERSIZE contral the receive data size to avoid the BufferOverflow attack
int rc = s.Receive(readclientchar, 0, BUFFERSIZE, SocketFlags.None);
string strReceive = ASCII.GetString(readclientchar, 0, rc);
RequestLines = strReceive.Split(sps);
} while (aRequestProcessor.ParseRequestAndProcess(RequestLines));
s.Close();
}
catch (SocketException)
{
}
}
}
}
四,主對話框中增加按鈕,按鍵的相應函數加如下代碼。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace TestWeb
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
//啟動監聽程序
TcpListener tcpl;
IPAddress LocalIP = Dns.Resolve(localhost).AddressList[0];
tcpl = new TcpListener(LocalIP, 80); // listen on port 80
tcpl.Start();
// int ThreadID = 0;
while (true)
{
while (!tcpl.Pending())
{
Thread.Sleep(100);
}
//啟動接受線程
ClientSocketThread myThreadHandler = new ClientSocketThread();
myThreadHandler.tcpl = tcpl;//Notice: dont forget do this
ThreadStart myThreadStart = new ThreadStart(myThreadHandler.HandleThread);
Thread myWorkerThread = new Thread(myThreadStart);
myWorkerThread.Start();
}
}
catch (SocketException )
{
}
catch (FormatException)
{
}
catch (Exception )
{
}
// Console.Read();
}
}
}
五,啟動TestWeb.exe,並單擊主對話框上的按鈕。在浏覽器中輸入:http://127.0.0.1/ 或http://127.0.0.1:80。