有了以下的知识: ASP.NET那点不为人知的事(一) ASP.NET那点不为人知的事(二)
想必开发一个小型服务器以不是问题了,功能补复杂,能够响应客户端浏览器的请求,并根据请求文件的类型返回响应的信息,如能处理静态页面、图片、样式、脚本、动态页面等。
由于客户端和服务端的通信是通过Socket通信,且它们通信的“语言”是基于Http1.1协议。根据这个线索,我们完全可以自己开发服务器软件,暂且叫他Melodies Server,当然这是一个很简单的样例,和真正的服务器还是有差距的,好,我们进入正题,首先需要了解以下几个知识点:
public partial class WebServerForm : Form
{
public WebServerForm()
{
InitializeComponent();
TextBox.CheckForIllegalCrossThreadCalls = false;
}
private Socket socketWatch;//负责监听浏览器连接请求的套接字
private Thread threadWatch;//负责循环调用Socket.Accept 监听线程
private void btnStartServer_Click(object sender, EventArgs e)
{
socketWatch=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
IPAddress address = IPAddress.Parse(txtIPAddress.Text.Trim());
IPEndPoint endPoint=new IPEndPoint(address,int.Parse(txtPort.Text.Trim()));
socketWatch.Bind(endPoint);
socketWatch.Listen(10);
threadWatch = new Thread(WatchConnect);
threadWatch.IsBackground = true;
threadWatch.Start();
}
private bool isWatch = true;
//Dictionary<>
void WatchConnect()
{
while (isWatch)
{
Socket socketConnection=socketWatch.Accept();
ShowMsg("浏览器:"+socketConnection.RemoteEndPoint.ToString()+",连接成功*********************");
ConnectionClient connectionClient = new ConnectionClient(socketConnection, ShowMsg);
}
}
void ShowMsg(string msg)
{
txtLog.AppendText(msg+"\r\n");
}
}
/// <summary>
/// 与客户端连接通信类(包含一个与客户端通信的套接字和通信线程)
/// </summary>
public class ConnectionClient
{
private Socket socketMsg;//与客户端通信套接字
private Thread threadMsg;//通信线程
private DGShowMsg dgShowMsg;//负责向主窗体文本框显示消息的委托
public ConnectionClient(Socket socket,DGShowMsg dgShowMsg)
{
this.socketMsg = socket;
this.dgShowMsg = dgShowMsg;
//负责启动一个接受客户端浏览器请求报文的线程
threadMsg = new Thread(ReceiveMsg);
threadMsg.IsBackground = true;
threadMsg.Start();
}
private bool isRec = true;
void ReceiveMsg()
{
while (isRec)
{
byte[] arrMsg=new byte[1024*1024*3];
//接受对应客户端发送过来的请求报文
int length = socketMsg.Receive(arrMsg);
string strMsg = System.Text.Encoding.UTF8.GetString(arrMsg, 0, length);
dgShowMsg(strMsg);
//处理报文
string[] arrStr = strMsg.Replace("\r\n", "韘").Split('韘');
string[] firstRow=arrStr[0].Split(' ');
string requestFile = firstRow[1];
ExcuteRequest(requestFile);//todo:长连接多少时间
}
}
private void ExcuteRequest(string requestFile)
{
//获得被请求页面的后缀名
string fileExtension = System.IO.Path.GetExtension(requestFile);
if (!string.IsNullOrEmpty(fileExtension))
{
switch (fileExtension.ToLower())
{
case ".html":
case ".htm":
case ".css":
case ".js":
ExcuteStaticPage(requestFile,fileExtension);
break;
case ".jpg":
ExcuteImg(requestFile,fileExtension);
break;
case ".aspx":
ExcuteDymPage(requestFile,fileExtension);
break;
}
}
}
/// <summary>
/// 处理静态页面,直接输出
/// </summary>
private void ExcuteStaticPage(string requestPath,string fileExtension)
{
StringBuilder sb=new StringBuilder();
//获得请求文件的文件夹的物理路径
string dataDir = AppDomain.CurrentDomain.BaseDirectory;
if (dataDir.EndsWith(@"\bin\Debug\") || dataDir.EndsWith(@"\bin\Release\"))
{
dataDir = System.IO.Directory.GetParent(dataDir).Parent.Parent.FullName;
}
string phyPath = dataDir + requestPath;
//读取静态页面内容
string fileContent = System.IO.File.ReadAllText(phyPath);
//获得响应体字节数组
byte[] fileArr = System.Text.Encoding.UTF8.GetBytes(fileContent);
//获得响应报文头:
string responseHeader = GetResponseHeader(fileArr.Length, fileExtension);
byte[] arrHead = System.Text.Encoding.UTF8.GetBytes(responseHeader);
//发送响应报文头回浏览器
socketMsg.Send(arrHead);
//发送响应报文体回浏览器
//todo:sleep 1分钟会怎样
socketMsg.Send(fileArr);
}
/// <summary>
/// 处理图片
/// </summary>
/// <param name="requestPath"></param>
/// <param name="extentionName"></param>
private void ExcuteImg(string requestPath, string extentionName)
{
//获得请求文件的文件夹的物理路径
string dataDir = AppDomain.CurrentDomain.BaseDirectory;
if (dataDir.EndsWith(@"\bin\Debug\") || dataDir.EndsWith(@"\bin\Release"))
{
dataDir = System.IO.Directory.GetParent(dataDir).Parent.Parent.FullName;
}
//获得请求文件的物理路径(绝对路径)
string phyPath = dataDir + requestPath;
int imgLength;
byte[] fileArr;
//读取图片内容
using (FileStream fs = new FileStream(phyPath, FileMode.Open))
{
fileArr = new byte[fs.Length];
imgLength = fs.Read(fileArr, 0, fileArr.Length);
//获得响应报文头
string responseHeader = GetResponseHeader(imgLength, extentionName);
byte[] arrHeader = System.Text.Encoding.UTF8.GetBytes(responseHeader);
socketMsg.Send(arrHeader);
socketMsg.Send(fileArr, imgLength, SocketFlags.None);
}
}
/// <summary>
/// 得到响应头信息
/// </summary>
/// <param name="contentLength"></param>
/// <param name="fileExtentionName"></param>
/// <returns></returns>
private string GetResponseHeader(int contentLength,string fileExtentionName)
{
StringBuilder sbHeader=new StringBuilder();
sbHeader.Append("HTTP/1.1 200 OK\r\n");
sbHeader.Append("Content-Length: "+contentLength+"\r\n");
sbHeader.Append("Content-Type:" + GetResponseHeadContentType(fileExtentionName) + ";charset=utf-8\r\n\r\n");
return sbHeader.ToString();
}
/// <summary>
/// 根据后缀名获取响应保文中的内容类型
/// </summary>
/// <param name="fileExtentionName"></param>
/// <returns></returns>
private string GetResponseHeadContentType(string fileExtentionName)
{
switch (fileExtentionName.ToLower())
{
case ".html":
case ".htm":
case ".aspx":
return "text/html";
break;
case ".css":
return "text/plain";
break;
case ".js":
return "text/javascript";
break;
case ".jpg":
return "image/JPEG";
case ".gif":
return "image/GIF";
break;
default:
return "text/html";
break;
}
}
public class View:IHttpHandler
{
public string ProcessRequest()
{
string dataDir = AppDomain.CurrentDomain.BaseDirectory;
//获得模板物理路径
if (dataDir.EndsWith(@"\bin\Debug\") || dataDir.EndsWith(@"\bin\Release"))
{
dataDir = System.IO.Directory.GetParent(dataDir).Parent.Parent.FullName;
}
string phyPath = dataDir + "/model.htm";
string modelContent=System.IO.File.ReadAllText(phyPath);
modelContent = modelContent.Replace("@Title", "动态页面").Replace("@Content", "反射创建页面类");
return modelContent;
}
}
/// <summary>
/// 反射创建动态页面对象
/// </summary>
/// <param name="requestFile"></param>
/// <param name="extentionName"></param>
private void ExcuteDymPage(string requestFile, string extentionName)
{
string pageClassName = System.IO.Path.GetFileNameWithoutExtension(requestFile);
string assemblyName = Assembly.GetExecutingAssembly().GetName().Name;
//获得页面类全名称
pageClassName = assemblyName + "." + pageClassName;
//通过反射创建页面类对象
object pageObj = Assembly.GetExecutingAssembly().CreateInstance(pageClassName);
IHttpHandler page = pageObj as IHttpHandler;
byte[] fileArr=null;
if (page!=null)
{
string strHtml=page.ProcessRequest();
fileArr= System.Text.Encoding.UTF8.GetBytes(strHtml);
}
//获得响应报文头
string responseHeader = GetResponseHeader(fileArr.Length, extentionName);
byte[] arrHeader = System.Text.Encoding.UTF8.GetBytes(responseHeader);
socketMsg.Send(arrHeader);
socketMsg.Send(fileArr);
}
至此,一个小型的服务器软件就构建好了,赶紧测试一下呵呵。