当前位置: 首页>后端>正文

rouyi微服务器设置打印sql语句 编写服务器

花了两天的时间搞的这个,写这个东西目的就是要搞清楚ASP.Net的运作原理。

这个山寨服务器的界面很简单,三个文本框,写IP、端口,还有一个显示报文。一个连接按钮。窗体嘛...就叫Form1吧。代码比较冗长...

第一步:

1 //搭建好窗口,为了防止意外,先:
 2  public Form1()
 3  {
 4      Control.CheckForIllegalCrossThreadCalls = false;
 5      InitializeComponent();
 6  }
 7  //全局线程th用于监听,当窗口关闭时,
 8  private void Form1_FormClosing(object sender, FormClosingEventArgs e)
 9  {
10      if (th != null)
11      {
12          th.Abort();
13      }
14  }
15  //另外定义ShowMsg方法:
16  void ShowMsg(string msg)
17  {
18      txtLog.Text += msg + "\r\n";
19  }

第二步:
在线程中进行循环监听,不多解释了,还是Socket那一套(可以看我上一篇博文):

1 Thread th;
 2  private void btnStart_Click(object sender, EventArgs e)
 3  {
 4      IPAddress ip = IPAddress.Parse(txtIp.Text);
 5      IPEndPoint endpoint = new IPEndPoint(ip, int.Parse(txtPort.Text));
 6      Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 7  
 8      try
 9      {
10          socket.Bind(endpoint);
11      }
12      catch (Exception ex)
13      {
14          ShowMsg(ex.Message);
15          return;
16      }
17      socket.Listen(10);
18      ShowMsg("开始运行.....");
19  
20      btnStart.Enabled = false;
21  
22      th = new Thread(Listen);
23      th.IsBackground = true;
24      th.Start(socket);
25  }
26  //监听用的方法
27  void Listen(object o)
28  {
29      Socket socket = o as Socket;
30      while (true)
31      {
32          Socket connect = socket.Accept();
33      DataConnection conn = new DataConnection(connect, ShowMsg);
34      }
35  }

第三步:
由于每次传输完信息连接就可以断开了,所以没必要用循环来接受客户端请求。注意到上面有个叫DataConnection的类,这个类是我们自定义的,为了不让代码显得臃肿。它的构造函数是DataConnection(Socket conn,DelShowMsg del),第一个参数是我们通过监听用的Socket生成的负责传输的Socket,第二个参数是委托,进行报文显示。

1 //首先我们在类外定义回显用的委托:
 2  public delegate void DelShowMsg(string msg);
 3  //下面是这个类的定义:
 4  class DataConnection
 5  {
 6      //定义委托类的对象
 7      private DelShowMsg del;
 8      //以及负责通信的socket
 9      private Socket connection;
10          
11      //之后在构造函数里初始化传进来的Socket和委托
12      public DataConnection(Socket conn,DelShowMsg del)
13      {
14          this.connection = conn;
15          this.del = del;
16  
17          //用一个字符串接收浏览器发来的请求报文
18  //这个方法也是自己写的,解释在后面
19          string msg = RecMsg();
20  
21          //然后解析请求头,这个类还是我们自己写的
22          Request req = new Request(msg);
23  
24          //根据解析后的请求头中的地址,判断请求文件的类型,并向浏览器做出响应
25  //这个方法也是自己写的,为了防止代码臃肿
26          Judge(req.Path);
27      }

(类中的方法还没写完)

第四步:
第三步中我们留下了RecMsg方法、Judge方法和Request类没有写。先来写RecMsg方法。

1 //RecMsg方法很简单,用来接收消息
 2  string RecMsg()
 3  {
 4      //定义缓冲区
 5      byte[] buffer = new byte[1024 * 1024 * 5];
 6      //服务器获取请求报文,并返回长度
 7      int length = connection.Receive(buffer);
 8  
 9      //得到请求报文字符串
10      string msg = System.Text.Encoding.UTF8.GetString(buffer, 0, length);
11      //显示报文
12      del(msg);
13  
14      del("连接关闭");
15      return msg;
16  }

第五步:
第三步中的Request类用来解析请求报文获得请求的路径,原理就是切割字符串。

1 class Request
 2  {
 3      public Request(string msg)
 4      {
 5          //这里根据换换行符切割报文获取每一行
 6          string[] arrLines = msg.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
 7          //然后获取请求行
 8          string[] firstLine = arrLines[0].Split(' ');
 9      //以及各个属性
10          method = firstLine[0];
11          path = firstLine[1];
12          protocol = firstLine[2];
13      }
14  
15      //山寨版服务器很简陋,这里只封装三个属性
16      private string method;
17      public string Method
18      {
19          get { return method; }
20          set { method = value; }
21      }
22  
23      private string path;
24      public string Path
25      {
26          get { return path; }
27          set { path = value; }
28      }
29  
30      private string protocol;
31      public string Protocol
32      {
33          get { return protocol; }
34          set { protocol = value; }
35      }
36  }

第六步:

接下来是第三步在DataConnection类中遗留的Judge方法,它接受上一步Request req = new Request(msg);之后得到的对象中的path属性,也就是地址,进行文件类型的判断。即Judge(req.Path);。

 

rouyi微服务器设置打印sql语句 编写服务器,rouyi微服务器设置打印sql语句 编写服务器_FileStream,第1张

rouyi微服务器设置打印sql语句 编写服务器,rouyi微服务器设置打印sql语句 编写服务器_html_02,第2张

View Code

1 void Judge(string path)
 2  {
 3      //首先拿到地址的扩展名,
 4      string ext = Path.GetExtension(path);
 5      //根据扩展名判断到底是静态页面还是动态页面
 6  //并分别处理
 7      switch (ext)
 8      {
 9          case ".gif":
10          case ".jpg":
11          case ".png":
12          case ".html":
13          case ".htm":
14          case ".css":
15          case ".js":
16              ProcessStaticPage(path);
17              break;
18          case ".aspx":
19          case ".jsp":
20              ProcessDyPage(path);
21              break;
22          default:
23              break;
24      }
25  }

第七步:

ProcessStaticPage和ProcessDyPage是处理静态和动态页面的两个方法,我们要通过服务器返回响应头和响应体。响应体好说,定义一个buffer就行了,问题是响应头很复杂,我们需要定义一个Response类来生成和拿到它,然后再处理ProcessStaticPage和ProcessDyPage这两个函数。这一步就是写Response类。

rouyi微服务器设置打印sql语句 编写服务器,rouyi微服务器设置打印sql语句 编写服务器_FileStream,第1张

rouyi微服务器设置打印sql语句 编写服务器,rouyi微服务器设置打印sql语句 编写服务器_html_02,第2张

View Code

1 void ProcessStaticPage(string path)
 2  {
 3      //首先找到静态文件的绝对路径,擦掉多出来的斜杠
 4      path =AppDomain.CurrentDomain.BaseDirectory + path.Remove(0,1);
 5      //先生成获取响应头的类
 6      Response res = null;
 7      //定义好响应体
 8      byte[] buffer;
 9  
10      //然后判断请求的文件是否存在
11      if (!File.Exists(path))
12      {
13          //如果文件不存在,读取404.html
14          path = AppDomain.CurrentDomain.BaseDirectory + "404.html";
15      //然后把404页面写进响应体
16          using(FileStream fs = new FileStream(path,FileMode.Open))
17          {
18              buffer = new byte[fs.Length];
19              fs.Read(buffer, 0, buffer.Length);
20              res = new Response(404,Path.GetExtension(path), buffer.Length);
21          }
22      }
23      else
24      {
25          //如果文件存在
26          using (FileStream fs = new FileStream(path, FileMode.Open))
27          {
28              buffer = new byte[fs.Length];
29              fs.Read(buffer, 0, buffer.Length);
30              res = new Response(Path.GetExtension(path), buffer.Length);
31          }
32      }
33      //发送响应头
34      connection.Send(res.GetHeaders());
35      //发送响应体
36      connection.Send(buffer);
37      //关闭连接
38      connection.Close();
39  }

第八步:

现在该写ProcessStaticPage和ProcessDyPage这两个方法来处理静态和动态页面了。先弄静态的。

rouyi微服务器设置打印sql语句 编写服务器,rouyi微服务器设置打印sql语句 编写服务器_FileStream,第1张

rouyi微服务器设置打印sql语句 编写服务器,rouyi微服务器设置打印sql语句 编写服务器_html_02,第2张

View Code

1 void ProcessStaticPage(string path)
 2  {
 3      //首先找到静态文件的绝对路径,擦掉多出来的斜杠
 4      path =AppDomain.CurrentDomain.BaseDirectory + path.Remove(0,1);
 5      //先生成获取响应头的类
 6      Response res = null;
 7      //定义好响应体
 8      byte[] buffer;
 9  
10      //然后判断请求的文件是否存在
11      if (!File.Exists(path))
12      {
13          //如果文件不存在,读取404.html
14          path = AppDomain.CurrentDomain.BaseDirectory + "404.html";
15      //然后把404页面写进响应体
16          using(FileStream fs = new FileStream(path,FileMode.Open))
17          {
18              buffer = new byte[fs.Length];
19              fs.Read(buffer, 0, buffer.Length);
20              res = new Response(404,Path.GetExtension(path), buffer.Length);
21          }
22      }
23      else
24      {
25          //如果文件存在
26          using (FileStream fs = new FileStream(path, FileMode.Open))
27          {
28              buffer = new byte[fs.Length];
29              fs.Read(buffer, 0, buffer.Length);
30              res = new Response(Path.GetExtension(path), buffer.Length);
31          }
32      }
33      //发送响应头
34      connection.Send(res.GetHeaders());
35      //发送响应体
36      connection.Send(buffer);
37      //关闭连接
38      connection.Close();
39  }

第九步:

处理动态页面麻烦一些,因为我们要根据请求的文件名来找对应同名的类,所以要用到反射技术。

rouyi微服务器设置打印sql语句 编写服务器,rouyi微服务器设置打印sql语句 编写服务器_FileStream,第1张

rouyi微服务器设置打印sql语句 编写服务器,rouyi微服务器设置打印sql语句 编写服务器_html_02,第2张

View Code

1 void ProcessDyPage(string path)
 2  {
 3      //根据请求的文件名,创建对应的类的对象
 4  //获得文件名
 5      string fileName = Path.GetFileNameWithoutExtension(path);
 6      //获得类所在的命名空间
 7      string nameSpace = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Namespace;
 8      //获得类的全名称
 9      string fullName = nameSpace + "." + fileName;
10  
11      //注意这里,IHttpHandler是个接口,这里用了李氏替换原则
12  //保证实现了这个接口的类都能处理http请求
13      IHttpHandler hander = Assembly.GetExecutingAssembly().CreateInstance(fullName,true) as IHttpHandler;
14  
15      if (hander != null)
16      {
17        //用ProcessRequest方法处理请求
18          byte[] buffer = hander.ProcessRequest();
19          Response response = new Response(Path.GetExtension(path), buffer.Length);
20  
21          connection.Send(response.GetHeaders());
22          connection.Send(buffer);
23  
24          connection.Close();
25      }
26      else
27      {
28          //处理404,不写了
29      }
30  }

第十步:

上面有一个IHttpHandler接口,接口里面有一个byte[] ProcessRequest();方法,因为这里是用反射去找字符串对应的同名类名的,这里之所以不写判断逻辑或者简单工厂,是因为一旦判断,我每增加一个页面都要改一次代码,所以采用反射机制。接口是在动态页面对应的类中实现的,里面就是在拼html代码。比如:

1 class MyPage : IHttpHandler
 2  {
 3      public byte[] ProcessRequest()
 4      {
 5          StringBuilder sb = "<html><body>";
 6          sb.Append("当前时间:" + DateTime.Now.ToString());
 7          sb.Append("</body></html>");
 8          string html = sb.ToString();
 9          return Encoding.UTF8.GetBytes(html);
10      }
11  }

 




https://www.xamrdz.com/backend/36m1926511.html

相关文章: