web服务器的开发(简易版本)

总体介绍

使用浏览器作为客户端访问web服务器;

使用的知识总结:

  socktet编程:
    socket -> setsockopt -> bind -> listen -> read -> write -> send -> recv -> close ;
  常用网络服务器模型:
    多进程版本;
    多线程版本;
    多路IO复用;
    第三方库libevent;
  TCP/IP四层模型:
     应用层 -> 传输层 -> 网络层 -> 数据链路层
  使用的协议:
    TCP/IP、http协议
  熟悉http协议的请求和应答协议

http协议请求报文格式:
1 请求行  GET /test.txt HTTP/1.1
2 请求行  健值对
3 空行  \r\n
4 数据

在这里插入图片描述

http协议响应消息格式:
1 状态行  200 表示成功, 404 表示请求的资源不存在
2 消息报头 健值对
3 空行 \r\nz
4 响应正文
使用epoll模型作为web服务器:

<webserver.c>

//web服务端程序--使用epoll模型
#include <unistd.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <signal.h>
#include <dirent.h>

#include "pub.h"
#include "wrap.h"
//处理客户端请求
int http_request(int cfd, int epfd);

int main(){
   
	//若web服务器给浏览器发送数据时,浏览器已经关闭连接,
	//则web服务器会受到SIGPIPE信号,会直接将进程终止,我们将该信号屏蔽
	struct sigaction act;
	act.sa_handler = SIG_IGN;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	sigaction(SIGPIPE, &act, NULL);

	//改变当前的工作目录,专门设置一个目录作为根目录
	char path[255] = {
   0};
	//getenv是函数名,从环境中取字符串,获取环境变量的值
	sprintf(path, "%s/%s", getenv("HOME"), "webpath");
	chdir(path);
	
	//创建socket--设置端口复用 -- bind
	//该函数 是自己写的 在 "wrap.c"中
	int lfd = tcp4bind(9999, NULL);
	
	//设置监听 "wrap.c"
	Listen(lfd, 128);
	
	//创建epoll树
	int epfd = epoll_create(1024);
	if(epfd < 0){
   
		perror("epoll_create error");
		close(lfd);
		return -1;
	}
	//将监听文件描述符上树
	struct epoll_event ev;
	ev.data.fd = lfd;
	ev.events = EPOLLIN;
	epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
	
	int i;
	int cfd;
	int sockfd;
	int nready;
	struct epoll_event events[1024];
	while(1){
   
		//等待事件发生
		nready = epoll_wait(epfd, events, 1024, -1);
		if(nready < 0){
   
			if(error == EINTR){
   
				//阻塞函数,可以被信号打断, 不应该退出进程
				continue;
			}
			break;
		}
		for(i = 0; i < nready; i++){
   
			sockfd = events[i].data.fd;
			//有客户端连接请求
			if(sockfd == lfd){
   
				//接受新的客户端连接 "wrap.c"
				cfd = Accept(lfd, NULL, NULL);
				//设置cfd为非阻塞
				int flags = fcntl(cfd, f_GETFL);
				flags |= O_NONBLOCK;
				fcntl(cfd, F_SETFL, flags);

				//将新的cfd上树
				ev.data.fd = cfd;
				ev.events = EPOLLIN;
				epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);
			}
			else{
   
				//有客户端数据发来
				http_request(sockfd, epfd);
			}
		}

	}
}
int send_header(int cfd, char *code, char *msg, char *fileType, int len){
   
	char buf[1024] = {
   0};
	sprintf(buf, "HTTP/1.1 %s %s\r\n", code, msg);
	sprintf(buf + strlen(buf), "Content-Length:%d\r\n", fileType);
	strcat(buf, "\r\n");
	Write(cfd, buf, strlen(buf));
	return 0;

}
int send_file(int cfd, char *fileName){
   
	//打开文件
	int fd = open(fileName, O_RDONLY);
	if(fd < 0){
   
		perror("open error");
		return -1;
	}
	int n;
	char buf[1024];
	while(1){
   
		memset(buf, 0x00, sizeof(buf));
		n = read(fd, buf, sizeof(buf));
		if(n <= 0){
   
			break;
		}
		else{
   
			Write(cfd, buf, n);
		}

	}
	return 0;
}
int http_request(int cfd, int epfd){
   
	int n;
	char buf[1024];
	
	//读取请求行数据,分析出要请求的资源文件名
	memset(buf, 0x00, sizeof(buf));
	//整行读 "wrap.c"
	n = Readline(cfd, buf, sizeof(buf));
	if(n <= 0){
   
		printf("read error or client closed, n == [%d]\n", n);
		close(cfd);
		//将文件描述符下树
		epoll_ctl(epfd, EPOLL_CTL_DEL, cfd,  NULL);
		return -1;
	
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值