序列化和反序列化(Linux)

1 序列化和反序列化

write和read实质是拷贝函数

1.1序列化和反序列化的概述

2网络版计算器

2.1代码实现

先把日志拷贝过来

2.1.1必须先要有网络功能

先把 TcpServer.hpp编写号

cpp 复制代码
#pragma once
#include <cstdint>

#include "Socket.hpp"
#include "./logs/ljwlog.h"

class TcpServer
{
public:
    TcpServer()
    {}  
    bool InitServer()
    {}
    void Start()
    {}

    ~TcpServer()
    {}
private:
    uint16_t port;

};

2.1.2 把套接字接口封装一下方便使用

cpp 复制代码
#pragma once

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>

using namespace std;

class Sock
{
public:
    Sock()
    {}
    ~Sock()
    {}
public:
    void Socket()//创建套接字的接口
    {}
    void bind()//绑定的接口
    {}
    void Listen()//监听状态的接口
    {}
    int Accept()//获取连接的接口
    {}
    int Connect()//方便两个客户端和服务器都能使用这个Sock的这个公共方法
    {}

private:
    int sockfd_;
};

2.1.3 TcpServer.hpp接口的补充

cpp 复制代码
#pragma once
#include <cstdint>

#include "Socket.hpp"
#include "./logs/ljwlog.h"

class TcpServer
{
public:
    TcpServer()
    {}  
    bool InitServer()
    {
        //先创建套接字,再绑定,设置监听状态
        listensock_.Socket();
        listensock_.bind();
        listensock_.Listen();
    }
    void Start()
    {
        while(true)
        {   
            int sockfd = listensock_.Accept();
        }
    }

    ~TcpServer()
    {}
private:
    uint16_t port_;
    Sock listensock_; //叫listen套接字,Listen完后有真正的网络文件描述符
};

2.1.4 Sock.hpp接口的补充

cpp 复制代码
#pragma once

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <cstring>
#include "./logs/ljwlog.h"

using namespace std;

enum{
    SocketErr = 2,
    BindErr,
    ListenErr
};
const int backlog = 10;
class Sock
{
public:
    Sock()
    {}
    ~Sock()
    {}
public:
    void Socket()//创建套接字的接口
    {
        sockfd_ = socket(AF_INET, SOCK_STREAM, 0);//流式套接字  第二个参数是协议类型
        if(sockfd_ < 0)
        {
            FATAL("Socket errno,error:%d,errstring:%s", errno, strerror(errno));
            exit(SocketErr);
        }
    }
    void Bind(uint16_t port)//绑定的接口
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);//主机转网络
        local.sin_addr.s_addr = INADDR_ANY;//ip默认0.0.0.0
        if(bind(sockfd_, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            FATAL("Bind errno,error:%d,errstring:%s", errno, strerror(errno));
            exit(BindErr);            
        }
    }
    void Listen()//监听状态的接口
    {
        if(listen(sockfd_, backlog) < 0)
        {
            FATAL("Listen errno,error:%d,errstring:%s", errno, strerror(errno));
            exit(ListenErr);   
        }
    }
    //              知道谁链接的我
    int Accept(string *clientip, uint16_t *clientport)//获取连接的接口
    {
        struct sockaddr_in peer;//远端的意思
        socklen_t len = sizeof(peer);
        int newfd = accept(sockfd_, (struct sockaddr*)&peer, &len);
        if(newfd < 0)
        {
            WARN("accept error, %s: %d", strerror(errno), errno);
            return -1;
        }
        //网络转主机
        //拿出客户端的ip和端口号
        char ipstr[64];
        inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr));//网络转主机
        *clientip = ipstr;//网络转主机
        *clientport = ntohs(peer.sin_port);//网络转主机
        return newfd;
    }
    void Close()
    {
        close(sockfd_);
    }
    int Connect()//方便两个客户端和服务器都能使用这个Sock的这个公共方法
    {
        return 0;
    }

private:
    int sockfd_;
};

2.1.4 TcpServer.hpp提供服务

2.1.5定制协议(Protocol)(约定好)

双方约定好,把请求放在Request(请求),结果放在Response(响应)

约定好了,最好就不要动他了

cpp 复制代码
#pragma once

#include<iostream>

class Request
{
public:

public: 
    int x;
    int y;
    char op;// + - * / %
};

class Response
{
public:

public:
    int result;
    int code;// 0,可信,否则!0具体是几,表明对应的错误原因
};

2.1.6 (手写)序列化和反序列化

serialization 序列化(把struct 转换成 字符串)

deserialization 反序列化

2.1.6.1序列化
2.1.6.2反序列化
2.1.6.3添加报头(要往网络里发 先添加报头)

这种

2.1.6.4 提出有效载荷
2.1.6.5代码
cpp 复制代码
#pragma once

#include<iostream>
#include<string>
using namespace std;

const string blank_space_sep = " ";//空格分隔符
const string protocol_sep = "\n";//报文分隔符


//序列化和反序列化解决的是对应的是一个报文内部的问题
//还要解决一下报文与报文之间的问题

//封装报头
string Encode(string &content)
{
    //封装报头  这里规定的协议就是长度+有效载荷
    string package = to_string(content.size());
    package += protocol_sep;
    package += content;
    package += protocol_sep;

    return package;
}

//"len"\n"x op y"\n
string Decode(string &package)//将"x op y"提取出来
{
    return "";
}

class Request
{
public: 
    Request(int data1, int data2, char oper): x(data1), y(data2), op(oper)
    {}

public:
    bool serialization(string *out)
    {
        //构建报文的有效载荷
        //序列化的就是把结构化的(struct)转成string,
        // "x op y"
        //数字之间是不可能出现分隔符的
        string s = to_string(x);
        s += blank_space_sep;
        s += op;
        s += blank_space_sep;
        s += to_string(y);

        *out = s;
        return true;
    }
    //"x op y"  进行分割
    bool deserialization(const string &in)
    {
        size_t left = in.find(blank_space_sep);
        if(left == string::npos) return false;//没找到
        string part_x = in.substr(0, left);

        size_t right = in.rfind(blank_space_sep);//逆着找
        if(right == string::npos) return false;//没找到
        string part_y = in.substr(right + 1);

        if(left + 2 != right) return false;
        op = in[right -1];

        x = stoi(part_x);
        y = stoi(part_y);
        return true;
    }
public: 
    int x;
    int y;
    char op;// + - * / %
};

class Response
{
public:
    Response(int res, int c):result(res), code(c)
    {}
public:
                //序列化是结构体转字符串的
    bool serialization(string *out)
    {
        // 序列化成这样"result code"
        //构建报文的有效载荷
        string s = to_string(result);
        s += blank_space_sep;
        s += to_string(code);

        *out = s;
        return true;
    }
    //反序列化 一定是你要传过来一个字符串
    bool deserialization(const string &in) //"result code" 协议定制好了就必须是这个样子
    {
        size_t pos = in.find(blank_space_sep);
        if(pos == string::npos) return false;//没找到
        string part_left = in.substr(0, pos);
        string part_right = in.substr(pos + 1);
        result = stoi(part_left);
        code = stoi(part_right);
        return true;
    } 
public:
    int result;
    int code;// 0,可信,否则!0具体是几,表明对应的错误原因
};
2.1.6.6 测试一下序列化和添加报头
cpp 复制代码
#include<iostream>
#include "TcpServer.hpp"
#include "Protocol.hpp"

using namespace std;

int main()
{
    Request req(123, 456, '+');
    string s;
    req.serialization(&s);//序列化
    cout << s << endl;

    s = Encode(s);//给s报文添加报头
    cout << s ;//这是网络里发的样子

    return 0;
}
2.1.6.7 测试一下拿到报文的内容
cpp 复制代码
#include<iostream>
#include "TcpServer.hpp"
#include "Protocol.hpp"

using namespace std;

int main()
{
    Request req(123, 456, '+');
    string s;
    req.serialization(&s);//序列化
    //cout << s << endl;

    s = Encode(s);//给s报文添加报头
    cout << s ;//这是网络里发的样子

    //拿到报文的内容
    string content;
    bool r = Decode(s, &content);
    cout<< content <<endl;

    return 0;
}
2.1.6.8 测试对收到报文进行反序列化 打散成对象
2.1.6.9 测试Response的要往网络里发 先添加报头
2.1.7.0 测试解码
2.1.7.1 测试Response反序列化
2.1.7.2 创建ServerCal.hpp处理整个报文

如何处理整个报文

把协议和网络功能组合在一起 然后再把这个头文件"ServerCal.hpp"交给ServerCal.cc

cpp 复制代码
#pragma once
#include<string>
#include<string>
#include "Protocol.hpp"
using namespace std;

//如何处理整个报文
//把协议和网络功能组合在一起 然后再把这个头文件"ServerCal.hpp"交给ServerCal.cc

class ServerCal
{
public:
    ServerCal()
    {}
    Response CalculatorHelper(const Request &req)
    {
        Response resp(0, 0);
        switch(req.op)
        {
            case '+':
                resp.result = req.x + req.y;
            case '-':
                resp.result = req.x - req.y;
            case '*':
                resp.result = req.x * req.y;           
            case '/':
                resp.result = req.x / req.y;
        }
        return resp;
    }

    //提供一个网络计算服务  把先把报文给我
    string Calculator(string &package)//肯定是收到一个序列化的请求报文
    {
        //收到一个请求报文 添加报头
        string content;
        bool r = Decode(package, &content);
        //报文不完整就直接返回
        if(!r) return;

        //完整的报文
        //请求的对象
        Request req;
        //反序列化
        r = req.deserialization(content);
        if(!r) return;

        //结果
        Response resp = CalculatorHelper(req);
        //返回结果
        //序列化
        string s;
        resp.serialization(&s);
        //序列化后,想把它当作网络报文发出去
        //添加报头
        s = Encode(s);

        return s;
    }

    ~ServerCal()
    {}
};
2.1.7.3 完善一下TcpServer.hpp
2.1.7.4 测试
2.1.7.5 完善一下ClientCal.cc

tcp客户端要绑定,但不用显示的绑定

客户端端口号是随机的,但是唯一的

udp端口那里随机绑定是首次发送数据的时候,那么你的端口号就随之确定了

tcp这里是面向连接的,客户端连接成功了,我才想让你进行通信

没有bind 也就没有listen了

向服务器发起连接的接口 connect

客户端发起connect的时候,系统进行自动随机bind

cpp 复制代码
#include <iostream>
#include <ctime>
#include <unistd.h>
#include <assert.h>
#include "Socket.hpp"
#include "Protocol.hpp"

using namespace std;

void Usage(const string &proc)
{
    cout << "\nUsage" << proc << "  serverip   serverport\n\n"
         << endl;
}

//./clientcal serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(0);
    }
    string serverip = argv[1];
    uint16_t serverport = stoi(argv[2]);

    Sock sockfd;
    sockfd.Socket();
    bool r = sockfd.Connect(serverip, serverport);
    if (!r)
        return 1;

    srand(time(nullptr));
    const string s = "+-*/^!~";
    int cnt = 10;

    string inbuffer_stream;
    while (cnt--)
    {
        cout<< "第" <<cnt <<"次测试..." <<endl;
        int x = rand() % 100 + 1;
        usleep(1234);
        int y = rand() % 100 + 1;
        usleep(4321);
        char op = s[rand() % s.size()];
        Request req(x, y, op);

        req.DebugPrint();

        // 根据协议把请求发给服务器,Request是一个结构化的数据,发不了
        // 先序列化得到对应的字符串
        string package;
        req.serialization(&package);

        // 还是不能往网络里发送 添加报头还要
        //"len"\n"x op y"\n
        package = Encode(package);
        cout << "这是最新的发出去的请求:\n"
             << package;
        // 把请求往服务器端写
        write(sockfd.Fd(), package.c_str(), package.size());
        cout<<flush;

        // 接收服务器发回来的信息
        char buffer[128];
        //我们也没法保证我们能够读到一个完整的报文
        ssize_t n = read(sockfd.Fd(), buffer, sizeof(buffer));
        if (n > 0)
        {
            buffer[n] = 0;
            inbuffer_stream += buffer;// "len"\n"result code"\n
            string content;
            //解码
            bool r = Decode(inbuffer_stream, &content);//"result code"
            assert(r);

            //反序列化
            Response resp;
            r = resp.deserialization(content);
            assert(r);
            resp.DebugPrint();

        }
        sleep(1);
    }

    sockfd.Close();

    return 0;
}
2.1.7.6 客户端一次发多个请求呢

解决方案

2.1.7 (jsoncpp)序列化和反序列化

2.1.7.1 安装json库
bash 复制代码
sudo apt install libjsoncpp-dev

这是头文件(只使用json.h)

2.1.7.2 简单的json的用法
cpp 复制代码
#include<iostream>
#include "jsoncpp/json/json.h"

using namespace std;

int main()
{   
    Json::Value root;
    root["x"] = 100;
    root["y"] = 100;
    root["op"] = '+';
    //描述
    root["dect"] = "this is a + oper";

    //比较快的序列化
    Json::FastWriter w;
    string res = w.write(root);

    cout<< res <<endl;

    return 0;
}

编译时要带-ljsoncpp

2.1.7.2.1 序列化
2.1.7.2.2 反序列化
2.1.7.2.3 json套json
2.1.7.3 Protocol.hpp中的json序列化和反序列化

2.1.7.4 守护进程化

3 重谈OSI七层模型

4 代码总合

4.1 ClientCal.cc

cpp 复制代码
#include <iostream>
#include <ctime>
#include <unistd.h>
#include <assert.h>
#include "Socket.hpp"
#include "Protocol.hpp"

using namespace std;

void Usage(const string &proc)
{
    cout << "\nUsage" << proc << "  serverip   serverport\n\n"
         << endl;
}

//./clientcal serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(0);
    }
    string serverip = argv[1];
    uint16_t serverport = stoi(argv[2]);

    Sock sockfd;
    sockfd.Socket();
    bool r = sockfd.Connect(serverip, serverport);
    if (!r)
        return 1;

    srand(time(nullptr));
    const string s = "+-*/^!~";
    int cnt = 10;

    string inbuffer_stream;
    while (cnt--)
    {
        cout<< "第" <<cnt <<"次测试..." <<endl;
        int x = rand() % 100 + 1;
        usleep(1234);
        int y = rand() % 100 + 1;
        usleep(4321);
        char op = s[rand() % s.size()];
        Request req(x, y, op);

        req.DebugPrint();

        // 根据协议把请求发给服务器,Request是一个结构化的数据,发不了
        // 先序列化得到对应的字符串
        string package;
        req.serialization(&package);

        // 还是不能往网络里发送 添加报头还要
        //"len"\n"x op y"\n
        package = Encode(package);
        cout << "这是最新的发出去的请求:\n"
             << package;
        // 把请求往服务器端写
        write(sockfd.Fd(), package.c_str(), package.size());
        // write(sockfd.Fd(), package.c_str(), package.size());
        // write(sockfd.Fd(), package.c_str(), package.size());
        // write(sockfd.Fd(), package.c_str(), package.size());
        // write(sockfd.Fd(), package.c_str(), package.size());
        // write(sockfd.Fd(), package.c_str(), package.size());
        cout<<flush;

        // 接收服务器发回来的信息
        char buffer[128];
        //我们也没法保证我们能够读到一个完整的报文
        ssize_t n = read(sockfd.Fd(), buffer, sizeof(buffer));
        if (n > 0)
        {
            buffer[n] = 0;
            inbuffer_stream += buffer;// "len"\n"result code"\n
            cout<< inbuffer_stream <<endl;
            string content;
            //解码
            bool r = Decode(inbuffer_stream, &content);//"result code"
            assert(r);

            //反序列化
            Response resp;
            r = resp.deserialization(content);
            assert(r);
            resp.DebugPrint();

        }
        sleep(1);
    }

    sockfd.Close();

    return 0;
}

4.2 makefile

cpp 复制代码
.PHONY:all
all:servercal clientcal
servercal:ServerCal.cc
	g++ -g -o $@ $^ -lpthread -std=c++11 -ljsoncpp
clientcal:ClientCal.cc
	g++ -o $@ $^ -lpthread -std=c++11 -ljsoncpp
.PHONT:clean
clean:
	rm -f clientcal servercal

4.3 Protocol.hpp

cpp 复制代码
#pragma once

#include<iostream>
#include<string>
#include "jsoncpp/json/json.h"
using namespace std;

const string blank_space_sep = " ";//空格分隔符
const string protocol_sep = "\n";//报文分隔符


//序列化和反序列化解决的是对应的是一个报文内部的问题
//还要解决一下报文与报文之间的问题

//添加报头  "len"\n"x op y"\n
string Encode(string &content)
{
    //封装报头  这里规定的协议就是长度+有效载荷
    string package = to_string(content.size());
    package += protocol_sep;
    package += content;
    package += protocol_sep;

    return package;
}

//将"x op y"提取出来 有效载荷
//"len"\n"x op y"\n   package整个报文    *content把结果带出去
bool Decode(string &package, string *content)
{
    size_t pos = package.find(protocol_sep);
    if(pos == string::npos) return false;
    string len_str = package.substr(0, pos);
    size_t len = stoi(len_str); //拿到了len长度

    //判断一下package的长度够不够
    //package = len_str + content_str + 2
    size_t total_len = len_str.size() + len + 2;
    if(package.size() < total_len) return false;
    //大于没事,因为肯定有完整的报文

    //*content把结果带出去  "x op y"
    *content = package.substr(pos + 1, len);

    // earse 移除报文  
    package.erase(0, total_len);
    return true;
}

class Request
{
public: 
    Request(int data1, int data2, char oper): x(data1), y(data2), op(oper)
    {}
    Request()
    {}
public:
    bool serialization(string *out)
    {
        // //构建报文的有效载荷
        // //序列化的就是把结构化的(struct)转成string,
        // // "x op y"
        // //数字之间是不可能出现分隔符的
        // string s = to_string(x);
        // s += blank_space_sep;
        // s += op;
        // s += blank_space_sep;
        // s += to_string(y);

        // *out = s;
        // return true;

        //Json 序列化
        Json::Value root;
        root["x"] = x;
        root["y"] = y;
        root["op"] = op;
        Json::FastWriter w;
        *out = w.write(root);
        return true;
    }

    //"x op y"  进行分割
    bool deserialization(const string &in)
    {
        // size_t left = in.find(blank_space_sep);
        // if(left == string::npos) return false;//没找到
        // string part_x = in.substr(0, left);

        // size_t right = in.rfind(blank_space_sep);//逆着找
        // if(right == string::npos) return false;//没找到
        // string part_y = in.substr(right + 1);

        // if(left + 2 != right) return false;
        // op = in[right -1];

        // x = stoi(part_x);
        // y = stoi(part_y);
        // return true;

        //Json 反序列化
        Json::Value root;
        Json::Reader r;
        r.parse(in, root);
        //反序列化到root
        //提取出来
        x = root["x"].asInt();
        y = root["y"].asInt();
        op = root["op"].asInt();
        return true;  
    }
    void DebugPrint()
    {
        cout<< "新请求构建完成" << x << op << y << "=?" <<endl<<endl;
    }

public: 
    int x;
    int y;
    char op;// + - * / %
};

class Response
{
public:
    Response(int res, int c):result(res), code(c)
    {}
    Response()
    {}
public:
                //序列化是结构体转字符串的
    bool serialization(string *out)
    {
        // // 序列化成这样"result code"
        // //构建报文的有效载荷
        // string s = to_string(result);
        // s += blank_space_sep;
        // s += to_string(code);

        // *out = s;
        // return true;

        //Json 序列化
        Json::Value root;
        root["result"] = result;
        root["code"] = code;
        Json::FastWriter w;
        *out = w.write(root);
        return true;
    }

    //反序列化 一定是你要传过来一个字符串
    bool deserialization(const string &in) //"result code" 协议定制好了就必须是这个样子
    {
        // size_t pos = in.find(blank_space_sep);
        // if(pos == string::npos) return false;//没找到
        // string part_left = in.substr(0, pos);
        // string part_right = in.substr(pos + 1);
        // result = stoi(part_left);
        // code = stoi(part_right); 
        // return true;

        //Json 反序列化
        Json::Value root;
        Json::Reader r;
        r.parse(in, root);
        //反序列化到root
        //提取出来
        result = root["result"].asInt();
        code = root["code"].asInt();
        return true;
    } 

    void DebugPrint()
    {
        cout<< "响应结果完成" << "result:"<< result <<"code:" <<code <<endl <<endl;
    }
public:
    int result;
    int code;// 0,可信,否则!0具体是几,表明对应的错误原因
};

4.4 ServerCal.cc

cpp 复制代码
#include<iostream>
#include<unistd.h>
#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "ServerCal.hpp"

using namespace std;

void Usage(const string& proc)
{
    cout<<"\nUsage" << proc << "port\n\n" <<endl;
}

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(0);   
    }
    uint16_t port = stoi(argv[1]);

    ServerCal cal;
    TcpServer *tsvp = new TcpServer(port, bind(&ServerCal::Calculator, &cal, placeholders::_1));
    tsvp->InitServer();
    daemon(0, 0);
    tsvp->Start();




    // Response resp(1000, 0);
    // //发回去
    // string content;
    // resp.serialization(&content);
    // //cout << content << endl;
    // //要往网络里发 先添加报头
    // string package = Encode(content);
    // cout << package << endl;

    // //解码 放进s
    // string s;
    // bool r = Decode(package, &s);
    // cout<< s <<endl;

    // //反序列化   打散成对象
    // Response temp;
    // temp.deserialization(s);
    // cout<<"\n\n";
    // cout<< temp.result <<endl;
    // cout<< temp.code << endl;


    // Request req(123, 456, '+');
    // string s;
    // req.serialization(&s);//序列化
    // //cout << s << endl;

    // s = Encode(s);//给s报文添加报头
    // cout << s ;//这是网络里发的样子

    // //拿到报文的内容
    // string content;
    // bool r = Decode(s, &content);
    // cout<< content <<endl;

    // //对收到报文进行反序列化  打散成对象
    // Request temp;
    // temp.deserialization(content);
    // cout<< temp.x <<endl << temp.op <<endl << temp.y <<endl;

    return 0;
}

4.5 ServerCal.hpp

cpp 复制代码
#pragma once
#include<string>
#include<string>
#include "Protocol.hpp"
using namespace std;

//如何处理整个报文
//把协议和网络功能组合在一起 然后再把这个头文件"ServerCal.hpp"交给ServerCal.cc

class ServerCal
{
public:
    ServerCal()
    {}
    Response CalculatorHelper(const Request &req)
    {
        Response resp(0, 0);
        switch(req.op)
        {
            case '+':
                resp.result = req.x + req.y;
                break;
            case '-':
                resp.result = req.x - req.y;
                break;
            case '*':
                resp.result = req.x * req.y;           
                break;
            case '/':
                if(req.y == 0) resp.code = 1;
                else 
                {
                    resp.result = req.x / req.y;
                }
                break;
            default:
                resp.code = 2;
                break;
        }
        return resp;
    }

    //提供一个网络计算服务  把先把报文给我
    //           package: "len"\n"10 + 20"\n
    string Calculator(string &package)//肯定是收到一个序列化的请求报文
    {
        //收到一个请求报文 添加报头
        string content;
        bool r = Decode(package, &content);
        //报文不完整就直接返回
        if(!r) return "";

        //完整的报文
        //请求的对象
        Request req;
        //反序列化
        r = req.deserialization(content);
        if(!r) return "";

        //结果
        Response resp = CalculatorHelper(req);
        //返回结果
        //序列化
        string s;
        resp.serialization(&s);
        //序列化后,想把它当作网络报文发出去
        //添加报头
        s = Encode(s);

        return s;
    }

    ~ServerCal()
    {}
};

4.6 Socket.hpp

cpp 复制代码
#pragma once

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <cstring>
#include "./logs/ljwlog.h"

using namespace std;

enum
{
    SocketErr = 2,
    BindErr,
    ListenErr
};
const int backlog = 10;
class Sock
{
public:
    Sock()
    {
    }
    ~Sock()
    {
    }

public:
    void Socket() // 创建套接字的接口
    {
        sockfd_ = socket(AF_INET, SOCK_STREAM, 0); // 流式套接字  第二个参数是协议类型
        if (sockfd_ < 0)
        {
            FATAL("Socket errno,error:%d,errstring:%s", errno, strerror(errno));
            exit(SocketErr);
        }
    }
    void Bind(uint16_t port) // 绑定的接口
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);       // 主机转网络
        local.sin_addr.s_addr = INADDR_ANY; // ip默认0.0.0.0
        if (bind(sockfd_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            FATAL("Bind errno,error:%d,errstring:%s", errno, strerror(errno));
            exit(BindErr);
        }
    }
    void Listen() // 监听状态的接口
    {
        if (listen(sockfd_, backlog) < 0)
        {
            FATAL("Listen errno,error:%d,errstring:%s", errno, strerror(errno));
            exit(ListenErr);
        }
    }
    //              知道谁链接的我
    int Accept(string *clientip, uint16_t *clientport) // 获取连接的接口
    {
        struct sockaddr_in peer; // 远端的意思
        socklen_t len = sizeof(peer);
        int newfd = accept(sockfd_, (struct sockaddr *)&peer, &len);
        if (newfd < 0)
        {
            WARN("accept error, %s: %d", strerror(errno), errno);
            return -1;
        }
        // 网络转主机
        // 拿出客户端的ip和端口号
        char ipstr[64];
        inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr)); // 网络转主机
        *clientip = ipstr;                                        // 网络转主机
        *clientport = ntohs(peer.sin_port);                       // 网络转主机
        return newfd;
    }
    void Close()
    {
        close(sockfd_);
    }
    int Connect(const string &ip, const uint16_t &port) // 方便两个客户端和服务器都能使用这个Sock的这个公共方法
    {
        struct sockaddr_in peer;
        memset(&peer, 0, sizeof(peer));
        peer.sin_family = AF_INET;
        peer.sin_port = htons(port);
        inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));
        int n = connect(sockfd_, (struct sockaddr *)&peer, sizeof(peer));
        if (n == -1)
        {
            cerr << "connect to" << ip << "::" << port <<"error"<< endl;
            return false;
        }

        return true;
    }
    int Fd()
    {
        return sockfd_;
    }

private:
    int sockfd_;
};

4.7 TcpServer.hpp

cpp 复制代码
#pragma once
#include <cstdint>
#include <signal.h>
#include <functional>
#include "Socket.hpp"
#include "./logs/ljwlog.h"

//                     返回值    参数
using fun_t = function< string(string &package)>;

class TcpServer
{
public:
    TcpServer(uint16_t port, fun_t callback):port_(port), callback_(callback)
    {}  
    bool InitServer( )
    {
        //先创建套接字,再绑定,设置监听状态
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();
        INFO("init server");
        return true;
    }
    void Start()
    {
        signal(SIGCHLD, SIG_IGN);//忽略就不用等待了
        signal(SIGPIPE, SIG_IGN);//忽略就不用等待了
        while(true)
        {   
            string clientip;
            uint16_t clientport;
            int sockfd = listensock_.Accept(&clientip, &clientport);
            if(sockfd < 0) continue;
            INFO("Accept server   sockfd:%d  clientip:%s  clientport:%d", sockfd, clientip.c_str(), clientport);
            //提供服务
            if(fork() == 0)
            {
                //子进程
                listensock_.Close();
                
                //请求的信息流
                string inbuffer_stream;

                //数据计算
                while(true)
                {
                    //从网络当中读数据到buffer当中
                    char buffer[1024];
                    ssize_t n = read(sockfd, buffer, sizeof(buffer));
                    if(n > 0)
                    {
                        buffer[n] = 0;
                        inbuffer_stream += buffer;

                        DEBUG("inbuffer_stream: %s", inbuffer_stream.c_str());
                        while(true)
                        {
                            string info = callback_(inbuffer_stream);
                            //如果没有读到完整的报文,就会返回一个空串infp
                            if(info.empty()) break;

                            write(sockfd, info.c_str(), info.size());
                        }

                    }
                    else if(n == 0) break;
                    else break;
                }

                exit(0);
            }
            close(sockfd);//不用等了直接关就好了
        }
    }

    ~TcpServer()
    {}
private:
    uint16_t port_;
    Sock listensock_; //叫listen套接字,Listen完后有真正的网络文件描述符

    fun_t callback_;
};
相关推荐
十年之少2 分钟前
内存检测工具——Qt Creator
开发语言·qt
嵌入式-老费21 分钟前
Linux上位机开发实战(x86和arm自由切换)
linux·运维·arm开发
猪猪侠|ZZXia25 分钟前
# linux有哪些显示服务器协议、显示服务器、显示管理器、窗口管理器?有哪些用于开发图形用户界面的工具包?有哪些桌面环境?
linux·服务器
网络安全(king)28 分钟前
基于java社交网络安全的知识图谱的构建与实现
开发语言·网络·深度学习·安全·web安全·php
和道一文字yyds36 分钟前
20天 - TCP 和 UDP 有什么区别?说说 TCP 的三次握手?TCP 是用来解决什么问题?
网络协议·tcp/ip·udp
艾希逐月37 分钟前
【动手实验】TCP 连接的建立与关闭抓包分析
网络·tcp/ip
niu_sama37 分钟前
[杂学笔记] TCP和UDP的区别,对http接口解释 , Cookie和Session的区别 ,http和https的区别 , 智能指针 ,断点续传
笔记·tcp/ip·http
人间凡尔赛39 分钟前
VSCode-Server 在 Linux 容器中的手动安装指南
linux·运维·服务器·docker
Chenyu_31041 分钟前
05.基于 TCP 的远程计算器:从协议设计到高并发实现
linux·网络·c++·vscode·网络协议·tcp/ip·算法
论迹1 小时前
【二分算法】-- 三种二分模板总结
java·开发语言·算法·leetcode