写一个简单版本的WebServer-Ver1.0-单进程

rio:

rio是一个结构体,rio结构体的作用是提供一个自己设置的IO缓冲区,从sockfd表示的文件中的接收缓冲区中读取数据到rio中,rio相当于一个中转站,获得对端写到sockfd中的数据时,首先将数据读取到rio维护的缓冲区中,然后再从rio维护的缓冲区读取到用户空间指定的区域中。

这个过程中有三个存放数据的区域:

1.sockfd维护的缓冲区:就是read读取数据的来源。这个接收缓冲区接收从对端发送的数据。

2.rio结构体维护的缓冲区:相当于一个中转站,sockfd中的数据首先转移到这里来,再从这转移到用户指定的区域中。

3.用户指定的区域:用户用于处理数据的区域,由自己指定。

rio维护四个成员变量:

1.int rio_fd.这个变量指向要读取的文件的文件描述符,通过这个将一个rio和某个文件绑定在一起。

2.int rio_cnt.这个变量表示rio中尚未转移到用户指定区域的字节数。因为rio从rio_fd指定的文件中读取数据到rio中,rio中存放的数据都是需要转移到用户指定区域的,用rio_cnt表示存在在rio中但尚未转移到用户指定区域的字节数。

3.char* rio_bufptr.这个指针用于指向rio缓冲区中未转移到用户指定区域的数据的起始处。因为rio中的缓冲区是自己设置的,不像sockfd会自己维护一个属于本文件的指针(read每次调用都会调整sockfd中的该指针),因此这个缓冲区需要自己维护,每次读取n个字节后该指针都要后移,保持在未读区域的第一个字节。

4.char rio_buf[MAXLINE].rio维护的缓冲区,用于存放从rio_fd绑定的文件中读取的数据。

rio_read:

read的包裹函数,简易的封装了下read函数。

为什么封装read为rio_read:

封装后,每次rio维护的缓冲区没有数据时,都会一下读入MAXLINE个数据到rio_buf中。然后如果要读取n个字节到用户指定区域,则采用memcpy直接在rio_buf与用户空间间进行拷贝。实际上调用read只是在每次rio_buf无数据,也就是未读数据rio_cnt为0时。

如果不封装,则每次读n个字节都要调用一次read,这样要频繁在用户态和内核态间切换。

read_requestthdrs:

这个函数用于处理头部信息,但因为很简陋,对头部信息没有什么处理的地方。

注意,每次Rio_readlineb(rp, buf, MAXLINE);实际上是使用memcpy拷贝的,而memcpy是直接覆盖掉,比如buf之前是"i'm a sb",而Rio_readlineb调用之后,直接从buf头开始覆盖,即使这次覆盖的字符比较少,如"aa",则拷成了"aa\0 a sb"。

这样只要最后一个请求头"\r\n"出现,即使buf拷贝后为"\r\n\0 a sb",strcmp也会判定与"\r\n"相等,因为到\0实际上就比对结束了。

整个流程实际上是:

首先处理请求行:请求行有method uri version三个信息。method为qingqiu方法,这个版本只能处理GET方法。uri为要请求的资源。version为请求的方法的版本。

然后处理请求头部:包含了客户端的一些信息,如支持的编码,是否保持连接,期望的语言类型。。。

\r\n为请求的结尾。读取到\r\n说明这一段的请求解析到此。计网Page271:CRLF作为报文的请求行或head的结尾.CR表示回车,LF表示换行,即\r\n.

parse_uri:

解析URL:
目前只考虑静态文档,因此cgiargs(CGI args)是不必要的(是这样的吗???),因此stcpy给cgiargs一个"",即一个\0。

然后将工作目录名传递给文件名filename,因为URI中传递的是相对URL(省略了主机的域名,见计网Page272),因此需要首先加入工作目录名才能定位到请求的文件。

然后将uri中的相对URL加入到filename中,这样就能定位到具体的文件了。

下面的这个if判断的作用是对于URL:http://www.hao123.com/

www.hao123.com为域名,真正传递的相对URL只有"/",除了这种情况,其他任何情况下uri[strlen[uri]-1]都不可能是'/'。这种情况应该跳转到主页,所以在filename后面加入的是home_page。

动态文档先跳过。

 

上一篇:网络安全传输系统-sprint1传输子系统


下一篇:套接字编程__tcp