Linuxwebserver项目

发布时间:2023年12月31日

1.主函数mian

signal(SIGPIPE,SIG_IGN);
?? ?char pwd_path[256]="";记录工作目录
?? ?char * path = getenv("PWD");获取当前目录工作路径
?? ?///home/itheima/share/bjc++34/07day/web-http
?? ?strcpy(pwd_path,path);字符串复制函数
?? ?strcat(pwd_path,"/web-http");两个字符型连接起来
?? ?chdir(pwd_path);改变当前工作路径

int lfd = tcp4bind(PORT,NULL);创建lfd,并且绑定,

Listen(lfd,128);监听

int epfd = epoll_create(1);创建红黑树,创建句柄

struct epoll_event ev,evs[1024];创建事件结构体和事件集合
?ev.data.fd = lfd;结构体内部的文件描述
?ev.events = EPOLLIN;定义事件为读事件
?epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);将lfd上树,

while(1)循环监听
?? ?{
?? ??? ?int nready = epoll_wait(epfd,evs,1024,-1);去监听集合
?? ??? ?if(nready < 0)报错
?? ??? ?{
?? ??? ??? ?perror("");
?? ??? ??? ?break;
?? ??? ?}
?? ??? ?else我们需要查找集合那个发生变化
?? ??? ?{
?? ??? ??? ?for(int i=0;i<nready;i++)
?? ??? ??? ?{
?? ??? ??? ??? ?printf("001\n");
?? ??? ??? ??? ?if(evs[i].data.fd == lfd && evs[i].events & EPOLLIN)判断是否是lfd,且是读事件
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?struct sockaddr_in cliaddr;构建客户端地址结构体
?? ??? ??? ??? ??? ?char ip[16]="";ip地址接收
?? ??? ??? ??? ??? ?socklen_t len = sizeof(cliaddr);地址长度
?? ??? ??? ??? ??? ?int cfd = Accept(lfd,(struct sockaddr*)&cliaddr,&len);提取新的cfd
?? ??? ??? ??? ??? ?printf("new client ip=%s port=%d\n",
?? ??? ??? ??? ??? ??? ?inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,ip,16),
?? ??? ??? ??? ??? ??? ?ntohs(cliaddr.sin_port));并且打印出出ip和端口
?? ??? ??? ??? ??? ?int flag = fcntl(cfd,F_GETFL);需要设置cfd为非阻塞fcntl函数可以设置
?? ??? ??? ??? ??? ?flag |= O_NONBLOCK;设置标志位
?? ??? ??? ??? ??? ?fcntl(cfd,F_SETFL,flag);
?? ??? ??? ??? ??? ?ev.data.fd = cfd;将cfd上树
?? ??? ??? ??? ??? ?ev.events = EPOLLIN;事件是读事件
?? ??? ??? ??? ??? ?epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
?? ??? ??? ??? ?}
?? ??? ??? ??? ?else if(evs[i].events & EPOLLIN)//cfd变化
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?read_client_request(epfd,&evs[i]);我们去调用这个函数读客户端请求

?? ??? ??? ??? ?}

?? ??? ??? ?}
?? ??? ?}
?? ?}

2.读客户端请求read_client_request函数

void read_client_request(int epfd ,struct epoll_event *ev)//句柄和事件
{
	char buf[1024]="";//读取一行,再把其他行读取,扔掉
	char tmp[1024]="";//
	 int n = Readline(ev->data.fd, buf, sizeof(buf));
	 if(n <= 0)//读取的是小于等于0代表关闭或者出错
	 {
	 	printf("close or err\n");
	 	epoll_ctl(epfd,EPOLL_CTL_DEL,ev->data.fd,ev);下树
	 	close(ev->data.fd);关闭文件描述符
	 	return ;
	 }
	 printf("[%s]\n", buf);缓冲区数据打印出来
	 int ret =0;返回值接收
	 while(  (ret = Readline(ev->data.fd, tmp, sizeof(tmp))) >0);  
	 //解析请求行 GET /a.txt  HTTP/1.1\R\N
	 //
	 char method[256]="";get
	 char content[256]="";/a.txt
	 char protocol[256]="";HTTP/1.1\R\N
	 sscanf(buf,"%[^ ] %[^ ] %[^ \r\n]",method,content,protocol);拆字符串空格拆分,
	 printf("[%s]  [%s]  [%s]\n",method,content,protocol );打印出来
	 //判断是否为get请求  get   GET
	 if( strcasecmp(method,"get") == 0)
	 {
	 	//[GET]  [/%E8%8B%A6%E7%93%9C.txt]  [HTTP/1.1]拆解的字符
	 		char *strfile = content+1;
	 		strdecode(strfile,strfile);
	 		 //GET / HTTP/1.1\R\N
	 		if(*strfile == 0)//如果没有请求文件,默认当前目录
	 			strfile= "./";
	 		//判断请求的文件在不在
	 		struct stat s;
	 		if(stat(strfile,&s)< 0)//文件不存在
	 		{
	 			printf("file not fount\n");
	 			//先发送 报头(状态行 消息头 空行)
	 			send_header(ev->data.fd, 404,"NOT FOUND",get_mime_type("*.html"),0);调用
	 			//发送文件error.html
	 			send_file(ev->data.fd,"error.html",ev,epfd,1);调用


	 		}
	 		else
	 		{
	 			
	 			//请求一个普通文件
	 			if(S_ISREG(s.st_mode))
	 			{
	 				printf("file\n");
	 				//先发送 报头(状态行 消息头 空行)
	 				send_header(ev->data.fd, 200,"OK",get_mime_type(strfile),s.st_size);
	 				//发送文件
	 				send_file(ev->data.fd,strfile,ev,epfd,1);

	 			}
	 			else if(S_ISDIR(s.st_mode))//请求是一个目录
	 			{
						printf("dir\n");
						//发送一个列表 网页
						send_header(ev->data.fd, 200,"OK",get_mime_type("*.html"),0);
						//发送header.html
						send_file(ev->data.fd,"dir_header.html",ev,epfd,0);

						struct dirent **mylist=NULL;定义一个指针
						char buf[1024]="";缓冲区
						int len =0;长度
						int n = scandir(strfile,&mylist,NULL,alphasort);
						for(int i=0;i<n;i++)
						{
							//printf("%s\n", mylist[i]->d_name);
							if(mylist[i]->d_type == DT_DIR)//如果是目录
							{
								len = sprintf(buf,"<li><a href=%s/ >%s</a></li>",mylist[i]->d_name,mylist[i]->d_name);
							}
							else
							{
								len = sprintf(buf,"<li><a href=%s >%s</a></li>",mylist[i]->d_name,mylist[i]->d_name);
							}

							

							send(ev->data.fd,buf,len ,0);发送数据

							free(mylist[i]);释放
						}
						free(mylist);释放


						send_file(ev->data.fd,"dir_tail.html",ev,epfd,1);发送文件

	 			}

	 		}

	 }

}

3.发送状态行函数send_header

void send_header(int cfd, int code,char *info,char *filetype,int length)
{	发送状态行
	char buf[1024]="";缓冲区
	int len =0;
	len = sprintf(buf,"HTTP/1.1 %d %s\r\n",code,info);字符串格式化
	send(cfd,buf,len,0);发送cfd中
	//发送消息头
	len = sprintf(buf,"Content-Type:%s\r\n",filetype);格式化
	send(cfd,buf,len,0);发送到cfd中
	if(length > 0)
	{
			//发送消息头
		len = sprintf(buf,"Content-Length:%d\r\n",length);对长度处理
		send(cfd,buf,len,0);

	}
	//空行
	send(cfd,"\r\n",2,0);
}

4.发送文件send_file函数

void send_file(int cfd,char *path,struct epoll_event *ev,int epfd,int flag)
{
		int fd = open(path,O_RDONLY);只读打开路径,生成文件描述符
		if(fd <0)文件不存在报错
		{
			perror("");
			return ;
		}
		char buf[1024]="";定义一个缓冲区
		int len =0;长度
		while( 1)
		{

			len = read(fd,buf,sizeof(buf));从缓冲区读
			if(len < 0)如果没有报错
			{
				perror("");
				break;

			}等于0跳出
			else if(len == 0)
			{
				break;
			}
			else
			{
				int n=0;
				n =  send(cfd,buf,len,0);我们发送数据
				printf("len=%d\n", n);

			}


		}
		close(fd);关闭文件描述符
		//关闭cfd,下树
		if(flag==1)
		{
			close(cfd);
			epoll_ctl(epfd,EPOLL_CTL_DEL,cfd,ev);
		}



}

#define PORT 8889端口号,

上述就是实现webserver整体代码了

文章来源:https://blog.csdn.net/weixin_55605689/article/details/135317204
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。