协议是一种 “约定”. socket api的接口, 在读写数据时, 都是按 “字符串” 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢?
结构化的数据:比如发送微信消息,消息的组合由时间,发送人等信息
序列化:发送信息的时候、需要将信息多变一
反序列化:接收信息、将信息一变多
例如, 我们需要实现一个服务器版的加法器. 我们需要客户端把要计算的两个加数发过去, 然后由服务器进行计算, 最后再把结果返回给客户端。
约定方案:
这种约定, 就是 应用层协议。
//Protocol.hpp:
#ifndef __PROTOCOL_HPP_
#define __PROTOCOL_HPP_
#include
//用结构体定的协议
typedef struct request
{
int x;
int y;
char op;
}request_t;
typedef struct response
{
int result;
int code;
}response_t;
#endif
#ifndef _SERVER_HPP_
#define _SERVER_HPP_
#include
#include
#include
#include
#include
#include
#include
#include
#include "Protocol.hpp"
class server
{
private:
int port;
int lsock;
public:
server(int _port):port(_port),lsock(-1)
{
}
void initServer()
{
//创建套接字
lsock = socket(AF_INET, SOCK_STREAM, 0);
if(lsock < 0)
{
std::cerr << "socket error" << std::endl;
exit(2);
}
//服务器绑定,填充服务器信息
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(lsock, (struct sockaddr*)&local, sizeof(local)) < 0)//传到inet_sock
{
std::cerr << "bind error! " << std::endl;
exit(3);
}
//监听套接字
if(listen(lsock, 5) < 0)
{
std::cerr << "listen error!" << std::endl;
//exit(4);
}
}
void cal(int sock)
{
//短链接来完成计算
request_t rq;
response_t rsp = {
4, 0};
ssize_t s = recv(sock, &rq, sizeof(rq), 0);//BUG
if(s > 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.result = rq.x / rq.y;
}
else
{
rsp.code = 1;
}
break;
case '%':
if(rq.y != 0)
{
rsp.result = rq.x % rq.y;
}
else
{
rsp.code = 2;
}
break;
default:
rsp.code = 3;
break;
}
}
send(sock, &rsp, sizeof(rsp), 0);
close(sock);
}
void start()
{
struct sockaddr_in peer;
for(;;)
{
socklen_t len = sizeof(len);
int sock = accept(lsock, (struct sockaddr*)&peer, &len);
if(sock < 0)
{
std::cerr << "accept error!" << std::endl;
continue;
}
if(fork() == 0)
{
if(fork() > 0)//子进程
{
exit(0);
}
//孙子
close(lsock);
//具体服务
cal(sock);
exit(0);//短链接、调用完就退出
}
//父进程
close(sock);
waitpid(-1, nullptr, 0);//等待任意子进程
}
}
~server()
{
close(lsock);
}
};
#endif
#include "Server.hpp"
void Menu(std::string proc)
{
std::cout << "Usage: \n\t";
std::cout << proc << "svr_ip svr_port" << std::endl;
}
int main(int argc, char *argv[])
{
if(argc != 2)
{
Menu(argv[0]);
exit(1);
}
server *sp = new server(atoi(argv[1]));
sp->initServer();
sp->start();
delete sp;
return 0;
}
#ifndef _CLIENT_HPP_
#define _CLIENT_HPP_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "Protocol.hpp"
class client
{
private:
int port;
int sock;
std::string ip;
public:
client(std::string _ip, int _port):ip(_ip),port(_port),sock(-1)
{
}
void initClient()
{
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0)
{
std::cerr << "socket error" << std::endl;
exit(2);
}
}
void start()
{
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 << "cennect error!" << std::endl;
exit(2);
}
response_t rsp;
request_t rq;
std::cout << "datax# ";
std::cin >> rq.x;
std::cout << "datay# ";
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 << "code:" << rsp.code << std::endl;
std::cout << "result: " << rsp.result << std::endl;
}
~client()
{
close(sock);
}
};
#endif
#include "Client.hpp"
void Menu(std::string proc)
{
std::cout << "Usage: \n\t";
std::cout << proc << "svr_ip svr_port" << std::endl;
}
int main(int argc, char *argv[])
{
if(argc != 3)
{
Menu(argv[0]);
exit(1);
}
client *cp = new client(argv[1], atoi(argv[2]));
cp->initClient();
cp->start();
delete cp;
return 0;
}
tcpdump:
作用:抓包 (传输层的协议基本都可以抓)
tcpdump -i
指定sniffer操作的侦听端口,比如:tcpdump -i eth0 ,
tcpdump -i any(发送至这台主机的都要抓) 。
-n:主机名这些能显示成数字就显示成数字
-nn:将更多的信息显示成数字
虽然说应用层协议是由我们程序猿自己定的,但实际上已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接使用。比如 HTTP(超文本传输协议)
我们平时说的 “网址” 其实就是说的 URL。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210525152057447.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MDg0Mzg2OA==,size_16,color_FFFFFF,t_70
?:后面叫做参数
互联网行为:
http:端口80 https:端口443