[linux]linux网络编程之http协议详解(代码片段)

哦哦呵呵 哦哦呵呵     2022-12-20     407

关键词:

1. 自定制协议

在上一篇socket编程中谈到了协议,程序员系的网络程序,全部都部署在应用层。
协议是一种“约定”,socket api的接口,在读写数据的时候,都是按照“字符串”的方式来进行发送和接受。那如果我们要传输的是结构化数据,怎么处理?这时候就需要我们用到自定义协议,可以自己写一套规则,服务器通过固定的方式解析数据,达到自定制协议的目的。

程序示例

// 以下是服务端部分代码
typedef struct request

    int x;
    int y;
    char op;
request_t;

typedef struct response

    int code;
    int result;
response_t;


void CalResult(int sock)

    // 短链接 完成对应的计算
    request_t rq;
    response_t rsp4, 0;
    ssize_t s = recv(sock, &rq, sizeof(rq), 0);
    if (s > 0)
    
        rsp.code = 0;
        switch(rq.op)
        
            case '+':
                rsp.result = rq.x + rq.y;
                break;
            case '-':
                rsp.result = rq.x - rq.y;
                break;
            case '*':
                rsp.result = rq.x * rq.y;
                break;
            case '/':
                if (rq.y == 0)
                
                    rsp.code = 1;
                
                else 
                
                    rsp.result = rq.x / rq.y;
                
                break;
            case '%':
                if (rq.y == 0)
                
                    rsp.code = 2;
                
                else 
                
                    rsp.result = rq.x % rq.y;
                
                break;
            default:
                rsp.code = 3;
                break;
        
    

    std::cout << "cal success!" << std::endl;
    send(sock, &rsp, sizeof(rsp), 0);
    
    close(sock);


// 客户端部分代码
void StartClient()

    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(ip.c_str()) ;

    if(connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0)
    
        std::cerr << "connect err.." << std::endl;
        exit(2);
    
   
    request_t rq;
    response_t rsp;
    std::cout << "data1# ";
    std::cin >> rq.x;
    std::cout << "data2# ";
    std::cin >> rq.y;
    std::cout << "op# ";
    std::cin >> rq.op;

    send(sock, &rq, sizeof(rq), 0);
    recv(sock, &rsp, sizeof(rsp), 0);

    std::cout << "cod: " << rsp.code << std::endl;
    std::cout << "result: " << rsp.result << std::endl;

2. HTTP协议

2.1 概述

HTTP协议是建立在TCP/IP协议之上的,是TCP/IP的上层协议,HTTP的数据通过TCP/IP层进行转发。
HTTP协议是无连接、无状态、工作在应用层的协议。

  • 无连接
    http协议本身是不建立连接的,http直接向对方发送http request即可,但是http在传输层使用的是tcp协议,tcp协议在传输数据的时候是需要建立连接,才可以通信的。
  • 无状态
    http协议本身是对请求和响应不做保存,双方的状态是服务端实现的会话机制进行保存的。

2.2 URL统一资源定位符

  • 服务器地址: 使用绝对 URI 必须指定待访问的服务器地址。地址可以是类似hackr.jp 这种 DNS 可解析的名称,或是 192.168.1.1 这类 IPv4 地址
    名,还可以是 [0:0:0:0:0:0:0:1] 这样用方括号括起来的 IPv6 地址名。
  • 服务器端口号: 指定服务器连接的网络端口号。此项也是可选项,若用户省略则自动使用默认端口号。
  • 带层次的文件路径(http://域名/路径): 代表的是向服务器后台请求资源的路径,路径中第一个’/'代表web根目录,服务端可以指定任意一个路径作为http服务端的根目录的起始路径
  • 查询字符串(key=value): 指定的是客户端向服务器提交的数据,多组时间之间用&连接。

URL中的特殊字符的转义

  • urlencode:
    在URL有一些特殊字符需要转义,对具有通俗意义的字符进行转码,采用16进制进行显示。例如下方字符,需要使用’%’ + urlencode,其中%是告诉服务器后面的内容是经过转义的。
  • urldecode: 将特殊字符编码转化回来。

2.3 HTTP协议格式

2.3.1 HTTP请求报文格式


上述就是一个请求报文的格式:
每一行都是用 \\r\\n作为改行的结束标记

  • 首行: 称为请求行,用空格分为三部分
      请求方法:GET… 其它方法下文介绍
      url: …想要请求资源所在的路径
      http版本: HTTP/1.1
  • 请求报头:
      每一行都是一个键值对 key: value\\r\\n
      其中Content-Length: 表示请求正文的长度,从请求报头后的多少个字节内容是请求正文。
  • 空行 \\r\\n用于分离请求报头与有效载荷
  • 请求正文(有效载荷): GET方法不需要携带正文,POST必须携带正文

请求方法

  • GET: 向服务端请求某些资源,也可以给服务端提交少量的数据(url),url的长度是有限制的,提交的请求会在地址栏中显示出来。
  • POST: 向服务器提交某些数据,提交的数据在请求报文中存储,提交时地址栏看不到提交内容,相对GET方法较私密。
  • HEAD: 获取响应报文头部信息,并没有获取响应正文,是为了测试资源是否有效。
  • DELETE:删除文件
  • PUT: 传输文件
    DELETE和PUT两种方法,http都没有校验,一般情况后台的服务端不支持PUT和DELETE方法
  • OPTIONS:询问服务端支持的方法

2.3.1 HTTP响应报文格式


每一行都是用 \\r\\n作为改行的结束标记

  • 首行: 响应行,三部分
      协议版本: http/1.1
      状态码: 200
      状态码描述
  • 响应报头
      由很多key/value构成
  • 空行
  • 响应正文: 是发送请求后,服务器向客户端发送的数据

2.4 状态码及解释

  • 2XX 成功, 2XX 的响应结果表明请求被正常处理了。
    200 OK表示从客户端发来的请求在服务器端被正常处理了。
    204 No Content
    206 Partial Content
  • 3XX 重定向
    301 Moved Permanently 永久性重定向。
    302 Found 临时性重定向
    303 See Other
    307 Temporary Redirect 临时重定向。
  • 4XX 客户端错误
    400 Bad Request
    401 Unauthorized 认证失败
    403 Forbidden
    404 Not Found
  • 5XX 服务器错误
    500 Internal Server Error 该状态码表明服务器端在执⾏请求时发⽣了错误。也有可能是 Web应⽤存在的 bug 或某些临时的故障。
    503 Service Unavailabl

2.5 HTTP的响应首部字段

这里只列举最常见的几种

  • Content-Type: 正文类型 text、html、css、js等
  • Content-Length: 正文长度
  • Host: 客户端告知服务器,所请求的资源是在哪个主机的哪个端口上
  • User-Agent: 声明用户的操作系统和浏览器版本信息
  • Referer: 当前页面是从哪个页面跳转过来的
  • Location: 搭配3xx状态码使用,告诉客户端接下来去哪访问
  • Conection: keep-alive 保持长连接
  • Cookie:下方详解

Cookie
1.概念

  • 用于在客户端浏览器存储少量的信息,通常用于实现会话的功能,本质是浏览器中的一个文件
  • cookie是服务器返回给浏览器,由浏览器进行保存cookie
  • 在访问服务器的其它页面时,由浏览器自动在请求体当中加上cookie

2.作用
  服务端通过cookie当中的value值,可以得到服务端生成session,通过会话id,可以在服务端查询出来是哪一个用户的session。浏览器通过请求中的cookie信息提交到服务器,服务端就可以通过cookie保存的会话信息,进行会话校验。

3. HTTP和HTTPS

3.1 HTTP

  我们已了解到 HTTP 具有相当优秀和方便的一面,然而HTTP 并非只有好的一面,事物皆具两面性,它也是有不足之处的。
HTTP 主要有这些不足,例举如下。

  • 通信使用明文(不加密),内容可能会被窃听
  • 不验证通信方的身份,因此有可能遭遇伪装
  • 无法证明报文的完整性,所以有可能已遭篡改

  这些问题不仅在 HTTP 上出现,其他未加密的协议中也会存在这类问题。除此之外,HTTP 本身还有很多缺点。而且,还有像某些特定的 Web服务器和特定的 Web 浏览器在实际应用中存在的不足(也可以说成是脆弱性或安全漏洞),另外,用 Java 和 PHP 等编程语言开发的
Web 应用也可能存在安全漏洞。

3.2 HTTPS

  HTTP 协议中没有加密机制,但可以通过和 SSL(Secure Socket Layer,安全套接层)或TLS(Transport Layer Security,安全层传输协议)的组合使用,加密 HTTP 的通信内容。
  用 SSL建立安全通信线路之后,就可以在这条线路上进行 HTTP通信了。与 SSL组合使用的 HTTP 被称为 HTTPS(HTTPSecure,超文本传输安全协议)或 HTTP over SSL。

HTTP+ 加密 + 认证 + 完整性保护 = HTTPS

HTTPS中的认证方式

  SSL采用一种叫做公开密钥加密(Public-key cryptography)的加密处理方式。近代的加密方法中加密算法是公开的,而密钥却是保密的。通过这种方式得以保持加密方法的安全性。
  加密和解密都会用到密钥。没有密钥就无法对密码解密,反过来说,任何人只要持有密钥就能解密了。如果密钥被攻击者获得,那加密也就失去了意义。
  加密和解密同用一个密钥的方式称为共享密钥加密(Common keycrypto system),也被叫做对称密钥加密。
  以共享密钥方式加密时必须将密钥也发给对方。可究竟怎样才能安全地转交?在互联网上转发密钥时,如果通信被监听那么密钥就可会落入攻击者之手,同时也就失去了加密的意义。另外还得设法安全地保管接收到的密钥。

使用两把密钥的公开密钥加密
  公开密钥加密方式很好地解决了共享密钥加密的困难。公开密钥加密使用一对非对称的密钥。一把叫做私有密钥(private key),另一把叫做公开密钥(public key)。顾名思义,私有密钥不能让其他任何人知道,而公开密钥则可以随意发布,任何人都可以获得。
  使用公开密钥加密方式,发送密文的一方使用对方的公开密钥进行加密处理,对方收到被加密的信息后,再使用自己的私有密钥进行解密。利用这种方式,不需要发送用来解密的私有密钥,也不必担心密钥被攻击者窃听而盗走。

4. HTTP代码示例

http 服务端

#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>

#define READFILESIZE 4096

class HttpServer

private:
    int port;
    int lsock;

public:
    HttpServer(int _port)
        : port(_port)
        , lsock(-1)
    

    void InitServer()
    
        signal(SIGCHLD, SIG_IGN);

        lsock = socket(AF_INET, SOCK_STREAM, 0);

        if (lsock < 0)
        
            std::cerr << "socket err..." << std::endl;
            exit(2);
        

        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY;

        if (bind(lsock, (struct sockaddr*)&local, sizeof(local)) < 0)
        
            std::cerr << "bind err..." << std::endl;
            exit(3);
        

        if (listen(lsock, 5) < 0)
        
            std::cerr << "listen err..." << std::endl;
            exit(4);
        

        // 设置端口复用,断开连接可以立即使用该端口,跳过TIME_WAIT
        int opt = 1;
        setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    

    // 读取所请求的资源,将资源内容添加到响应报文中
    std::string ReadHTMLfile(std::string file)
    
        std::string path = "./WEB";
        path += file;

        int fd = open(path.c_str(), O_RDONLY);       
        if (fd < 0)
        
            std::cerr << "open err..." << std::endl;
            exit(6);
        

        char buf[READFILESIZE];
        std::string res;
        size_t rlen;

        while (true) 
        
            rlen = read(fd, buf, sizeof(buf) - 1);
            res += buf;

            // 清空buf缓冲区 很重要
            memset(buf, '\\0', sizeof(buf));

            if (rlen < READFILESIZE - 1)
            
                break;
            
        

        return res;
    

    // 提取请求中的资源的后缀
    std::string ExtractSuffix(std::string filepath)
    
        std::string suffix;
        size_t pos = filepath.find_last_of(".");
        if (pos != std::string::npos)
        
            suffix = filepath.substr(pos + 1, filepath.size() - (filepath.size() - pos));
        
        else 
        
            suffix = "/";
        

        return suffix;
    

    std::string ResponseHead(std::string suffix)
    
        // 添加响应报头
        std::string response;
        response += "HTTP/1.1 200 OK\\r\\n";
        
        if (suffix == "/" || suffix == "html")
        
            response += "Content-type: text/html\\r\\n";
        
        else if (suffix == "css")
         
            response += "Content-type: text/css\\r\\n";
        
        else if (suffix == "js")
        
            response += "Content-type: text/javascript\\r\\n";
        
        // 图片不能正常的读取
        else if (suffix == "jpg")
        
            response += "Accept-Ranges: bytes\\r\\n";
            response += "Content-Type: image/jpeg\\r\\n";
        
        else if (suffix == "png")
        
            response += "Accept-Ranges: bytes\\r\\n";
            response += "Content-Type: image/png\\r\\n";
        

        return response;
    

    // 组装响应报文
    std::string ResponseContent(std::string filepath)
    
        // 查找文件 条件响应信息
        std::string responseContent;
        responseContent.clear();
        // 从文件中读取数据,添加至response报头
        if (filepath == "/")
        
            // 读取login.js 末尾会出现问题
            responseContent += ReadHTMLfile("/login.html");
        
        else 
        
            responseContent += ReadHTMLfile(filepath);
        

        // 查找后缀
        std::string suffix = ExtractSuffix(filepath);

        std::string response;
        response += ResponseHead(suffix);
        //response += "Content-Length: " + std::to_string(responseContent.size());
        response += "\\r\\n";
        response += responseContent;
        
        return response;
    

    // 资源提取
    std::string ExtractPath(char* request)
    
        // 提取报头信息中请求文件路径
        int posl = strcspn(request, " ");
	    int posr = strcspn(request + posl + 1, " ");

	    char buf[64];
	    memset(buf, '\\0', sizeof(buf));
	    strncpy(buf, request + posl + 1, posr);

        std::string filepath = buf;

        return filepath;
    

    void EchoHttp(int sock)
    
        char request[2048];

        size_t s = recv(sock, request, sizeof(request), 0);
        if (s > 0)
        
            request[s] = 0;
            std::cout << request << std::endl;

            std::string filepath = ExtractPath(request); 

            // 向客户端相应
            std::string response = ResponseContent(filepath);
            
            send(sock, response.c_str(), response.size(), 0);
        

        close(sock);
    

    void StartServer()
    
        struct sockaddr_in peer;
        while (true)
        
            socklen_t len = sizeof(peer);

            int sock = accept(lsock, (struct sockaddr*)&peer, &len);
            if (sock < 0)
            
                std::cerr << "accept err..." << std::endl;
                continue;
            

            std::cout << "get a new connect ... done" << std::endl;

            if (fork() == 0)
            
                // child
                close(lsock);
                
                EchoHttp(sock);

                exit(0);
            

            close(sock);
        
    

    ~HttpServer()
    
        if (lsock != -1)
        
            close(lsock);
        
    
;

[linux]linux网络之tcp协议详解(代码片段)

目录1.传输层2.端口号3.TCP协议3.1TCP协议的特性3.1.1面向连接3.1.2可靠传输3.1.3面向字节流3.2TCP报头3.3TCP连接管理及可靠性问题3.3.1可靠性3.3.2保证可靠性的机制3.3.3保证效率的机制3.3.3.1滑动窗口机制3.3.3.2拥塞控制3.4其它机制3.4.1延... 查看详情

[linux]linux网络之tcp协议详解(代码片段)

目录1.传输层2.端口号3.TCP协议3.1TCP协议的特性3.1.1面向连接3.1.2可靠传输3.1.3面向字节流3.2TCP报头3.3TCP连接管理及可靠性问题3.3.1可靠性3.3.2保证可靠性的机制3.3.3保证效率的机制3.3.3.1滑动窗口机制3.3.3.2拥塞控制3.4其它机制3.4.1延... 查看详情

linux网络基础--应用层详解(代码片段)

网络基础--应用层⏰应用层🕛再谈“协议”🕒简易网络计算器🕛HTTP协议🕒HTTP协议格式🕕认识URL🕕urlencode与urldecode🕒HTTP协议格式🕒实现一个最简单的HTTP服务器🕕简易HTTP服务器代码🕒HT... 查看详情

linux高级网络编程系列教程

一、网络应用层编程1、Linux网络编程01——网络协议入门2、Linux网络编程02——无连接和面向连接的区别3、Linux网络编程03——字节序和地址转换4、Linux网络编程04——套接字5、Linux网络编程05——C/S与B/S架构的区别6、Linux网络编... 查看详情

[linux]linux网络之数据链路层详解(代码片段)

1.数据链路层  处于TCP/IP中物理层,数据链路层的协议定义了通过通信媒介互连的设备之间的传输规范.在计算机中数据是由01表示的,实际通信时通信媒介处理的时电压的高低、光的闪灭及电波的强弱等信号。把这些信号与0... 查看详情

linux系统诊断必备技能之二:tcpdump抓包工具详解

TcpDump可以将网络中传送的数据包完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的过滤,并提供and、or、not等逻辑语句来帮助你去掉无用的信息。 查看详情

linux之netstat命令详解(代码片段)

netstat命令用于显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况。netstat是在内核中访问网络及相关信息的程序,它能提供TCP连接,TCP和UDP监听,进程内存管理的相关报告。TCP... 查看详情

linux网络基础--应用层详解(代码片段)

网络基础--应用层⏰应用层🕛再谈“协议”🕒简易网络计算器🕛HTTP协议🕒HTTP协议格式🕕认识URL🕕urlencode与urldecode🕒HTTP协议格式🕒实现一个最简单的HTTP服务器🕕简易HTTP服务器代码🕒HT... 查看详情

docker网络原理详解02(代码片段)

...间、Veth、Iptables、网桥、路由网络名称空间为了支持网络协议栈的多个实例,Linux在网络协议栈中引入了网络名称空间(NetworkNamespace),这些独立的协议栈被隔离到不同的命名空间中。处于不同的命名空间的网络协议栈是完... 查看详情

linux基础网络详解

...53。DHCP(英语:DynamicHostConfigurationProtocol,动态主机设置协议)是一个局域网的网络协议,使用UDP协议工作,主要有两个用途:用于内部网或网络服务供应商自动分配IP地址;给用户 查看详情

linux之网络基础?

网络基础网络模型有两种基本类型:协议模型和参考模型。TCP/IP模型描述了TCP/IP协议簇中每个协议层实现的功能,因此属于协议模型。开放式系统互联(OSI)模型是最广为人知的网际网络参考模型,用于数据网络设计、操作规范和... 查看详情

book

...入理解Linux内核》《深入Linux内核架构》《TCP/IP详解卷1:协议》《Linux系统编程(第2版)》《Linux内核设计与实现(第3版)》《深入理解计算机系统(原书第2版)》《计算机程序的构造和解释(原书第2版)》《编码:隐匿在计... 查看详情

网络之socket详解

网络之Socket详解1.什么是Socket?Socket本质上还是文件,因为Linux上一切皆文件。Socket也有对应的文件描述符(fd)。文件描述符相关的参考另外一篇博客。http://blog.csdn.net/weililansehudiefei/article/details/78113082在这里简单就认为,它是... 查看详情

linux之ftp服务器,nfs服务器,samba服务器详解(代码片段)

...服务:ftp,nfs,sambaFTP服务FileTransferProtocol早期的三个应用级协议之一基于C/S结构?双通道协议:数据和命令连接数据传输格式:二进制(默认)和文本两种模式:(服务器角度)主动(PORTstyle):服务器主动连接???命令(控制):客户... 查看详情

[linux]linux网络之数据链路层详解(代码片段)

1.数据链路层  处于TCP/IP中物理层,数据链路层的协议定义了通过通信媒介互连的设备之间的传输规范.在计算机中数据是由01表示的,实际通信时通信媒介处理的时电压的高低、光的闪灭及电波的强弱等信号。把这些信号与0... 查看详情

linux之socket套接字编程20160704

介绍套接字之前,我们先看一下传输层的协议TCP与UDP:TCP协议与UDP协议的区别   首先咱们弄清楚,TCP协议和UCP协议与TCP/IP协议的联系,很多人犯糊涂了,一直都是说TCP/IP协议与UDP协议的 区别,我觉得这是没有从本... 查看详情

linux内核源码分析之网络协议栈架构

Linux内核源码分析之网络协议栈架构专注于服务器后台开发,包括C/C++,Linux,内核,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2 查看详情

[linux]linux网络之socket编程入门(代码片段)

目录1.前言2.网络基础2.1协议2.1.1TCP和UDP协议2.2网络的层状结构2.3一台主机向另一台主机的发送数据的流向2.4IP和MAC地址2.5端口2.6网络字节序3.SocketAPI3.1公共接口3.2UDP接收发送数据3.3.TCP部分4.UDP及TCP的通信程序4.1UDP4.2TCP1.前言本文主... 查看详情