HTTP协议浅析(下): 使用HTTP协议实现通信

来源:互联网 发布:web编程语言有哪些 编辑:程序博客网 时间:2024/06/06 17:46

HTTP协议浅析(下): 使用HTTP协议实现通信http://blog.csdn.net/tennysonsky/article/details/53909564

1. 概述

服务器的开发不容易,尤其是开发高性能、稳定性好服务器,更加不容易,因此人们尝试更好简单的方式来开发软件。

在服务器方面,使用Web服务器,采用HTTP协议来代替底层的socket,是常见的选择。采用HTTP协议更加除了能得到稳定的服务器支持外,更加可以兼容各种客户端(手机、PC、浏览器)等等。这样实现了一个服务器之后,多个客户端可以通用。

2.通信过程

这里写图片描述

HTTP 协议采用请求/响应模型。客户端向服务器发送一个请求报文,服务器以一个状态作为响应。

HTTP 请求/响应的步骤:

  • 客户端连接到web服务器:HTTP 客户端与web服务器建立一个 TCP 连接;
  • 客户端向服务器发起 HTTP 请求:通过已建立的TCP 连接,客户端向服务器发送一个请求报文;
  • 服务器接收 HTTP 请求并返回 HTTP 响应:服务器解析请求,定位请求资源,服务器将资源副本写到 TCP 连接,由客户端读取;
  • 释放 TCP 连接:若connection 模式为close,则服务器主动关闭TCP 连接,客户端被动关闭连接,释放TCP 连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;
  • 客户端浏览器解析HTML内容:客户端将服务器响应的 html 文本解析并显示。

3. 测试代码

#include <stdio.h>  #include <stdlib.h>  #include <string.h>                         #include <unistd.h>  #include <fcntl.h>#include <sys/socket.h>  #include <netinet/in.h>  #include <arpa/inet.h>int main(){     // 创建通信端点:套接字    int sockfd = socket(AF_INET, SOCK_STREAM, 0);      if(sockfd < 0)      {          perror("socket");          return -1;      }      // 设置本地地址结构体      struct sockaddr_in my_addr;      bzero(&my_addr, sizeof(my_addr));   // 清空          my_addr.sin_family = AF_INET;       // ipv4      my_addr.sin_port   = htons(8000);   // 端口      my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // ip      // 绑定      int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));      if( err_log != 0)      {          perror("binding");          close(sockfd);                return -1;      }      err_log = listen(sockfd, 10); // 监听,监听套接字改为被动      if(err_log != 0)      {          perror("listen");          close(sockfd);                return -1;        }         printf("listen client @port=%d...\n", 8000);     int connfd;      connfd = accept(sockfd, NULL, NULL);    // 等待连接      char recv_buf[8*1024] = {0};    read(connfd, recv_buf, sizeof(recv_buf));    printf("%s", recv_buf);    //获取客户端需要的网页内容    char filename[200] = {0};    sscanf(recv_buf, "GET /%[^ ]", filename); //获取文件名字    printf("filename = %s\n", filename);    int fd;    fd = open(filename, O_RDONLY);//只读方式打开    if(fd < 0)//打开文件失败    {        //HTTP 响应报文由状态行、响应头部、空行、响应包体4个部分组成        char err[]= "HTTP/1.1 404 Not Found\r\n"    //状态行                    "Content-Type: text/html\r\n"   //响应头部                    "\r\n"                          //空行                    "<HTML><BODY>File not found</BODY></HTML>";  //响应包体                   perror("open");         send(connfd, err, strlen(err), 0);//发送失败的响应报文头        close(connfd);        return -1;    }       //HTTP 响应报文由状态行、响应头部、空行、响应包体4个部分组成    char head[] = "HTTP/1.1 200 OK\r\n"     //状态行              "Content-Type: text/html\r\n" //响应头部              "\r\n";                       //空行    send(connfd, head, strlen(head), 0); //发送成功的响应报文头    //发送响应包体    int len;    char file_buf[4 * 1024];    //循环读取并发送文件,读多少,发多少    while((len = read(fd, file_buf, sizeof(file_buf))) > 0)     {        send(connfd, file_buf, len, 0);    }    close(fd);    close(connfd);     while(1)    {        NULL;    }    return 0;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99

终端运行服务器程序: 
这里写图片描述

浏览器请求服务,得到相应内容: 
这里写图片描述

4. 简单版Web服务器

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <string.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>/************************************************************************函数名称:   int main(int argc, char *argv[])函数功能:   通过进程创建webserver函数参数:   int argc, char *argv[]函数返回:   无************************************************************************/int main(int argc, char *argv[]){    unsigned short port = 8000;   //设置默认端口号    if(argc > 1)    {        port = atoi(argv[1]);   //将参数2赋值给端口号变量    }    //创建TCP套接字    int sockfd = socket(AF_INET, SOCK_STREAM, 0);    if( sockfd < 0)    {        perror("socket");           exit(-1);    }    //服务器套接字地址变量赋值    struct sockaddr_in my_addr;    bzero(&my_addr, sizeof(my_addr));    my_addr.sin_family = AF_INET;   //IPV4族    my_addr.sin_port   = htons(port); //将端口号转换成网络字节序    my_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本机IP地址    //绑定TCP套接字    if( bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr)) != 0)    {        perror("bind");        close(sockfd);              exit(-1);    }    //监听    if( listen(sockfd, 10) != 0)    {        perror("listen");        close(sockfd);              exit(-1);    }    printf("Listenning at port=%d\n",port);   //打印端口号信息    printf("Usage: http://127.0.0.1:%d/html/index.html\n", port);    while(1)    {        char cli_ip[INET_ADDRSTRLEN] = {0};  //存放客户端点分十进制IP地址        struct sockaddr_in client_addr;        socklen_t cliaddr_len = sizeof(client_addr);        //等待客户端连接        int connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);        printf("connfd=%d\n",connfd); //打印已连接套接字        if(connfd > 0)        {            if(fork() == 0)  //创建进程并判断返回值            {                close(sockfd);                //子进程执行                int  fd = 0;                int  len = 0;                char buf[1024] = "";                char filename[50] = "";                //将网络字节序转换成点分十进制形式存放在cli_ip中                inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);                printf("connected form %s\n\r", cli_ip);   //打印点分十进制形式的客户端IP地址                recv(connfd, buf, sizeof(buf), 0);   //接收客户端发送的请求内容                sscanf(buf, "GET /%[^ ]", filename);   //解析客户端发送请求字符串                printf("filename=*%s*\n", filename);                fd = open(filename, O_RDONLY);   //以只读方式打开文件                if( fd < 0)   //如果打开文件失败                {                    //HTTP失败头部                    char err[]= "HTTP/1.1 404 Not Found\r\n"                                "Content-Type: text/html\r\n"                                "\r\n"                                  "<HTML><BODY>File not found</BODY></HTML>";                    perror("open error");                                       send(connfd, err, strlen(err), 0);                    close(connfd);  //关闭已连接套接字                    exit(0);    //子进程退出                }                //打开文件成功后                //接收成功时返回的头部                char head[]="HTTP/1.1 200 OK\r\n"                            "Content-Type: text/html\r\n"                            "\r\n";                send(connfd, head, strlen(head), 0);  //发送HTTP请求成功头部                while( (len = read(fd, buf, sizeof(buf))) > 0)   //循环读取文件内容                {                    send(connfd, buf, len, 0);       //将读得的数据发送给客户端                }                close(fd);   //成功后关闭文件                close(connfd);   //关闭已连接套接字                exit(0);     //子进程退出            }        }           close(connfd);   //父进程关闭连接套接字    }    close(sockfd);    printf("exit main!\n");    return 0;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125

本教程示例代码下载请点此链接:http://download.csdn.net/detail/tennysonsky 
github下载路径:https://github.com/mikejiangsky/http_communication.git


原创粉丝点击