17.TCP编程(二)和序列化

一.概念回顾

建议先学上篇博客,再向下学习,上篇博客的链接如下:

https://blog.csdn.net/weixin_60668256/article/details/154752573?fromshare=blogdetail&sharetype=blogdetail&sharerId=154752573&sharerefer=PC&sharesource=weixin_60668256&sharefrom=from_link

二.远程命令分析功能

我们还是在原来的代码的基础上,增加一个CommandExec的类(我们可以把我们原来的shell直接拿来)



#pragma once
 
 
#include <iostream>
#include <string>
 
class Command
{
public:
    //给你一个命令字符串"ls -l",让你执行,执行完,把结果进行返回
    std::string Execute(std::string cmdstr)
    {
        //1. pipe
        //2. fork + dup2(pipe[1],1) + exec*,把执行结果交给父进程(pipe[0])
        //3. return
        
 
        return std::string();
    }
};

但是有一个接口完成了上面的基本功能

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化



//给你一个命令字符串"ls -l",让你执行,执行完,把结果进行返回
    std::string Execute(std::string cmdstr)
    {
        //1. pipe
        //2. fork + dup2(pipe[1],1) + exec*,把执行结果交给父进程(pipe[0])
        //3. return
 
        FILE* fp = ::popen(cmdstr.c_str(),"r");
        if(nullptr == fp)
        {
            return std::string("Failed");
        }
 
        char buffer[line_size];
        std::string result;
        while(true)
        {
            char* ret = ::fgets(buffer,sizeof(buffer),fp);
            if(!ret)
            {
                break;
            }
            result += ret;
        }
        pclose(fp);
        return result.empty() ? std::string("Done") : result;
    }

三.将可执行命令设置进入白名单(防止危险命令)



#pragma once
 
 
#include <iostream>
#include <string>
#include <cstdio>
#include <set>
 
const int line_size = 1024;
 
class Command
{
public:
    Command()
    {
        _white_list.insert("ls");
        _white_list.insert("pwd");
        _white_list.insert("ls -l");
        _white_list.insert("ll");
        _white_list.insert("touch");
        _white_list.insert("who");
        _white_list.insert("whoami");
    }
 
    bool SafeCheck(const std::string& cmdstr)
    {
        if(_white_list.count(cmdstr))
        {
            return true;
        }
        return false;
    }
    //给你一个命令字符串"ls -l",让你执行,执行完,把结果进行返回
    std::string Execute(std::string cmdstr)
    {
        //1. pipe
        //2. fork + dup2(pipe[1],1) + exec*,把执行结果交给父进程(pipe[0])
        //3. return
        if(!SafeCheck(cmdstr))
        {
            return std::string(cmdstr + "不支持");
        }
 
        FILE* fp = ::popen(cmdstr.c_str(),"r");
        if(nullptr == fp)
        {
            return std::string("Failed");
        }
 
        char buffer[line_size];
        std::string result;
        while(true)
        {
            char* ret = ::fgets(buffer,sizeof(buffer),fp);
            if(!ret)
            {
                break;
            }
            result += ret;
        }
        pclose(fp);
        return result.empty() ? std::string("Done") : result;
    }
private:
    std::set<std::string> _white_list;
};
 
 

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

这里我们采取的是严格匹配,所以可执行的命令较少(可以用shell,但是安全一定要做好)

17.TCP编程(二)和序列化

这里我们就基本完成了TCP套接字的初次使用

四.应用层协议

1.协议讲解

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

但是这样是不行的,对于我们的(两个机器位数,平台等)不同,这样数据会出现不匹配的问题

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

2.重新理解read、write、recv、send和tcp为什么支持全双工

17.TCP编程(二)和序列化

这里操作系统会对于一个sockfd创建对应的两个缓冲区,用于发送和接收

两对接收和发送缓冲区,所以对应的就是全双工的了

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

3.面向字节流

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

TCP的发送方式就是基于拥塞控制,流量控制等操作,将部分报文进行发送,导致我们读取数据出现错误(所以在OS层面是,不能保证读取正确的)

UDP的发送方式就是用户数据报,发送的信息一定是完整的(和快递类似,只能收到一个完整的快递,不能只收到半个快递)

TCP无法确定报文完整性的,所以我们在用户层可以保证自己报文的完整性

五.网络版本计算器的实现

1.协议的定制

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

但是不能将结构体直接进行发送(可能会出错(兼容性问题))

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

我们的方案一,要是读取多了怎么办,所以我们要进行协议的定制

17.TCP编程(二)和序列化

所以  head_lengt
  就相当于是我们自己定制的报头

17.TCP编程(二)和序列化

这里我们直接使用json方案进行处理

2.Jsoncpp的处理

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

a.序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

b.反序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

c.总结

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

要使用的时候,我们直接查就行了

3.Protocol.hpp的实现

a.结构定义



#pragma once
 
#include <iostream>
#include <string>
 
 
 
// _x _oper _y
class Request
{
private:
    int _x;
    int _y;
    char _oper;
};
 
 
 
class Response
{
private:
    int _result; //结果
    int _code;   //出错码,0,1,2,3,4
};

b.构造的实现



#pragma once
 
#include <iostream>
#include <string>
 
 
 
// _x _oper _y
class Request
{
public:
    Request(int x,int y,char oper):_x(x),_y(y),_oper(oper)
    {
 
    }
    ~Request()
    {
 
    }
private:
    int _x;
    int _y;
    char _oper;
};
 
class Response
{
public:
    Response(int result,int code):_result(result),_code(code)
    {
 
    }
    ~Response()
    {
 
    }
private:
    int _result; //结果
    int _code;   //出错码,0,1,2,3,4
};

c.Request的序列化和反序列化



class Request
{
public:
    Request(int x,int y,char oper):_x(x),_y(y),_oper(oper)
    {
 
    }
    ~Request()
    {
 
    }
    bool Serialize(std::string& out_string)
    {
        Json::Value root;
        root["x"] = _x;
        root["y"] = _y;
        root["oper"] = _oper;
 
        Json::StreamWriterBuilder wb;
        std::unique_ptr<Json::StreamWriter> w(wb.newStreamWriter());
        std::stringstream ss;
 
        w->write(root,&ss);
        out_string = ss.str();
        return true;
    }
    bool Deserialize(std::string& in_string)
    {
        Json::Value root;
        Json::Reader reader;
        bool parsingSuccessful = reader.parse(in_string,root);
        if(!parsingSuccessful)
        {
            std::cout << "Failed to parse JSON: " << reader.getFormatedErrorMessages() << std::endl;
            return false;
        }
        _x = root["x"].asInt();
        _y = root["y"].asInt();
        _oper = root["oper"].asInt();
 
        return true;
    }
private:
    int _x;
    int _y;
    char _oper;
};

17.TCP编程(二)和序列化

序列化的本质,就是将我们对应的  内容  转化成为  字符串

反序列化的本质,就是将我们对应的  字符串  转化成为  内容

d.测试代码



#include "Protocol.hpp"
 
int main()
{
    Request req(10,20,'+');
    std::string s;
    req.Serialize(s);
    std::cout << s << std::endl; 
    return 0;
}

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化



#include "Protocol.hpp"
 
int main()
{
    //序列化
    Request req(10,20,'+');
    std::string s;
    req.Serialize(s);
    std::cout << s << std::endl;
 
    //反序列化
    req.Deserialize(s);
    req.Print();
    return 0;
}

17.TCP编程(二)和序列化

e.Response的序列化和反序列化



class Response
{
public:
    Response(int result,int code):_result(result),_code(code)
    {
 
    }
    ~Response()
    {
 
    }
    bool Serialize(std::string& out_string)
    {
        Json::Value root;
        root["result"] = _result;
        root["code"] = _code;
 
        Json::StreamWriterBuilder wb;
        std::unique_ptr<Json::StreamWriter> w(wb.newStreamWriter());
        std::stringstream ss;
 
        w->write(root,&ss);
        out_string = ss.str();
        return true;
    }
    bool Deserialize(std::string& in_string)
    {
        Json::Value root;
        Json::Reader reader;
        bool parsingSuccessful = reader.parse(in_string,root);
        if(!parsingSuccessful)
        {
            std::cout << "Failed to parse JSON: " << reader.getFormatedErrorMessages() << std::endl;
            return false;
        }
        _result = root["result"].asInt();
        _code = root["code"].asInt();
 
        return true;
    }
private:
    int _result; //结果
    int _code;   //出错码,0,1,2,3,4
};

17.TCP编程(二)和序列化

f.其他接口的实现



#pragma once
 
#include <iostream>
#include <string>
#include <sstream>
#include <memory>
#include <jsoncpp/json/json.h>
 
 
// _x _oper _y
class Request
{
public:
    Request(int x,int y,char oper):_x(x),_y(y),_oper(oper)
    {
 
    }
    ~Request()
    {
 
    }
    bool Serialize(std::string& out_string)
    {
        Json::Value root;
        root["x"] = _x;
        root["y"] = _y;
        root["oper"] = _oper;
 
        Json::StreamWriterBuilder wb;
        std::unique_ptr<Json::StreamWriter> w(wb.newStreamWriter());
        std::stringstream ss;
 
        w->write(root,&ss);
        out_string = ss.str();
        return true;
    }
    bool Deserialize(std::string& in_string)
    {
        Json::Value root;
        Json::Reader reader;
        bool parsingSuccessful = reader.parse(in_string,root);
        if(!parsingSuccessful)
        {
            std::cout << "Failed to parse JSON: " << reader.getFormatedErrorMessages() << std::endl;
            return false;
        }
        _x = root["x"].asInt();
        _y = root["y"].asInt();
        _oper = root["oper"].asInt();
 
        return true;
    }
    void Print()
    {
        std::cout << _x << std::endl;
        std::cout << _oper << std::endl;
        std::cout << _y << std::endl;
    }
    int X()
    {
        return _x;
    }
    int Y()
    {
        return _y;
    }
    char Oper()
    {
        return _oper;
    }
private:
    int _x;
    int _y;
    char _oper;
};
 
class Response
{
public:
    Response(int result,int code):_result(result),_code(code)
    {
 
    }
    ~Response()
    {
 
    }
    bool Serialize(std::string& out_string)
    {
        Json::Value root;
        root["result"] = _result;
        root["code"] = _code;
 
        Json::StreamWriterBuilder wb;
        std::unique_ptr<Json::StreamWriter> w(wb.newStreamWriter());
        std::stringstream ss;
 
        w->write(root,&ss);
        out_string = ss.str();
        return true;
    }
    bool Deserialize(std::string& in_string)
    {
        Json::Value root;
        Json::Reader reader;
        bool parsingSuccessful = reader.parse(in_string,root);
        if(!parsingSuccessful)
        {
            std::cout << "Failed to parse JSON: " << reader.getFormatedErrorMessages() << std::endl;
            return false;
        }
        _result = root["result"].asInt();
        _code = root["code"].asInt();
 
        return true;
    }
    int Result()
    {
        return _result;
    }
    int Code()
    {
        return _code;
    }
private:
    int _result; //结果
    int _code;   //出错码,0,1,2,3,4
};

g.报头的添加和删除



const std::string Sep = "
";
 
 
//添加报头
//{json} -> len
{json}

bool Encode(std::string& message)
{
    if(message.size() == 0)
    {
        return false;
    }
    std::string package = std::to_string(message.size()) + Sep + message + Sep;
    message = package;
    return true;
}
 
//删除报头
//len
{json}
 -> {json}
bool Decode(std::string &package,std::string* content)
{
    auto pos = package.find(Sep);
    if(pos == std::string::npos)
    {
        return false;
    }
    std::string content_length_str = package.substr(0,pos);
    int content_length = std::stoi(content_length_str);
    int full_length = content_length_str.size() + content_length + 2 * Sep.size();
    
    if(package.size() < full_length)
    {
        return false;
    }
    *content = package.substr(pos + Sep.size(),content_length);
 
    //package erase
    package.erase(0,full_length);
    return true;
}

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

所以我们说read这个接口是不太完整的(没有进行Decode)

4.Server的代码实现



void HandlerRequest(int sockfd)
    {
        char inbuffer[4096];
        std::string package;
        //长任务
        while(true)
        {
            //约定: 用户发过来的是一个完整的命令string
            ssize_t n = recv(sockfd,inbuffer,sizeof(inbuffer)-1,0);
            if(n > 0)
            {
                inbuffer[n] = 0;
                LOG(LogLevel::INFO) << inbuffer;
                package += inbuffer;
 
                std::string cmd_result = _handler(package);
                if(cmd_result.empty())
                {
                    continue;
                }
 
                ::send(sockfd,cmd_result.c_str(),cmd_result.size(),0);
            }
            else if(n == 0)
            {
                // read 如果读取返回值是0,标识client退出
                LOG(LogLevel::INFO) << "client exit: " << sockfd;
                break;
            }
            else
            {
                //读取失败了
                break;
            }
        }
        ::close(sockfd);
    }

17.TCP编程(二)和序列化

如果处理完之后的报文为空(我们上层会做相对的处理,如果读取错误就返回空),那么我们直接重新进行读取数据

5.Calculator的代码实现



#pragma once
 
#include <iostream>
#include "Protocol.hpp"
 
 
class Calculator
{
public:
    Calculator()
    {
 
    }
    Response Execute(const Request& req)
    {
        
    }
    ~Calculator()
    {
 
    }
private:
 
};
 
 
 

6.func()的设计



#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "Calculator.hpp"
#include <memory>
 
 
using namespace LogModule;
 
Calculator gcal;
 
//计算这里要检测是否有完整的报文
//不完整 -> 继续读
//完整   -> 提取  ->  反序列化  -> Request  -> 计算模块,进行处理
std::string Entry(std::string& package)
{
    //1. 完整性判断
    std::string message;
    bool res = Decode(package,&message);
    if(!res || message.empty())
    {
        return std::string();
    }
    //2. 反序列化,message是一个曾经被序列化的数据{json}
    Request req;
    if(!req.Deserialize(message))
    {
        return std::string();
    }
 
    //3.计算
    Response resp = gcal.Execute(req);
 
    //4.序列化
    std::string respstr;
    resp.Serialize(respstr);
 
    //5.添加长度报头字段
    Encode(respstr);
 
    return respstr;
}
 
int main()
{
    ENABLE_CONSOLE_LOG();
 
    //TCP 只负责IO
    std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(Entry);
    tsvr->InitServer();
    tsvr->Start();
    return 0;
}

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

因为我们对应的package被处理了一个请求,我们就要删除该请求

这个package就相当于我们的生产消费模型(生产者从后面追加,消费者从前面读取)

7.func()的多请求处理

上面我们对应的Entry只能一次处理一个请求,那么我们想一次处理多个请求呢?



std::string Entry(std::string& package)
{
    //1. 完整性判断
    std::string message;
    std::string respstr;
    while(Decode(package,&message))
    {
        if(!message.empty())
        {
            break;
        }
        //2. 反序列化,message是一个曾经被序列化的数据{json}
        Request req;
        if(!req.Deserialize(message))
        {
            break;
        }
 
        //3.计算
        Response resp = gcal.Execute(req);
 
        //4.序列化
        std::string res;
        resp.Serialize(res);
 
        //5.添加长度报头字段
        Encode(res);
 
        //6.拼接应答
        respstr += res;
    }
 
    return respstr;
}

17.TCP编程(二)和序列化

8.Execute()的实现



Response Execute(const Request& req)
    {
        //我们拿到就是结构化数据
        Response resp;
        switch(req.Oper())
        {
        case '+':
            resp.SetResult(req.X() + req.Y());
            break;
        case '-':
            resp.SetResult(req.X() - req.Y());
            break;
        case '*':
            resp.SetResult(req.X() * req.Y());
            break;
        case '/':
            {
                if(req.Y() == 0)
                {
                    resp.SetCode(1);//1 就是除0
                }
                else
                {
                    resp.SetResult(req.X() + req.Y());
                }   
            }
            break;
        case '%':
            {
                if(req.Y() == 0)
                {
                    resp.SetCode(2);//2 就是模0
                }
                else
                {
                    resp.SetResult(req.X() % req.Y());
                }   
            }
            break;
        default:
            resp.SetCode(3);//3 就是没有操作
            break;
        }
        return resp;
    }

17.TCP编程(二)和序列化

9.Parse类的封装



#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "Calculator.hpp"
#include <memory>
 
 
using namespace LogModule;
 
 
using cal_fun = std::function<Response(const Request& req)>;
//计算这里要检测是否有完整的报文
//不完整 -> 继续读
//完整   -> 提取  ->  反序列化  -> Request  -> 计算模块,进行处理
 
class Parse
{
public:
    Parse(cal_fun c):_cal(c)
    {
 
    }
    std::string Entry(std::string& package)
    {
        //1. 完整性判断
        std::string message;
        std::string respstr;
        while(Decode(package,&message))
        {
            if(!message.empty())
            {
                break;
            }
            //2. 反序列化,message是一个曾经被序列化的数据{json}
            Request req;
            if(!req.Deserialize(message))
            {
                break;
            }
 
            //3.计算
            Response resp = _cal(req);
 
            //4.序列化
            std::string res;
            resp.Serialize(res);
 
            //5.添加长度报头字段
            Encode(res);
 
            //6.拼接应答
            respstr += res;
        }
 
        return respstr;
    }
private:
    cal_fun _cal;
};
 
int main()
{
    ENABLE_CONSOLE_LOG();
 
    //1.计算模块
    Calculator mycal;
    //2.解析模块
    Parse myparse([&mycal](const Request& req){
        return mycal.Execute(req);
    });
    //3.通信模块
    //TCP 只负责IO
    std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>([&myparse](std::string& package){
        return myparse.Entry(package);
    });
    tsvr->InitServer();
    tsvr->Start();
    return 0;
}

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

这样我们就以后就直接会调用myparse的Entry函数了



"makefile"
 
.PHONY:all
all:server_tcp client_tcp
 
server_tcp:TcpServer.cc
	g++ -o $@ $^ -std=c++17 -lpthread -ljsoncpp
client_tcp:TcpClient.cc
	g++ -o $@ $^ -std=c++17 -lpthread -ljsoncpp
 
 
.PHONY:clean
clean:
	rm -f server_tcp client_tcp

10.客户端的实现



#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 
#include "Protocol.hpp"
 
 
// ./clinet server_ip server_port
int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        std::cout << "Usage:./client server_ip server_port" << std::endl;
        return 1;
    }
    std::string server_ip = argv[1];
    int server_port = std::stoi(argv[2]);
 
    int sockfd = ::socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0)
    {
        std::cout << "create socket error" << std::endl;
        return 2;
    }
 
    //client 不需要显示进行bind, 因为bind只在服务端使用
    //TCP时面向连接的
    struct sockaddr_in server_addr;
    memset(&server_addr,0,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(server_port);
    server_addr.sin_addr.s_addr = inet_addr(server_ip.c_str());
    //connect底层会自动进行bind
    int n = ::connect(sockfd, (struct sockaddr*)&server_addr,sizeof(server_addr));
    if(n < 0)
    {
        std::cout << "connect error" << std::endl;
        return 3;
    }
 
    //echo client
    std::string message;
    while(true)
    {
        int x,y;
        char oper;
        std::cout << "input x: ";
        std::cin >> x;
        std::cout << "input y: ";
        std::cin >> y;
        std::cout << "input oper: ";
        std::cin >> oper;
 
        Request req(x,y,oper);
        
        //1.序列化
        req.Serialize(message);
 
        //2.Encode
        Encode(message);
 
        //3.发送
        n = send(sockfd,message.c_str(),message.size(),0);
        if(n > 0)
        {
            char inbuffer[1024];
            int m = ::recv(sockfd,inbuffer,sizeof(inbuffer),0);
            if(m > 0)
            {
                inbuffer[m] = 0;
                std::string package = inbuffer;
                std::string content;
                //4.读到的应答是完整的
                Decode(package,&content);
 
                //5.反序列化
                Response resp;
                resp.Deserialize(content);
 
                //6.结构化数据
                std::cout << resp.Result() << "[" << resp.Code() << "]" << std::endl;
            }
            else
            {
                break;
            }
        }
        else
        {
            std::cout << "write error" << std::endl;
            break;
        }
    }
 
    ::close(sockfd);
    return 0;
}

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

11.OSI七层模型的重新理解

17.TCP编程(二)和序列化

上三层,现在我们都做了(将这三层压缩成为了一层)

以后,下面两层,我们有对应封装好的库,我们真正编写的是上面的应用层模块

TCP/IP只实现了下四层,上面三层根据业务的需求不同,我们程序员要进行定制

六.守护进程

1.前台进程和后台进程

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

2.守护进程

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

3.守护进程的操作方式

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

我们守护进程打印的结果直接往这个黑洞文件里面打印



#pragma once
 
 
#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
 
 
#define ROOT "/"
#define devnull "/dev/null"
 
 
void Daemon(bool ischdir,bool isclose)
{
    //1.守护进程一般要屏蔽到特定的异常信号
    signal(SIGCHLD,SIG_IGN);
    signal(SIGPIPE,SIG_IGN);
 
    //2.成为非组长
    if(fork() > 0)
    {
        exit(0);
    }
 
    //3.建立新会话
    setsid();
 
    //4.每一个进程都有自己的CWD,是否将当前进程的CWD更改成为 / 根目录
    if(ischdir)
    {
        chdir(ROOT);
    }
 
    //5.已经变成守护进程了,不需要和用户的输入输出,错误进行关联了
    if(isclose)
    {
        ::close(0);
        ::close(1);
        ::close(2);
    }
    else
    {
        int fd = ::open(devnull,O_WRONLY);
        if(fd > 0)
        {
            //各种重定向
            dup2(fd,0);
            dup2(fd,1);
            dup2(fd,2);
            close(fd);
        }
    }
    
}

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

17.TCP编程(二)和序列化

我们现在将云服务器关闭,那个程序还是在跑的,我们将客户端上传到我们对应的应用商店,供别人下载就可以让别人访问了

我们将程序进行静态编译,那么就不需要用户进行下载对应的jsoncpp的库了



.PHONY:all
all:server_tcp client_tcp
 
server_tcp:TcpServer.cc
	g++ -o $@ $^ -std=c++17 -lpthread -ljsoncpp
client_tcp:TcpClient.cc
	g++ -o $@ $^ -std=c++17 -lpthread -ljsoncpp -static
 
 
.PHONY:clean
clean:
	rm -f server_tcp client_tcp

我们后续还可以更改成发送图片的(有兴趣的自己进行尝试)

© 版权声明

相关文章

暂无评论

none
暂无评论...