首页 > Web开发 > 详细

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

时间:2019-12-27 10:46:12      阅读:75      评论:0      收藏:0      [点我收藏+]
  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] ="<html><body>";

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","<table>");

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),"<tr><td><a href=%s/>%s</a></td></tr>",name,name);

?

?

}

?

?

else{

?

sprintf(buffer+strlen(buffer),"<tr><td><a href=%s>%s</a></td></tr>",name,name);

}

?

}

?

?

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

sprintf(buffer+strlen(buffer),"%s","</table>");

sprintf(buffer+strlen(buffer),"%s","</body></html>");

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

原文:https://www.cnblogs.com/ywtt/p/12105704.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!