HTTP是一个客户端和服务器端请求和应答的标准(TCP)。
服务器,使用Socket监听指定的端口,等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。
客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;
会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个1024以上的端口。
这个小程序就是,为了深入了解浏览器与服务器使用http协议通信的过程,当然真正的服务器肯定比这复杂,这里只是测试学习。
1、启动服务器的时候,服务器端就会新建一个监听套接字, 根据指定的IP地址和端口号 绑定一个网络节点,然后新建一个线程循环监听 浏览器请求
2、当有浏览器 发出连接请求的时候,将新建一个通信套接字负责与浏览器通信
3、根据浏览器发来的请求信息,生成相应的相应报文发送回浏览器
服务器可以从请求报文头中解析出 浏览器请求的页面信息,
若为静态的页面或资源 如 :html、htm、图片、css、js等等的话就 直接将请求内容 根据http协议添加报文头后发送给浏览器
若为动态资源 如:aspx、php、jsp等页面,将会通过做相应的 函数的执行以后再生成静态资源发送给浏览器
如图,浏览器请求test.htm页面,页面中包括图片和文字:
关键过程:
启动服务器:
//首先获取IP地址和端口,定义网络节点
//然后定义监听请求的套接字,将其绑定到该网络节点
IPAddress ipAddress=IPAddress.Parse("127.0.0.1");
int port=50000;
ipAddress = IPAddress.Parse(txtServerIP.Text.Trim());
port=int.Parse(txtServerPort.Text.Trim());
IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, port);
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
socketWatch.Bind(ipEndPoint);
socketWatch.Listen(20);//设置监听队列,服务器能处理的请求数目
//定义独立线程来监听连接请求
threadWatch = new Thread(new ThreadStart(WatchConnection));
threadWatch.IsBackground = true;//设置为后台线程
threadWatch.Start();
ShowMsg("服务器启动成功!");
监听连接请求:
//判断监听循环是否退出
bool isRunning = true;
///<summary>
/// 监听浏览器连接请求
///</summary>
void WatchConnection()
{
while (isRunning)
{
//接收浏览器请求,并创建与浏览器通信的套接字
Socket soketCommunication=socketWatch.Accept();
ShowMsg("浏览器连接成功:"+soketCommunication.RemoteEndPoint.ToString());
//定义自定义数据通信对象,
//并将 发出请求浏览器的套接字和 在消息框显示消息的方法传进去
//剩下的通信工作就交给该对象内部去处理
DataCommunication dataCommu = new DataCommunication(soketCommunication, ShowMsg);
}
}
上面的DataCommunication类专门用来处理与浏览器的通信 里面的关键过程有:
接收请求信息:
void ReceiveMsg()
{
//缓冲数组
byte[] arryRequest = new byte[1024 * 1024 * 2];
try
{
int length = soketCommunication.Receive(arryRequest);
string strRequset = System.Text.Encoding.UTF8.GetString(arryRequest, 0, length);
HttpRequestService requestService = new HttpRequestService(strRequset);
JudgeFileType(requestService);
showMsg("请求信息:\r\n" + strRequset);
}
catch (SocketException ex)
{
showMsg("异常信息:" + ex.ToString());
}
catch (Exception ex)
{
showMsg("异常信息:" + ex.ToString());
}
}
判断请求文件类型:
///<summary>
/// 判断请求文件的类型,然后采取不同的操作进行处理
///</summary>
///<param name="requestService"></param>
void JudgeFileType(HttpRequestService requestService)
{
//获取请求文件的物理路径
string dataDir = AppDomain.CurrentDomain.BaseDirectory;
if (dataDir.EndsWith(@"\bin\Debug\") || dataDir.EndsWith(@"\bin\Release\"))
{
dataDir = Directory.GetParent(dataDir).Parent.Parent.FullName;
}
string filePath = dataDir + requestService.Path;
//根据后缀名 判断请求文件的类型
switch (Path.GetExtension(requestService.Path))
{
case ".html":
case ".htm":
case ".css":
case ".js":
case ".jpg":
case ".gif":
case ".png":
{
ProcessStaticPage(filePath);
break;
}
case ".aspx":
case ".jsp":
case ".php":
{
ProcessDynamicPage(filePath);
break;
}
}
}
处理静态资源:
///<summary>
/// 处理静态的资源,将指定路径的文件 封装成 响应包发给浏览器
///</summary>
void ProcessStaticPage(string path)
{
using (FileStream fs = new FileStream(path, FileMode.Open))
{
byte[] fileContent = new byte[fs.Length];
fs.Read(fileContent, 0, fileContent.Length);
HttpResponseService responseSevice=new HttpResponseService(fileContent,Path.GetExtension(path));
soketCommunication.Send(responseSevice.GetResponseHeader());
soketCommunication.Send(fileContent);
}
}
处理动态资源:
///<summary>
/// 处理动态文件,将指定路径的文件 封装成 响应包发给浏览器
///先通过反射来找到请求的页面要执行的程序类并动态创建该类的对象,
///然后执行该类里面相应的方法,获取html代码
///</summary>
void ProcessDynamicPage(string path)
{
string className = Path.GetFileNameWithoutExtension(path);
string nameSpaceName = Assembly.GetExecutingAssembly().GetName().Name;
string fullClassName = nameSpaceName + "." + className;
object pageObj = Assembly.GetExecutingAssembly().CreateInstance(fullClassName);
DynamicPageTest page = pageObj as DynamicPageTest;
byte[] arryFile = System.Text.Encoding.Default.GetBytes(page.ProcessRequest());
HttpResponseService responseSevice = new HttpResponseService(arryFile, Path.GetExtension(path));
soketCommunication.Send(responseSevice.GetResponseHeader());
soketCommunication.Send(arryFile);
}
接收请求信息中的HttpRequestService类负责对请求信息做解析,这里仅取出其请求文件path:
public HttpRequestService(string requestMsg)
{
string[] arryRequest = requestMsg.Replace("\r\n", "傂").Split('傂');
string[] firstRow = arryRequest[0].Split('');
Path = firstRow[1];
}
做出http相应 首先生成报文头,这里只是报文头部分:
///<summary>
/// 获取响应文件头
///</summary>
public byte[] GetResponseHeader()
{
string header = "HTTP/1.1 200 OK\r\nContent-Length: "
+ arryResponse.Length + "\r\nContent-Type: " + contentType + ";charset=GB2312\r\n\r\n";
return System.Text.Encoding.UTF8.GetBytes(header);
}
这样基本上就可以了,请求效果如图: