Web服务器练习 -- 思路笔记

文章介绍了如何搭建一个简单的B/S模型服务器,主要涉及epoll模型和http协议。在do_read()函数中处理http请求,提取请求方式和文件对象,根据请求类型发送响应,包括文件内容或目录列表。示例代码展示了get_line()、sscanf()和scandir()函数的用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

最近跟着完成了一个简单的B/S模型服务器的搭建,一路写下来,虽然最终是完成了预定的效果,但是写的过程中老想着参考参考,于是编着编着有些地方就开始变成“打字”训练了。所以为了让自己的印象能够深刻一点,在这里把搭建的流程简单梳理一下,包括记录一些代码。

涉及知识

1. 边沿触发下的epoll模型

2. http基础协议

3. html超文本标记语言

逻辑流程图

1. epoll模型流程

    不管多线程/进程并发服务器,还是多路I/O转接服务器,我们平时写练习所完成的基本都是C/S模型。而搭建Web服务器,需要我们完成一个B/S模型。但既然它是服务器,那么它就与我们搭建C/S模型一样,都需要基础的服务器模型,所以这里我们给出使用较多的epoll模型的搭建流程。而与C/S模型不一样的是数据交流的部分,所以我们将重点放在do_read()函数中。

    在C/S模型中,我们需要完成两份代码的编写,即客户端和服务器端。所以两者的信息交流部分,是可以由我们根据需求自定义编写的。而在B/S模型中,我们只需要完成服务器端代码的编写。同时由于我们需要借助浏览器,那么在数据交流部分,我们就必须要遵循浏览器和Web服务器之间的通信协议,即http协议根据协议内容,进行请求的接收和响应的回发

 2. 基于http协议的数据交流流程

    当发生读事件时,也就是建立好连接后的客户端向我们的服务器发送了数据,即http请求消息,此时do_read()函数被调用

在do_read()函数中,我们主要完成两件事:

1) 处理http请求消息。根据http协议内容,我们可以知道,发送过来的请求消息中,最重要的内容为请求方式以及请求的文件对象 (本次Web实验,只处理GET请求) 。所以我们需要在请求消息中,提取出这两个信息,并且对它们加以判断,根据不同的判断结果进行不同的处理:当用户请求了一个普通文件时,我们就单纯的将这个文件的内容回发。当请求了一个目录文件时,我们需要将目录的内容呈现在浏览器中。

2) 回复http响应消息。根据http协议内容,我们需要按照 响应格式 发送响应消息,其中重要的内容包括:文件类型、文件大小、连接关闭以及响应正文(数据)。 对于普通文件,我们直接发送源文件数据即可。为此需要先判断出它的文件类型,如.txt / .jpg / .mp3等,然后回复规定的传输内容;对于目录文件,我们需要将目录内容呈现在浏览器中。也就是说,我们需要回发的文件类型为.html,回发的响应正文就是我们所编写的html语言。由于浏览器本身就是编译器,所以它能将这些代码标签以网页的形式展现出来。

最终效果 (部分结果图)

1. 请求目录

 (因为未做汉字字符对应的编码和译码部分的代码,所以这里的文件名采用的都是拼音的方式)

2. 请求未提供的文件

 部分源码

1. get_line() 函数  -- 读取一行数据

int get_line(int cfd,char *buf,int size)
{
	int n,i=0;
	char c = '\0';	
	while( i<size-1 && c!='\n'){

		n = recv(cfd,&c,1,0);     //读取一个字符
		if(n > 0){
			if(c == '\r'){        //判断是否到结尾  '以/r/n结尾'
				n = recv(cfd,&c,1,MSG_PEEK);   //尝试能否读取到/n	
				if(n > 0 && c == '\n'){
					recv(cfd,&c,1,0);	  //真正去读取
				} else { 
					c = '\n';       //未读到则补上	
				}
			}	
			buf[i] = c;
			i++;
		} else {
			c = '\n';   //仅起到退出循环的作用
		}

		buf[i] = '\0';

		if(-1 == n){   //出错
			i = n;	
		}
	}

	return i;    //返回读取到的字符数
}

2. sscanf() 函数 / scandir() 函数 的用法

/* sscanf()  --  拆分请求数据 */  
sscanf(buf,"%[^ ] %[^ ] %[^ ]",method,path,protocol); //通过正则表达式拆分信息


/* scandir() --  遍历目录项 */
struct dirent** ptr;    //目录项结构体二级指针
int num = scandir(dir_name,&ptr,NULL,alphasort);  //按字母顺序获取目录项

for(i=0; i<num ; i++){  //循环遍历
	char *name = ptr[i]->d_name;  //从ptr指针数组中获取文件名
    /*  向浏览器回发目录中各文件名  */
    ...
}

3. do_read() 函数

void do_read(int cfd,int epfd)    //数据交流函数
{
	char buf[1024] = { 0 };
	char method[16],path[256],protocol[16];

	int len = get_line(cfd,buf,sizeof(buf));   //获取一行数据	
        if(len == 0){
		    printf("客户端断开连接......\n");	
		    disconnected(cfd,epfd);      //断开连接
	} else {
		sscanf(buf,"%[^ ] %[^ ] %[^ ]",method,path,protocol);  //拆分

		while(1){      //由于是非阻塞ET,所以需要我们主动读取掉多余的信息
			char bin[1024] = { 0 };	
			len = get_line(cfd,bin,sizeof(bin));
			if(len == -1){
				break;	
			}
		}

		if( strncasecmp(method,"GET",3) == 0 ){     //GET请求方法
			char *file_name = path+1;   //获取请求文件名称			
            if(strcmp(path,"/") == 0){  //若请求了工作根目录				            
                file_name = "./";	
			}
			http_request(cfd,file_name);    //根据文件名,回复http响应
		}

	}
}

如果有初学的小伙伴,欢迎一同交流哦 ~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值