当前位置: 首页 > news >正文

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

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\nint n = content.size();string s = to_string(n);s += '\n';s += content;s += '\n';return s;
}
string prase(string &package)
{//len  \n  content  \nsize_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 _ystring 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 _codestring 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.mrgr.cn/news/70701.html

相关文章:

  • 关于安卓模拟器或手机设置了BurpSuite代理和安装证书后仍然抓取不到APP数据包的解决办法
  • springcloud-网关路由gateway
  • JAVA中的Lamda表达式
  • Python的3D可视化库 - vedo (2)visual子模块 基本可视化行为
  • 机器学习阶段学习Day31
  • 使用Python 在Excel中创建和取消数据分组 - 详解
  • PCL 点云拟合 Ransac拟合圆柱
  • FastHTML快速入门:服务器渲染超媒体应用的利器
  • 并发编程(9)——Actor/CSP设计模式
  • js 数据类型=》理解=》应用
  • 【Android、IOS、Flutter、鸿蒙、ReactNative 】文本点击事件
  • 工具收集 - java-decompiler / jd-gui
  • Property ‘name‘ does not exist on type ‘Object‘.
  • 三十七、Python基础语法(异常)
  • 没有想到AI以这样的方式入侵人类——AI泛滥——关于AI的冷思考
  • 避免内存陷阱:掌握memcpy和memmove的正确用法
  • 智慧园区解决方案:科技赋能,打造未来管理新典范
  • 常见计算机网络知识整理(未完,整理中。。。)
  • Linux(CentOS)项目总结(前后端分离)
  • 【寻找重复数字】——脑筋急转弯...
  • 深入理解分支预测原理,揭开AMD Zen 5的高性能秘诀
  • 项目管理中不可或缺的能力
  • Qt文件系统-二进制文件读写
  • 【优选算法 — 滑动窗口】水果成篮 找到字符串中所有字母异位词
  • 函数
  • Flink独立集群+Flink整合yarn