linux-myhttp实现

一.写一个发布静态资源的http服务器(静态资源就是已经存好的文件)

1.只用写服务端,客户端就是浏览器。解析浏览器发过来的数据,服务端组装一个应答报文进行回复。具体流程如下:

2.连接后(tcp连接),浏览器发送的请求报文:

一般的网站都会设置一个名叫Index.html文件,当只访问IP没加后缀(如:127.0.0.1  加后缀:127.0.0.1/a.jpg)就将这个页面返回。就是访问页面127.0.0.1与127.0.0.1/index.html等价。

3.myhttp组装一个应答报文发给浏览器(这里先只写前三个):

在接下来的代码中我将组装以下部分:

4.代码实现简单来说就是:你要访问哪个文件,我在解析文件名,拼接好路径,open在打开它,得到文件大小,组装好报文,再把报头发过去,最后把文件内容发过去。

这里我先将代码中出现的三个函数进行解释:

①off_t lseek(int fd, off_t offset, int whence);-->计算文件偏移量

fd  :文件描述符,即你想要操作的文件的描述符。 

offset  :偏移量,以字节为单位。

 whence  :有以下三种

SEEK_SET  :读写偏移量将指向  offset  字节位置处(从文件头部开始算);

SEEK_CUR  :读写偏移量将指向当前位置偏移量  + offset  字节位置处,  offset  可以为正、也可以为负,如果是正数表示往后偏移,如果是负数则表示往前偏移;

 SEEK_END  :读写偏移量将指向文件末尾  + offset  字节位置处,同样  offset  可以为正、也可以为负,如果是正数表示往后偏移、如果是负数则表示往前偏移。

②int sprintf(char *str, const char *format, ...);-->将...所属的format格式插入到str中

str  :指向目标字符串的指针,  sprintf   将格式化后的字符串存储在这里。

format  :格式字符串,定义了后续参数如何被格式化和插入到最终的字符串中。

 ...  :可变参数列表,这些参数将按照   format   字符串中指定的格式被转换并插入到字符串中。

③char *strcat(char *dest, const char *src);-->j将一个字符串追加到另一个字符串的结尾

dest  :指向目标字符串的指针,  src   字符串的内容将被追加到这个字符串的末尾。

src  :指向源字符串的指针,其内容将被复制到   dest   字符串的末尾。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <fcntl.h>//open

#define PATH "/home/stu/mycodes/day9"//这里是我自己的当前路径

int socket_init()//函数定义

char*get_filename(char buff[])
{
    //用strtok_r实现(线程安全的函数)
    char*ptr=NULL;
    char*s=strtok_r(buff," ",&ptr);//接收recv的信息,接收GET
    if(s==NULL)
    {
        return NULL;
    }
    printf("请求方法:%s\n",s);
    s=strtok_r(NULL," ",&ptr);//从上次接收完的地方继续接收,这次接收的是文件名
    return s;
}
void*fun(void*arg)
{
    int *p=(int*)arg;
    int c=*p;
    free(p);
    while(1)//长连接
    {
        char buff[1024]={0};
        int n=recv(c,buff,1023,0);//接收浏览器发来的请求报文http
        if(n<=0)
        {
            break;
        }
        printf("recv:%s\n",buff);

        char*filename=get_filename(buff);//将提取请求报文的文件名交给函数实现
        if(filename==NULL)
        {
            send(c,"404",3,0);//这里应该写个报文发送404,这里简化了直接发送404
            break;
        }
        //提取文件名成功了,要访问这个文件,这里先将文件放在当前路径,也可以放在其他地方
        char path[128]={PATH};//组装绝对路径
        //将文件名拼接到PATH后面
        if(strcmp(filename,"/")==0)//只写了IP地址,后面默认是index.html
        {
            strcat(path,"/inddex.html");//将/index.html拼接到path后面
        }
        else
        {
            strcat(path.filename);
        }
        printf("path=%s\n",path);//得到路径
        
        int fd=open(path,O_RDONLY);//打开文件这里只读打开
        if(fd==-1)
        {
            send(c,"404",3,0);//这里要改为报文
            break;
        }
        int filesize=lseek(fd,0,SEEK_END);//获得文件大小,为下面组装报头准备,lseek函数根据起始位置和末尾位置的偏移量计算大小
        lseek(fd,0,SEEK_SET);//计算完大小后,让文件回到起始位置
        //要给浏览器发送数据,组装一个应答报文,发送报头
        char head[128]={"HTTP/1.1 200 OK\r\n"};
        strcat(head,"Server:myhttp\r\n");
        //拼接文件的大小,将整形转为字符串拼接到一起。可以使用sprintf-->输出到一个字符数组里面,它会帮我们进行格式转换,输出格式写%d,后面给个整形,一输出到一个字符数组里面就自动转换为字符串。
        sprintf(head+strlen(head),"Content-Length:%d\r\n",filesize);//head+strlen(head)是在head的结尾加上后面的%d
        strcat(head,"\r\n");
        send(c,head,strlen(head),0);//流式服务可以一部分一部分的发,发送http应答报文的报头给浏览器

        //循环发送文件数据部分直到发完。
        char data[1024];
        int num=0;//统计每次读到了多少
        while((num=read(fd,data,1024))>0)//至少读了一个字节
        {
            send(c,data,num,0);
        }
        close(fd);
    }
    close(c);
}
int main()
{
    int sockfd=socket_init();
    if(sockfd==-1)\
    {
        exit(1);
    }
    while(1)
    {
        struct sockaddr_in caddr;
        int len=sizeof(caddr);
        int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
        if(c<=0)
        {
            continue;
        }
        printf("accept c=%d\n",c);

        pthread_t id;
        int *p=(int *)malloc(sizeof(int));
        if(NULL==p)
        {
            close(c);
            continue;
        }
        *p=c;
        pthread_create(&id,NULL,fun,(void*)p);
    }
}
int socket_init()
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd==-1)
    {
        exit(1);
    }
    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family=AF_INET;
    saddr.sin_port=htons(80);//http默认80端口
    saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if(res==-1)
    {
        printf("bind err\n");
        exit(1);
    }
    res=listen(sockfd,10);
    if(res==-1)
    {
        return -1;
    }
    return sockfd;
}

5.这里的favicon.ico就是请求图标(系统会默认请求)图标名称就是:favicon.ico

6.当连接html时系统会请求文档,当html文档中有图片,则还会请求连接图片,如:

7.wget www.baidu.com-->可以拿到百度的网址wget就是拿网址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值