【linux】网络基础 ---- 应用层

news2024/11/27 10:48:13

1. 再谈 "协议"

协议是一种 "约定",在读写数据时, 都是按 "字符串" 的方式来发送接收的.

但是这里我们会遇到一些问题:

如何确保从网上读取的数据是否是完整的,区分缓冲区中的由不同客户端发来的数据

2. 网络版计算器

举例:我们需要实现一个服务器版的加法器. 我们需要客户端把要计算的两个加数发过去, 然后由服务器进行计算, 最后再把结果返回给客户端

约定方案一:

客户端发送一个形如"1+1"的字符串;

这个字符串中有两个操作数, 都是整形;

两个数字之间会有一个字符是运算符, 运算符只能是 + ;

数字和运算符之间没有空格;

结果:不可取,无法判断从网上读取的数据是否是完整的

约定方案二:

定义结构体来表示我们需要交互的信息;

发送数据时将这个结构体按照一个规则转换成字符串(这个过程叫做 "序列化"), 接收到数据的时候再按照相同的规则把字符串转化回结构体;(这个过程叫做“反序列化”)

自定义序列化,反序列化

#include<iostream>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string>
#include<memory>
#include<unistd.h>
using namespace std;
#include"head.hpp"
class sock
{
public:
     sock(uint16_t port = 8080)
    :_port(port)
    {}
    void Init()
    {
        fd = socket(AF_INET,SOCK_STREAM,0);
        if(fd < 0)
        {
            cout << "socket fail" << endl;
            exit(1);
        }
    }
    void Bind()
    {
        sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(_port);
        addr.sin_addr.s_addr = inet_addr("0.0.0.0");
        socklen_t len = sizeof(addr);
        int n = bind(fd,(struct sockaddr*)&addr,len);
        if(n < 0)
        {
            cout << "bind fail" << endl;
            exit(2);
        }
    }
    void Listen()
    {
        int n = listen(fd,0);
        if(n < 0)
        {
            cout << "listen fail" << endl;
            exit(3);
        }
    }
    void Accept(int &client_fd)
    {
        sockaddr_in addr;
        socklen_t len = sizeof(addr);
        client_fd = accept(fd,(struct sockaddr*)&addr,&len);
        if(client_fd < 0)
        {
            cout << "accept fail" << endl;
        }
        else
        {
            cout << "get a new link ..." << endl;
        }
    }
    int  Connect(const uint16_t& port,const string &s)
    {
        sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(s.c_str());
        socklen_t len = sizeof(addr);
        int n = connect(fd,(struct sockaddr*)&addr,len);
        if(n < 0)
        {
            cout << "connect fail" << endl;
            exit(4);
        }
        return fd;
    }
private:
    int fd;
    uint16_t _port;
};
#include<string>
string package(const string &content)
{
     //len\ncontent\n
     int n = content.size();
     string s = to_string(n);
     s += '\n';
     s += content;
     s += '\n';
     return s;
}
string prase(string &package)
{
    //len  \n  content  \n
    size_t pos = package.find('\n');
    if(pos == string :: npos)
    {
        return nullptr;
    }
    int len_package = package.size();
    string l = package.substr(0,pos);
    int len = stoi(package.substr(0,pos));
    if(l.size() + 2 + len > len_package)
    {
        return nullptr;
    }
    string s;
    s = package.substr(pos + 1,len);
    package.erase(0,l.size() + 2 + len);
    return s;

}
struct protocol_client
{
    protocol_client(int x = 0,int y = 0,char op = '+')
    :_x(x)
    ,_y(y)
    ,_op(op)
    {}
    string serialize()
    {
        // _x _op _y
        string s;
        s += to_string(_x);
        s += " ";
        s += _op;
        s += " ";
        s += to_string(_y);
        return s;
    }
    void deserialize(const string &s)
    {
        int left = s.find(' ');
        if(left == string :: npos)
        {
            cout << "protocol_client : find fail" << endl;
        }
        int right = s.rfind(' ');
        if(right == string :: npos)
        {
            cout << "protocol_client : find fail" << endl;
        }
        if(left + 2 != right)
        {
            cout << "protocol_client : deserialize fail" << endl;
            return ;
        }
        _x = stoi(s.substr(0,left));
        _y = stoi(s.substr(right + 1));
        _op = s[left + 1];
    }


    int _x;
    int _y;
    char _op;
};
struct protocol_server
{
    protocol_server(int result = 0,int code = 0)
    :_code(code)
    , _result(result)
    {}
    string serialize()
    {
        //_result _code
        string s;
        s += to_string(_result);
        s += " ";
        s += to_string(_code);
        return s;
    }
    void deserialize(const string &s)
    {
        size_t pos = s.find(" ");
        if(pos == string :: npos)
        {
            cout << "protocol_client : find fail" << endl;
        }
        _result = stoi(s.substr(0,pos));
        _code = stoi(s.substr(pos + 1));
    }

int _code;
int _result;
};
#include"server.hpp"
int main()
{
     
    unique_ptr<server> sv(new server());
    sv->start();
    sv->run();
    return 0;
}
#include "sock.hpp"
#include"protocol.hpp"
class server
{
public:
     server()
     {}
     void start()
     {
        svr.Init();
        svr.Bind();
        svr.Listen();
     }
     void run()
     {
        int client_fd;
        svr.Accept(client_fd);
        string e;
        while(true)
        {
            //读端
            char buff[1024];
            ssize_t n = read(client_fd, buff, sizeof(buff));
            if (n < 0)
            {
                cout << "server : read fail" << endl;
            }
            buff[n] = 0;
            
            e += buff;
            string s = prase(e);
            protocol_client sv;
            sv.deserialize(s);
            
            //计算
            int result,code = 0;
            switch(sv._op)
            {
                case '+':
                result = sv._x + sv._y;
                break;
                case '-':
                result = sv._x - sv._y;
                break;
                case '*':
                result = sv._x * sv._y;
                break;
                case '/':
                if(sv._y == 0)
                {
                    code = 1;
                }
                else
                {
                    result = sv._x / sv._y;
                }
                break;
                default:
                code = 2;
            }

            protocol_server ss(result,code);
            s = ss.serialize();
            s = package(s);

            //写端
            
            n = write(client_fd,s.c_str(),s.size());
            if(n < 0)
            {
                cout << "server : write fail" << endl;
            }
        }
        
     }
private: 
    sock svr;
};
#include"sock.hpp"
#include"protocol.hpp"
void usage()
{
    cout << "x + y = ? " << endl;
}

int main()
{
    uint16_t port = 8080;
    sock client;
    client.Init();
    int fd = client.Connect(port,"1.94.49.66");
    string e;
    while(true)
    {
        usage();
        int x,y;
        char op;
        //写端
        cout << "please enter: x >   ";
        cin >> x;
        cout << "please enter: op >   ";
        cin >> op;
        cout << "please enter: y >   ";
        cin >> y;
        protocol_client sv(x,y,op);
        string s = sv.serialize();
        s = package(s);
        
        ssize_t n = write(fd,s.c_str(),s.size());
        if(n < 0)
        {
            cout << "client : write fail" << endl;
        }
        

        //读端
        char buff[1024];
        n = read(fd,buff,sizeof(buff));
        if(n < 0)
        {
            cout << "client : read fail" << endl;
        }
        buff[n] = 0;
        e += buff;
        s = prase(e);  
        protocol_server ss;
        ss.deserialize(s);
        
        cout << "result : " << ss._result << "    code : " << ss._code << endl;

        cout << "-------------------------------------------------------------" << endl << endl;
    }
    
    return 0;
}

 json类

#include<jsoncpp/json/json.h>
int main()
{
    Json::Value root;
    root["x"] = 100;
    root["y"] = 200;
    root["op"] = '+';
    root["dect"] = "this is a + oper";
    Json::FastWriter w;
    string res = w.write(root);
    cout << res << endl;
    return 0;
}
Json::Value v;
Json::Reader r;
r.parse(res,v);
int x = v["x"].asInt();
int y = v["y"].asInt();
char op = v["op"].asInt();
string dect = v["dect"].asString();

3. HTTP协议

(一)认识 HTTP协议

应用层协议是我们程序员自己定的,但实际上, 已经有一些现成的, 又非常好用的应用层协议, 供我们直接参考使用,如 HTTP(超文本传输协议) 就是其中之一.

(二)认识URL

平时我们说 "网址" 就是URL

注意:

  1. 像网站这种,一般默认会添加协议方案名,且协议方案名对应唯一一个端口号,所以即使不写端口号,也没关系(如:http 对应端口号 80,https 对应端口号 443)
  2. web根目录不一定是linux系统下的根目录,具体指什么,完全由服务器那边解释

(三)urlencode(编码) 和 urldecode(解码)

像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现

所以,如果某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.

转义的规则如下(这个过程叫编码):

将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式

编码解码工具:

URL 编码解码 - Codeeeee 在线小工具

4. HTTP协议格式

注意:

  1. 上图对应格式是打印出来的格式,实际上,发送到网络上的是整个字符串,是连在一起的
  2. http也是一种协议,它也需要将数据序列化,反序列化

代码

5. HTTP的方法

其中最常用的就是 GET方法 和 POST方法

注意:

  1. GET 方法 和 POST 方法都可以传参
  2. GET 方法的参数是在 url 内的,通过 url 提交的,POST 方法的参数是在正文内
  3. POST 方法 比 GET 方法更加私密

GET 方法:

POST 方法:

6. HTTP的状态码

最常见的状态码, 比如 200(OK), 404(Not Found,一般是客户端要访问的文件不存在), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)

注意:

  1. 3XX代表的重定向有两种:一种是临时移动,另一种是长期移动
  2. 长期移动:一旦设置了永久重定向,所有后续对原始URL的请求都应该被自动转发到新的URL,而且这个重定向是长期有效的;临时移动:表明资源的移动是暂时的。 客户端在接收到临时重定向响应后,会临时使用新的URL,但在将来的某个时间点,对原始URL的请求可能会恢复为直接访问原始资源,而不是被重定向

HTTP常见Header

Content-Type: 数据类型(text/html等)

Content-Length: Body的长度

Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;

User-Agent: 声明用户的操作系统和浏览器版本信息;

referer: 当前页面是从哪个页面跳转过来的;

location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;

Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能

注意:

  1. Content-Type : 指明访问的路径是什么类型(如:.html : 网页 ; .jpg :jpg格式的图片 ;.png : png格式的图片 ...... )
  2. Cookie : Cookie文件可能是内存级的(储存在内存里),也可能是文件级的(储存在磁盘里)

  1. cookie文件的内容保存多久,一般是由浏览器决定的,如果内容没有了,则下一次访问对应服务器,仍要进行验证
  2. http协议默认是无状态的
  3. http对登入用户的会话保持功能

7. 长连接和短连接

  1. 即使是一个网页,也可能包含很多元素,每一个元素,都要进行一次http请求,即建立一个tcp连接,就会发生多个请求和响应,这就是长连接
  2. 只有一次请求和响应,连接就断开的,就是短连接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2239088.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言PythonBash:空白(空格、水平制表符、换行符)与转义字符

C语言 空白 C语言中的空白&#xff08;空格、水平制表符、换行符&#xff09;被用于分隔Token&#xff0c;因此Token间可以有任意多个空白。 // 例1 printf("Hello, World!"); 例1中存在5个Token&#xff0c;分别是&#xff1a; printf("Hello, World! \n&qu…

Linux基础(十四)——BASH

BASH 1.BASH定义2.shell的种类3.bash的功能3.1 命令记录功能3.2 命令补全功能3.3 命令别名设置3.4 工作控制、 前景背景控制3.5 程序化脚本&#xff1a; &#xff08; shell scripts&#xff09;3.6 万用字符 4.bash的内置命令5.shell的变量功能5.1 变量的取用5.2 新建变量5.3 …

【重学 MySQL】八十二、深入探索 CASE 语句的应用

【重学 MySQL】八十二、深入探索 CASE 语句的应用 CASE语句的两种形式CASE语句的应用场景数据分类动态排序条件计算在 SELECT 子句中使用在 WHERE子句中使用在 ORDER BY 子句中使用 注意事项 在MySQL中&#xff0c;CASE 语句提供了一种强大的方式来实现条件分支逻辑&#xff0c…

由播客转向个人定制的音频频道(1)平台搭建

项目的背景 最近开始听喜马拉雅播客的内容&#xff0c;但是发现许多不方便的地方。 休息的时候收听喜马拉雅&#xff0c;但是还需要不断地选择喜马拉雅的内容&#xff0c;比较麻烦&#xff0c;而且黑灯操作反而伤眼睛。 喜马拉雅为代表的播客平台都是VOD 形式的&#xff0…

7+纯生信,单细胞识别细胞marker+100种机器学习组合建模,机器学习组合建模取代单独lasso回归势在必行!

影响因子&#xff1a;7.3 研究概述&#xff1a; 皮肤黑色素瘤&#xff08;SKCM&#xff09;是所有皮肤恶性肿瘤中最具侵袭性的类型。本研究从GEO数据库下载单细胞RNA测序&#xff08;scRNA-seq&#xff09;数据集&#xff0c;根据原始研究中定义的细胞标记重新注释各种免疫细胞…

uniapp解析蓝牙设备响应数据bug

本文章为了解决《uniapp 与蓝牙设备收发指令详细步骤(完整项目版)》中第十步的Array 解析成 number函数bug 1、原代码说明 function array16_to_number(arrayValue) {const newArray arrayValue.filter(item > String(item) ! 00 || String(item) ! 0)const _number16 ne…

【递归回溯与搜索算法篇】算法的镜花水月:在无尽的自我倒影中,递归步步生花

文章目录 递归回溯搜索专题&#xff08;一&#xff09;&#xff1a;递归前言第一章&#xff1a;递归基础及应用1.1 汉诺塔问题&#xff08;easy&#xff09;解法&#xff08;递归&#xff09;C 代码实现时间复杂度和空间复杂度易错点提示 1.2 合并两个有序链表&#xff08;easy…

大数据开发面试宝典

312个问题&#xff0c;问题涵盖广、从自我介绍到大厂实战、19大主题&#xff0c;一网打尽、真正提高面试成功率 一、Linux 1. 说⼀下linux的常⽤命令&#xff1f; 说一些高级命令即可 systemctl 设置系统参数 如&#xff1a;systemctl stop firewalld关闭防火墙 tail / hea…

链表归并与并集相关算法题|两递增归并为递减到原位|b表归并到a表|两递减归并到新链表(C)

两递增归并为递减到原位 假设有两个按元素递增次序排列的线性表&#xff0c;均以单链表形式存储。将这两个单链表归并为一个按元素递减次序排列的单链表&#xff0c;并要求利用原来两个单链表的节点存放归并后的单链表 算法思想 因为两链表已按元素值递增次序排列&#xff0…

【RabbitMQ】06-消费者的可靠性

1. 消费者确认机制 没有ack&#xff0c;mq就会一直保留消息。 spring:rabbitmq:listener:simple:acknowledge-mode: auto # 自动ack2. 失败重试机制 当消费者出现异常后&#xff0c;消息会不断requeue&#xff08;重入队&#xff09;到队列&#xff0c;再重新发送给消费者。…

【陕西】《陕西省省级政务信息化项目投资编制指南(建设类)(试行)》-省市费用标准解读系列07

《陕西省省级政务信息化项目投资编制指南&#xff08;建设类&#xff09;&#xff08;试行&#xff09;》规定了建设类项目的费用投资测算方法与计价标准&#xff0c;明确指出建设类项目费用包括项目建设费和项目建设其他费&#xff08;了解更多可直接关注咨询我们&#xff09;…

VB6.0桌面小程序(桌面音乐播放器)

干货源码 Imports System.IO Imports System.Security.Cryptography Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load Button1.Text “上一曲” Button4.Text “播放” Button3.Text “下一曲” Button2.Text “顺序播…

docker安装jdk8

1、拉取镜像 docker pull openjdk:82、运行镜像 docker run -d --restartalways --network portainer_network -it --name jdk8 openjdk:8命令 作用 docker run 创建并启动一个容器 –name jdk8 将容器取名为jdk8 -d 设置后台运行 –restartalways 随容器启动 –network port…

【人工智能】Transformers之Pipeline(二十三):文档视觉问答(document-question-answering)

​​​​​​​ 目录 一、引言 二、文档问答&#xff08;document-question-answering&#xff09; 2.1 概述 2.2 impira/layoutlm-document-qa 2.2.1 LayoutLM v1 2.2.2 LayoutLM v2 2.2.3 LayoutXLM 2.2.4 LayoutLM v3 2.3 pipeline参数 2.3.1 pipeline对象实例化…

微服务day06

MQ入门 同步处理业务&#xff1a; 异步处理&#xff1a; 将任务处理后交给MQ来进行分发处理。 MQ的相关知识 同步调用 同步调用的小结 异步调用 MQ技术选型 RabbitMQ 安装部署 其中包含几个概念&#xff1a; publisher&#xff1a;生产者&#xff0c;也就是发送消息的一方 …

[CKS] K8S RuntimeClass SetUp

最近准备花一周的时间准备CKS考试&#xff0c;在准备考试中发现有一个题目关于RuntimeClass创建和挂载的题目。 ​ 专栏其他文章: [CKS] Create/Read/Mount a Secret in K8S-CSDN博客[CKS] Audit Log Policy-CSDN博客 -[CKS] 利用falco进行容器日志捕捉和安全监控-CSDN博客[CKS…

Halcon基于laws纹理特征的SVM分类

与基于区域特征的 SVM 分类不同&#xff0c;针对图像特征的 SVM 分类的算子不需要直接提取 特征&#xff0c;下面介绍基于 Laws 纹理特征的 SVM 分类。 纹理在计算机视觉领域的图像分割、模式识别等方面都有着重要的意义和广泛的应 用。纹理是指由于物体表面的物理属性不同所…

初始Python篇(6)—— 字符串

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; Python 目录 字符串的常见操作 格式化字符串 占位符 f-string 字符串的 format 方法 字符串的编码与解码 与数据验证相关的方法 …

基于Spring Boot+Vue的养老院管理系统【原创】

一.系统开发工具与环境搭建 1.系统设计开发工具 后端使用Java编程语言的Spring boot框架 项目架构&#xff1a;B/S架构 运行环境&#xff1a;win10/win11、jdk17 前端&#xff1a; 技术&#xff1a;框架Vue.js&#xff1b;UI库&#xff1a;ElementUI&#xff1b; 开发工具&…

Maven 中央仓库地址 mvnrepository.com

下载一些 jar 包驱动&#xff0c;不需用去官网下了&#xff0c;直接去 Maven 中央仓库&#xff0c;高效、简单 Maven 中央仓库地址 https://mvnrepository.com/open-source 我们下期见&#xff0c;拜拜&#xff01;