【HTTP协议与Web服务器】

本文介绍了浏览器与Web服务器通信的HTTP协议基础,包括三次握手、请求报头和应答报头结构、请求方法以及应答状态码。此外,还提供了一个简单的C语言实现的Web服务器示例,用于处理HTTP请求并返回文件内容。
摘要由CSDN通过智能技术生成

浏览器与服务器通信过程

浏览器与Web服务器再应用层通信使用的是HTTP协议,而HTTP协议在传输层使用的是TCP协议。那么浏览器需要和web服务器进行三次握手建立连接之后才能发送HTTP请求报文,服务器收到请求保温,向浏览器回复HTTP应答报文。在发起连接之前需要得到服务器的IP以及端口。用户在浏览器中输入网址,浏览器会通过DNS服务查询获取到服务器的IP地址。对于端口来讲,使用HTTP协议的程序一般默认使用80端口。
浏览器服务器建立连接之后,如果两次以上的请求复用同一个TCP连接,则称之为长连接。如果浏览器发送一次请求报文,服务器回复一次应答就断开连接,下次交互重新进行三次握手建立连接,这种称之为短连接。现如今大多数为长连接,可以减少网络中的同步报文,也使得服务器的响应速度变快。

HTTP的请求报头

HTTP请求报头结构

GET/index.html HTTP/1.0\r\n 请求方法 请求页面 协议版本
User-Agent:Wget/1.12\r\n 客户端应用程序
Host:192.168.141.128\r\n 目标主机
Connection:close\r\n 连接方式(close/keep-alive)
在这里插入图片描述

HTTP的请求方法

请求方法含义
GET申请获取资源,而不对服务器产生任何影响
HEAD和GET方法类似,不过仅要求服务器返回头部信息,而不需要传输任何实际内容
POST客户都安向服务器提交数据的方法.这种方法会影响服务器:服务器可能根据收到的数据动态创建新的资源,也可能更新原有的资源
PUT上传某个资源
DELETE删除某个资源
TRACE要求目标服务器返回原始HTTP请求的内容.他可用来查看中间服务器对HTTP请求的影响
OPTIONS查看服务器对某个特定URL都支持那些请求方法.也可以把URL设置为*,从而获取服务器支持的所有请求方法
CONNECT用于某些代理服务器,他们能把请求的连接转化为一个安全隧道
PATCH对某个资源做部分修改

HTTP应答报头

HTTP应答报头结构

在这里插入图片描述

应答状态

状态码和状态信息含义
100 Continue服务器收到了客户端的请求行和头部信息,告诉客户端继续发送数据部分,客户端通常要先发送Expect:100-continue 头部字段告诉服务器自己还有数据要发送
200 OK请求成功
400 Bad Request通用客户请求错误
403 Forbidden访问被服务器禁止,通常是由于客户端没有权限访问该资源
404 Not Found资源没找到

web服务器的c语言实现

Web 服务器对应的文件是 myHttp.c,代码示例如下, 其中使用到的页面文件(.html)需要
用户自己提供,并且和程序在同一个位置:

#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>

#define  PATH       "/home/xin/kd1"
#define  DFL_FILENAME "/index.html"//默认路径
struct mess
{
    int fd;
};

int socket_init();

char* get_filename(char http_req[])//获取该主机的所申请文件的路径
{
    if ( http_req == NULL )
    {
        return NULL;
    }

    char * ptr = NULL;
    char* s = strtok_r(http_req," ",&ptr);
    if ( s == NULL )
    {
        return NULL;
    }

    printf("客户端请求方法:%s\n",s);
    s = strtok_r(NULL," ",&ptr);
    return s;
}

//struct mess节点的地址传入
void* fun(void * arg)
{
    struct mess * p = (struct mess*)arg;
    int c = p->fd;
    free(p);

    char http_req[512] = {0};
    int num = recv(c,http_req,512,0);
    if ( num <= 0 )
    {
        close(c);
        pthread_exit(NULL);
    }

    printf("浏览器发送的请求报文:\n%s\n",http_req);

    char* filename = get_filename(http_req);
    if ( filename == NULL )
    {
        //回复404
        close(c);
        pthread_exit(NULL);
    }

    char path[256] = {PATH};
    if ( strcmp(filename,"/") == 0 )
    {
        strcat(path,DFL_FILENAME);
    }
    else
    {
        strcat(path,filename);
    }

    printf("open:%s\n",path);
    //打开文件
    int fd = open(path,O_RDONLY);
    if ( fd == -1 )
    {
        //错误回复 404
        printf("打开失败\n");
        close(c);
        pthread_exit(NULL);
    }

    int filesize = lseek(fd,0,SEEK_END);//计算文件大小
    lseek(fd,0,SEEK_SET);
    //组装报文
    char http_res[512] = {0};
    strcpy(http_res,"HTTP/1.0 200 OK\r\n");
    strcat(http_res,"Server: myhttp\r\n");
    sprintf(http_res+strlen(http_res),"Content-Length: %d\r\n",filesize);
    strcat(http_res,"\r\n");
    send(c,http_res,strlen(http_res),0);
    printf("发送应答头部:%s\n",http_res);
    
    char data[1024] = {0};
    int m = 0;
    while( (m = read(fd,data,1024)) > 0 )
    {
        send(c,data,m,0);
    }
    close(fd);
    close(c);
}
int main()
{
    int sockfd = socket_init();
    if ( sockfd == -1 )
    {
        printf("socket init err\n");
        exit(0);
    }
   
   while( 1 )
   {
       struct sockaddr_in caddr;
       int len = sizeof(caddr);
       int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
       if ( c < 0 )
       {
           continue;
       }

       struct mess * p = (struct mess*)malloc(sizeof(struct mess));
       pthread_t id;
       p->fd = c;
       pthread_create(&id,NULL,fun,(void*)p);
   } 
}

int socket_init()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if ( sockfd == -1 )
    {
        return -1;
    }

    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(80);//浏览器http端口
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if ( res == -1 )
    {
        return -1;
    }

    res = listen(sockfd,30);
    if ( res == -1 )
    {
        return -1;
    }

    return sockfd;
}

index.html:
在这里插入图片描述
服务器连接结果:在这里插入图片描述
此处正常解析文件需要时html文档,如果是其他文档运行则结果会因为无法解析出现文件直接下载或直接打印在网页上(不同浏览器可能出现状况不一样)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

*闲鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值