目录

1. C# Socket通讯

2. HTTP 解析引擎

3. 资源读取和返回

4. 服务器测试和代码下载

  Web服务器是Web资源的宿主,它需要处理用户端浏览器的请求,并指定对应的Web资源返回给用户,这些资源不仅包括HTML文件,JS脚本,JPG图片等,还包括由软件生成的动态内容。为了满足上述需求,一个完整的Web服务器工作流程:

  1) 服务器获得浏览器通过TCP/IP连接向服务器发送的http请求数据包。

  2) HTTP请求经过Web服务器的HTTP解析引擎分析得出请求方法、资源地址等信息,然后开始处理。

  3) 对于静态请求,则在服务器上查询请求url路径下文件,并返回(如果未找到则返回404 No Found)。

  4) 涉及动态请求,如CGI, AJAX, ASP等,则根据http方法,采取不同处理。

  

  Web服务器的核心由C# Socket通讯,http解析引擎,静态资源文件查找,动态数据接收和发送4部分组成,本节因为个人编写进度原因主要实现前3个部分(即能够查询静态资源的Web服务器),动态数据处理因为涉及的处理方式CGI,AJAX,ASP的方法不同,后续完成后在总结相关知识。

1. C# Socket通讯 

  C# Socket通过对TCP/IP协议进行封装,用于实现满足TCP通讯的API。在B/S架构中,服务器端的处理和C/S连接基本相同,主要工作包含:创建Socket套接字,监听连接,建立连接,获得请求,处理并返回数据,关闭连接等。

  程序入口函数,采用轮询方式实现对客户端请求的监听。

          //创建监听线程Thread Listen_thread = new Thread(socket_listen);Listen_thread.IsBackground = false;Listen_thread.Start();        

  监听线程,创建Socket套接字,绑定并监听指定端口,等待连接建立,连接建立后,考虑到网页请求高并发的特性,采用另开线程的方式来处理建立的连接,从而实现并发服务器模式。

            Socket server_socket = null;//监听的IP地址和端口 作为服务器,绑定的只能是本机Ip地址或者环回地址(不能与系统其它进程端口冲突)//如果绑定为本节IP地址,局域网下其它设备可以通过http://host:port来访问当前服务器string host = "127.0.0.1";int port = 3000;IPAddress ip = IPAddress.Parse(host);IPEndPoint ipe = new IPEndPoint(ip, port);//新建Socket套接字,绑定在指定的端口并开始监听server_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);server_socket.Bind(ipe);server_socket.Listen(100);Console.WriteLine("Server Binding at " + host + ":" + port.ToString() +"...");Console.WriteLine("Wait for connect....");while (true){Socket CurrentSocket;//三次握手成功,新建一个连接CurrentSocket = server_socket.Accept();Console.WriteLine("New TCP Socket Create...");//单开一个线程用来处理服务器收发, 并发服务器模式ParameterizedThreadStart tStart = new ParameterizedThreadStart(socket_process);Thread process_thread = new Thread(tStart);process_thread.IsBackground = false;process_thread.Start(CurrentSocket);}

  连接处理,socket通讯处理主要负责接收连接产生的数据,并将http引擎处理后数据提交给客户端浏览器。

          Socket CurrentSocket = (Socket)obj;try{string recvStr = "";byte[] recvBytes = new byte[2000];int length;//获得当前Socket连接传输的数据,并转换为ASCII码格式length = CurrentSocket.Receive(recvBytes, recvBytes.Length, 0);recvStr = Encoding.ASCII.GetString(recvBytes, 0, length);//http引擎处理,返回获得数据byte[] bs =  http_engine(recvStr, length);//通过socket发送引擎处理后数据CurrentSocket.Send(bs, bs.Length, 0);Console.WriteLine("File Send Finish, Socket Close....\r\n");//关闭socket连接CurrentSocket.Close();}catch (Exception exception){Console.WriteLine(exception);CurrentSocket.Close();}

  C# Socket通讯架构的实现和C/S结构没有什么区别,如果了解过Socket可以轻松实现上述socket通讯架构。 不过下面这部分将讲述Web服务器的实现核心--http解析引擎,这也是B/S架构和C/S架构中服务器端最大的区别。

2. http解析引擎

  Web服务器主要实现对浏览器请求数据包的处理,并返回指定的http资源或者数据,这些都是由http解析引擎实现,在写http解析引擎之前,我们要知晓接收到的数据才能进行后续的处理,这里提供通过WireShark抓取的http请求包:

  虽然HTTP请求包的内容很多,但因为目前实现的功能较少,所以关注的只有http报文起始行就可以,而首部字段可以直接丢弃不处理,后续如果使用认证机制,如白名单,黑名单过滤,帐号/密码保护,资源权限管理等,首部仍然要处理。

  对于http报文起始行, 内部以space隔开,并以'\r\n'作为结尾与首部隔开。其中GET:HTTP方法, '/' :资源路径url, HTTP/1.1:协议版本,参照http权威指南的内容,  HTTP协议的常见方法有GET, PUT, DELETE, POST, HEAD这5种,本节中的静态服务器主要涉及到GET方法。了解了需要如何解析HTTP请求报文后,我们先定义一个HTTP报文解析结构,用于存储到解析的信息。

        public class HTTPPrase{//http方法public string http_method;//http资源public string url;//http版本号public string version;//url解析的请求网页类型public string type;};

  下面我们就要开始利用C#提供的String方法来截取http报文来实现上述结构体内参数的初始化。

            int pos;//根据\r\n截断,获取http报文首部并转换为小写,方便后续处理//Get / HTTP/1.1/r/npos = str.IndexOf("\r\n");string str_head = str.Substring(0, pos);str_head = str_head.ToLower();//根据' '来截断起始行,并赋值给对应参数string[] arr = Regex.Split(str_head, @"\s+");HTTPServer.HTTPPrase http_head = new HTTPServer.HTTPPrase();http_head.http_method = arr[0];      // "Get"http_head.url = arr[1];              // "/"http_head.version = arr[2];          // "HTTP/1.1"//判断是否有通过ajax要求获得或者提交的动态数据 http_head.ajax_status = str_head.IndexOf(".ajax") != -1 ? true : false;byte[] bs = http_head.ajax_status == true ? ajax_process(http_head, str) : static_process(http_head, str);return bs;

  下面就可以把数据提交给后端接口,进行处理。因为动态网页处理需要网页端和后端相互的配合,工作量较大,因此本节主要阐述静态网页请求的实现。

3. 资源读取和返回

  局域网Web请求一般是通过ip+port的模式直接访问服务器端,所以第一个接收到的请求的url为‘/',这时我们需要将它映射到服务端定位的访问主页,目前设置为index.html,对于其它请求,url的值一般是'/xxx/xxx.js", '/xxx/xxx.jpg"等,而在服务器中读取时我们需要定义绝对地址,所以还要在前面添加资源存储的根地址,目前将程序当前所在文件夹+html作为资源的根地址,而且操作系统存储的数据路径为\xxx\xxx.js,所以对于请求中url数据还要替换为'\\'(为了保证转义符能够转变为路径符,需要用'\\'表示实际的'\'),此外为了后续的http响应报文中返回正确的Content-Type字段,还有截取'.'后字段,来获取请求文件的类型。

              //获得当前程序所在的文件夹string url_str = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase;if (string.Compare(head.url, "/") == 0){//对于首个请求127.0.0.1:3000/ 返回index.htmlurl_str += "html\\index.html";head.type = "html";}else{//其它请求 如/spring.js 替换为 ...\html\spring.js便于C#查询文件路径url_str =url_str + "html\\" + head.url.Substring(1);url_str = url_str.Replace('/', '\\');int pos = url_str.IndexOf('.');//获得当前请求的网页类型 head.type = url_str.Substring(pos + 1);}

  到此为止,完成了整个http解析的过程,包括http方法, url资源地址获得并转换为windows系统路径,协议版本获得三个部分。对于静态网页请求,后续就比较简单,查询系统路径下资源,通过文件流打开,并以字符流的形式放置在内存中,作为http响应报文的正文部分。

             //以文件流的方式打开指定路径内文件using (FileStream fs = new FileStream(url_str, FileMode.Open, FileAccess.Read)){//StreamReader temp = new StreamReader(fs, Encoding.Default);int fslen = (int)fs.Length;byte[] fbyte = new byte[fslen];int r = fs.Read(fbyte, 0, fslen);fs.Close();   //......}    

文件打开成功后,我们就要生成http响应报文了,http响应报文和请求报文相同,也由三部分构成。

  状态码:主要为客户端提供一种理解事务处理结果的便捷方式。主要实现的有:

  HTTP/1.1 200 OK 请求没有问题,实体的主体部分包含请求的资源

HTTP/1.1 400 Bad Request 通知客户端它发送了一个错误的请求

  HTTP/1.1 401 Unauthorized 与适当的首部一同返回,通知客户端进行相应的认证

HTTP/1.1 404 No Found 说明服务器无法找到请求的URL

 响应首部:为客户端提供额外的关于服务器的消息,本项目中实现比较简单:

  Content-type:CurrentType\r\n

  Server:C# Web\r\n

  Content-Length:CurrentLength\r\n

  Connection: close

 其中Contenet-type需要根据我们上文获得的type类型来替换,这里阐述常见的替换规则。

Content-Length字段是http响应报文正文的长度,即我们获得资源的总长度(上文中fslen), 最后将状态码,响应首部和正文数据整合在一起通过socket发送到客户端,就实现了静态服务器的全部过程。

                            string HTTP_Current_Head = HTTPServer.HTTP_OK_Head.Replace("CurrentLength", Convert.ToString(fslen));//根据不同url需要返回不同的首部类型 具体对比详见http://tool.oschina.net/commonsswitch (head.type){case "jpg":HTTP_Current_Head = HTTP_Current_Head.Replace("CurrentType", "application/x-jpg");break;case "png":HTTP_Current_Head = HTTP_Current_Head.Replace("CurrentType", "image/png");break;case "html":HTTP_Current_Head = HTTP_Current_Head.Replace("CurrentType", "text/html");break;case "gif":HTTP_Current_Head = HTTP_Current_Head.Replace("CurrentType", "image/gif");break;case "js":HTTP_Current_Head = HTTP_Current_Head.Replace("CurrentType", "application/x-javascript");break;case "asp":HTTP_Current_Head = HTTP_Current_Head.Replace("CurrentType", "text/asp");break;default:HTTP_Current_Head = HTTP_Current_Head.Replace("CurrentType", "text/html");break;}send_str = HTTPServer.HTTP_OK_Start + HTTP_Current_Head;byte[] head_byte = new byte[send_str.Length];head_byte = Encoding.UTF8.GetBytes(send_str);//字符串流合并,生成发送文件 //之前采用的是byte[]->string, string合并, string->byte[],这种方法读取图片乱码//因此修改为,string合并, string->byte[], byte[]合并方式,读取图片成功byte[] send_byte = new byte[send_str.Length + fbyte.Length];Buffer.BlockCopy(head_byte, 0, send_byte, 0, head_byte.Length);Buffer.BlockCopy(fbyte, 0, send_byte, head_byte.Length * sizeof(byte), fbyte.Length);Console.WriteLine("File Send....");return send_byte;

4. 服务器测试和代码下载

  到现在为止,一个简单的静态web服务器就实现了,将希望访问的资源文件放入当前程序文件夹/html/下, 并将首页定义为index.html, 点开服务器程序,浏览器中输入http://127.0.0.1:3000, 就可以查看返回的网页。

具体程序参考:Web服务器下载

转载于:https://www.cnblogs.com/zzp0320/p/7908412.html

TCP/IP协议学习(四) 基于C# Socket的Web服务器---静态资源处理相关推荐

  1. TCP/IP协议学习(五) 基于C# Socket的C/S模型

    TCP/IP协议作为现代网络通讯的基石,内容包罗万象,直接去理解理论是比较困难的:然而通过实践先理解网络通讯的理解,在反过来理解学习TCP/IP协议栈就相对简单很多.C#通过提供的Socket API ...

  2. TCP/IP协议学习( 三 ) ---- ping原理 和 ICMP

    TCP/IP协议学习( 三 ) ---- ping原理 和 ICMP 1. 命令ping的用法和解析 1.1 ping 的用法 1.2 TTL 是什么? 1.3 SEQ是什么? 2.ICMP协议 2. ...

  3. 《Linux高性能服务器编程》学习总结(四)——TCP/IP通信案例:访问Internet上的Web服务器...

    第四章      TCP/IP通信案例:访问Internet上的Web服务器 HTTP协议是工作在应用层上的协议,其应用十分广泛,而在进行通信的过程中,经常使用HTTP代理服务器.HTTP代理服务器主 ...

  4. tcp ip 协议 学习

    tcp ip 协议学习. linux 内核版本 : 3.18.48 主题: 由于只关心 传输层和网络层这2层 ,所以TCP/IP是重点. 从哪里开始? 从下往上还是从上往下 ?  思考良久 还是从 T ...

  5. TCP/IP协议学习笔记

    TCP/IP详解学习笔记(1)-基本概念 为什么会有TCP/IP协议 在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别.就好像圣经中 ...

  6. TCP/IP协议的一个具体实现Socket

    java 中Socket的用法 TCP/IP协议 两个不同的协议,放在一起说.IP协议是用来查找地址的,对应网际互连层:TCP协议是用来规范传输规则的,对应传输层. TCP在传输之前会进行三次沟通(三 ...

  7. TCP/IP协议学习笔记(二)

    工程建立: 先建Project然后添加module和package,如图所示: 代码编写: 首先代码中会出现中文字符,所以要在代码中添加#coding utf-8,避免出现乱码. 然后明确TCP/IP ...

  8. TCP/IP协议学习之计算机中的端口

    端口的概念 在网络技术中,端口(Port)大致有两种意思:一是物理意义上的端口,比如,ADSL Modem.集线器.交换机.路由器用于连接其他网络设备的接口,如RJ-45端口.SC端口等等.二是逻辑意 ...

  9. TCP/IP协议学习

    计算机网路学得不好,首先先放个OSI七层网络模型吧 在协议的控制下,上层对下层进行调用,下层对上层进行服务, 上下层间用交换原语交换信息.这样可以提高传输速率,并且保证数据安全,所以说其实每一层都有存 ...

  10. TCP/IP协议学习之TCP、IP篇

    TCP/IP是不同通信协议的大集合 协议族 TCP/IP是基于TCP和IP这两个最初的协议之上的不同的通信协议的大集合. 团结就是力量嘛,厉害的人总是倾向于聚集起来,厉害的协议同理,那么这些协议具体包 ...

最新文章

  1. Windows 2003 主域控和DNS迁移到Windows 2008 R2(2)
  2. 【R】Rstudio set up
  3. xtrabackup mysql 5.6_MySQL 5.6对于Xtrabackup的影响
  4. echarts.js:1136 Uncaught Error: Initialize failed: invalid dom.
  5. [实战]java回调函数
  6. mysql定时异地备份_MYsql 异地备份脚本
  7. oracle sql 查询无数据_信运大讲堂丨ORACLE数据库SQL和索引
  8. mysql 0000-00-00无效_mysql0000-00-00日期异常及解决方法
  9. python结巴分词代码_结巴分词Python代码
  10. 为什么程序员下班后只关显示器却从不关电脑?
  11. 基于web的小区物业管理系统
  12. 乐理基础-曲谱、简谱、音名、唱名、调、调号
  13. 互联网教育:玩法与困境
  14. java 生成 rtf_如何从Java生成RTF?
  15. 计算机管理器没有注册类别,Win10电脑系统没有注册类别怎么解决
  16. 17.11.25B组总结
  17. 三磁环网络变压器圈比如何用LCR测量仪来检测?
  18. 一种基于属性加密技术(ABE)的轻量级数据共享方案
  19. .NET Framework的作用是什么
  20. 人工智能在无人驾驶中的应用

热门文章

  1. 【渝粤教育】国家开放大学2018年秋季 1301T病理生理学 参考试题
  2. 2018北理复试机试题
  3. Python进阶(十三) os、random、time等标准库
  4. 无约束最优化(二) 共轭方向法与共轭梯度法
  5. SpringCloud-Config通过Java访问URL对敏感词加密解密
  6. Cocos Creator 原生安卓改包名+AS运行到真机apk被slice处理而失败
  7. C和C指针小记(五)-指针类型
  8. 使用PHP和GZip压缩网站JS/CSS文件加速网站访问速度
  9. 对MVC设计模式的理解
  10. es6笔记 day1---let和const的应用