Linuxwebserver项目

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             {
                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;id_name);
							if(mylist[i]->d_type == DT_DIR)//如果是目录
							{
								len = sprintf(buf,"
  • %s
  • ",mylist[i]->d_name,mylist[i]->d_name); } else { len = sprintf(buf,"
  • %s
  • ",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整体代码了

    你可能感兴趣的:(服务器,网络,windows,linux,运维)