手把手教你写HTTPserver 服务器-2

  1. 项目名称:httpserver服务器

     

    1. 项目结构
    2. 相关知识说明
    3. 启动程序开发
    4. 核心程序开发

    上篇文章我们写到了启动程序,和核心程序的主逻辑部分,接下来就来将主逻辑的代码展开。

     

  • createfd 创建监听socket

int createlfd(int port){

 

int lfd = socket(AF_INET,SOCK_STREAM,0);

/*

设置端口复用:原因是在socket的在四次挥手的时候

Fin

Fin_wait_1 client -----> server close_wait

ack

Fin_wait_2 client <------ server close_wait

Fin

Time_wait client ------> server last_ack

acl

client <------- server

 

其中 在Fin_wait -- > Time_wait 中间会有一段缓冲时间,保证服务器端发送数据成功。

*/

 

int optval = 1;

setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval));

 

/*

 

sockaddr_in

 

*/

 

struct sockaddr_in serveraddr;

serveraddr.sin_family=AF_INET;

serveraddr.sin_addr.s_addr=inet_addr("0.0.0.0");

serveraddr.sin_port=htons(port);

bind(lfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));

//监听服务

listen(lfd,20);

return lfd;

}

        创建epoll树,监听文件描述符的读取缓冲区

//添加监听socket到时间树上,返回二叉树efd

int addfdtoevets(int efd,int lfd){

 

 

/*

epool 的平滑模式:当有数据到达的时候触发一次,然后读取数据,如果没有读取完缓冲区的数据,则在触发,直到读取完成为止

epool 边缘阻塞模式: 当有数据到达时,就开始读取,直到读完为止,只触发一次

*/

ev.events=EPOLLIN | EPOLLET;

ev.data.fd=lfd;

epoll_ctl(efd,EPOLL_CTL_ADD,lfd,&ev);

return efd;

}

  • 解析http协议,并进行响应

void parsecontent(int confd,int efd){

char buffer[1024]={0};

FILE * handler = fdopen(confd,"r");

//将socket 转换为文件字符,用fgets读取一行

char *header = fgets(buffer,1024,handler);

if (!header)

{

/* code */

return;

}

 

//解析头部

 

/*

 

请求方法,请求资源, 协议版本,

*/

char* delim = " ";

char* p;

printf("method:%s\n", strtok(header, delim));

//printf("method:%s\n", strtok(s, delim));

 

char* uri = strtok(NULL, delim);

decode_str(uri, uri);

char* file = uri+1;

printf("uri :%s\n",uri);

 

char* protocal = strtok(NULL, delim);

printf("protocal :%s\n", protocal);

//解析请求行

int flag =0;

 

if(strcmp(uri, "/") == 0)

{

// file的值, 资源目录的当前位置

file = "./";

}

if (!strcmp(uri,"/favicon.ico"))

{

//如果两个值相等,则返回,不响应此请求;

return;

}

 

 

//判断是文件还是目录

 

struct stat s_buf;

stat(file,&s_buf);

if(S_ISDIR(s_buf.st_mode)){

printf("it is hrere..............\n");

char * retval = send_dir(confd,file,flag);

//发送响应头和响应行

send_response_header("text/html",countlengh(retval),confd);

发送数据

write(confd,retval,countlengh(retval));

 

}

 

else{

 

int fp = open(file,O_RDWR);

int size = get_file_size(fp);

char tmp[1024] ={0};

strcpy(tmp,file);

//判断文件类型

char * filetype = orgnizefiletype(tmp);

printf("The Last FileType is %s\n",filetype);

//发送响应头和响应行

send_response_header(filetype,size,confd);

//发送文件信息

send_file(confd,file);

 

}

 

 

 

 

 

}

 

  • 如果是目录

    

char * send_dir(int confd,char * Path, int flag){

printf("senddir path %s\n",Path);

 

 

char buffer[8096] ="";

char tmp[1024]={0};

struct dirent *direntp;

char filepath[1024]={0};

 

 

 

 

DIR* sdir = opendir(Path);

//如果返回NULL,表示打开目录失败

if (!sdir)

{

//这里表示是文件

//发送文件到客户端

perror("can't open the file!\n");

return NULL ;

 

}

 

 

 

 

sprintf(buffer+strlen(buffer),"%s","

");

while( (direntp = readdir(sdir)) != NULL )//读取当前目录下的文件和目录信息

{

if( strcmp(direntp->d_name,".")==0||strcmp(direntp->d_name,"..")==0)

continue;

 

 

char * name = direntp->d_name;

char enstr[1024]={0};

encode_str(enstr, sizeof(enstr), name);

if (direntp->d_type & DT_DIR)

{

/* code */

 

 

sprintf(buffer+strlen(buffer),"

",name,name);

 

 

}

 

 

else{

 

sprintf(buffer+strlen(buffer),"

",name,name);

}

 

}

 

 

//组装目录信息和目录下的文件为html代码并返回

sprintf(buffer+strlen(buffer),"%s","

%s
%s
");

sprintf(buffer+strlen(buffer),"%s","");

char * ret = buffer;

closedir(sdir);

return ret;

 

 

}

  • 如果是文件

 

void send_file(int confd,const char * path){

 

 

 

printf("the file path is %s\n",path);

 

//如果是文件,就开发文件并发送文件到socket缓冲区

//这里的文件包括文本类型,图片

 

int fd = open(path,O_RDONLY);

 

if (fd==-1)

{

/* code */

perror("Not Found The File,Please Check Path!");

return ;

}

 

char buffer[4096]={0};

int len=0;

 

while ((len=read(fd,buffer,sizeof(buffer)))>0)

{

write(confd,buffer,len);

}

if (len==-1)

{

perror("read file error!");

exit(1);

}

close(fd);

 

 

 

 

 

 

}

通过以上代码,我们可以总结一下逻辑:

  1.  本地起socket服务
  2. 接受连接请求
  3. 解析http协议,解析出请求的资源
  4. 判断资源是何种类型,如果是目录,则遍历当前目录,并返回
  5. 如果是文件,则进行读取文件内容,发送给client端

其核心逻辑还是比较简单的,主要是要了解httpserver的通信原理,搭配着epoll可以写出一个支持多客户端的httpserver。

 

如果要查看完整代码,请点击右侧连接:https://github.com/kaiven11/cproject ,里面有文章的代码。

你可能感兴趣的:(手把手教你写HTTPserver 服务器-2)