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

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;
}

终端运行服务器程序:

浏览器请求服务,得到相应内容:

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;
}

本教程示例代码下载请点此链接:http://download.csdn.net/detail/tennysonsky

github下载路径:https://github.com/mikejiangsky/http_communication.git

时间: 2024-10-10 00:19:07

HTTP协议浅析(下): 使用HTTP协议实现通信的相关文章

在ns2下添加新协议:

http://blog.csdn.net/fhtingtian/article/details/5362653 在NS2想要添加一个协议,至少要实现如下8个步骤.下面以ns2中的ping为例子说明,在版本2.29中已存在ping,这里改为bing. 1.因为ping在文件夹apps下,这里就在该目录下(自己新建协议时,应该建立新的文件夹bing)新建文件bing.h bing.cc. 2.在bing.h中定义为packet头定义一个struct数据类型, 例如 "struct hdr_bing&

【重磅】移动网络性能揭秘(下)--网络协议及性能提升实践

网络协议的性能 现在轮到我们实际上可以控制的东西了. 网络处理的性能与延迟时间的增加是不成比例的.这是由于大多数网络协议的内在操作是双向信息交换.本章的其余部分则侧重于理解为什么会产生这些信息交换以及如何减少甚至消除它们交换的频率. 图3:网络协议 传输控制协议 传输控制协议(TCP)是一种面向连接.基于ip的传输协议.TCP影响下的无差错双工通信信道对其他协议如HTTP或TLS来说都必不可少. TCP展示了许多我们需要尽量避免的双向通讯.这其中一些可以通过采用扩展协议如TCP Fast Ope

浅析低延迟直播协议设计:RTP/RTCP

转自:http://blog.csdn.net/dj0379/article/details/51960237 如今的直播市场非常火爆,有很多直播云服务的提供商可供产品选择.同时视频直播产品喷涌而出,比如大家耳熟能详的映客.YY,还有最近特别火爆的一直播. 基于TCP的协议延迟不够低 众所周知,直播中通用CDN大部分提供的是RTMP的方案以及HLS的方案.HLS在手机H5里面的兼容性非常好,而RTMP是Adobe的协议,它在延迟.稳定性和分发质量方面平衡得很不错.但是当涉及会议场景时,基于TCP

架构设计:系统间通信(20)——MQ:消息协议(下)

(接上文<架构设计:系统间通信(19)--MQ:消息协议(上)>) 上篇文章中我们重点讨论了"协议"的重要性,并为各位读者介绍了Stomp协议和XMPP协议.这两种协议是消息队列中两种不同使用场景下的典型代表.本文主要接续上文的篇幅,继续讨论消息队列中另一种典型协议:AMQP协议. 3-3.AMQP协议 AMQP协议的全称是:Advanced Message Queuing Protocol(高级消息队列协议).目前AMQP协议的版本为 Version 1.0,这个协议标准

Windows 下 ORA-12560: TNS: 协议适配器错误的问题

Windows 下 ORA-12560: TNS: 协议适配器错误的问题原因有三个: 1.监听服务没有起起来.windows平台个一如下操作:开始---程序---管理工具---服务,打开服务面板,启动oraclehome92TNSlistener服务. 2.database instance没有起起来.windows平台如下操作:开始---程序---管理工具---服务,打开服务面板,启动oracleserviceXXXX,XXXX就是你的database SID. 3.注册表问题.regedit

前端接入HTTP协议浅析

目录 1.           HTTP协议简介... 1 2.           协议流程... 1 2.1        HTTP/1.0的通信过程... 1 2.2        HTTP /1.1的通信过程... 2 3.           HTTP请求... 2 3.1        请求行... 2 3.2      HTTP头域... 3 3.2.1      通用头域... 3 3.2.2      请求头域... 4 3.2.3      响应头域... 4 3.2.4  

下拉滚动协议文本框展示样式(不可删除文本内容)

  <!doctype html>   <html>   <head>   <meta charset="utf-8">   <title>协议文本框展示样式</title>   <style type="text/css">       .protocol_detail_content {   width: 600px;   height: 410px;   padding: 40

Linux内核--网络栈实现分析(九)--传输层之UDP协议(下)

本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7549340 更多请查看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析.”(下)“表示分析是从上向下分析. 上篇分析了应用层经过BSD s

刨根问底 HTTP 和 WebSocket 协议(下)

上篇介绍了HTTP1.1协议的基本内容,这篇文章将继续分析WebSocket协议,然后对这两个进行简单的比较. WebSocket WebSocket协议还很年轻,RFC文档相比HTTP的发布时间也很短,它的诞生是为了创建一种「双向通信」的协议,来作为HTTP协议的一个替代者.那么首先看一下它和HTTP(或者HTTP的长连接)的区别. 为什么要用 WebSocket 来替代 HTTP 上一篇中提到WebSocket的目的就是解决网络传输中的双向通信的问题,HTTP1.1默认使用持久连接(pers