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

郑州 科技有限公司 网站建设中卫网站制作公司报价

郑州 科技有限公司 网站建设,中卫网站制作公司报价,网站的logo在百度怎么显示不出来,wordpress 主机配置#x1f30e;实现网络版计算器【下】 本次序列化与反序列化所用到的代码#xff0c;Tcp服务自定义序列化反序列化实现网络版计算器。 文章目录#xff1a; 实实现网络版计算器【下】 客户端实现     基于守护进程的改写 #x1f680;客户端实现 在这之前#xff0c…实现网络版计算器【下】 本次序列化与反序列化所用到的代码Tcp服务自定义序列化反序列化实现网络版计算器。 文章目录 实实现网络版计算器【下】 客户端实现     基于守护进程的改写 客户端实现 在这之前我们已经将服务器端的代码部分做好了准备现在万事俱备只欠客户端发起连接而客户端在这里不准备那么多的封装了与之前写的客户端相同我们想要客户端以 ./cal_client ip port 的形式来创建客户端 #include iostream #include string #include memory #include ctime#include Socket.hpp #include Log.hpp #include Protocol.hppvoid Usage(std::string proc) {std::cout Usage:\n\t proc serverip serverport\n std::endl; }using namespace socket_ns; using namespace protocol_ns;// ./tcp_client serverip serverport int main(int argc, char *argv[]) {if (argc ! 3){Usage(argv[0]);exit(1);}std::string serverip argv[1];// 服务器端ipuint16_t serverport std::stoi(argv[2]);// 服务器端portreturn 0; }而Udp客户端构建对象就不需要这么麻烦了因为我们早在最开始已经将Socket类进行了封装这样我们就不需要在调用原生接口在客户端裸露式调用 InetAddr serveraddr(serverip, serverport);// 通过ip和port构建InetAddr对象 std::unique_ptrSocket cli std::make_uniqueTcpSocket();// 子类对象构造父类指针方便多态式调用 bool res cli-BuildClientSocket(serveraddr);// 建立客户端Socket 链接此时客户端的Socket服务就已经构建完毕网络通信已经做好准备只要服务器通客户端随时都可以发起连接。接着就是处理客户端的业务逻辑。 我们要知道客户端是要给服务器端发送请求并且获取相应的一个过程获取成功之后将响应进行反序列化拿到最终的结果。为了方便测试我们这里让客户端采用固定的提问方式不断对客户端发送请求获取响应并且解析我们将构建请求以及接收响应封装为一个 Factory类。 其中客户端请求让 x 为 1-10的随机数y为 0-4的随机数让他们进行模运算并将计算构造为Request类返回值为Request的指针 class Factory { public:Factory(){srand(time(nullptr) ^ getpid());opers /*/%^|;}std::shared_ptrRequest BuildRequest(){int x rand() % 10 1;usleep(x * 10);int y rand() % 5; // [0,1,2,3,4]usleep(y * x * 5);char oper opers[rand() % opers.size()];std::shared_ptrRequest req std::make_sharedRequest(x, y, oper);return req;}std::shared_ptrResponse BuildResponse(){return std::make_sharedResponse();}~Factory(){}private:std::string opers;// 操作数: /-/*/\/% ... };为了方便测试我们这里只启动一个客户端这个客户端不停的给服务器发送数据所以我们需要将待发送请求以及返回的响应放在while循环内不断发送获取解析。 我们想要积压一批数据然后在一次性发送这样就能测试服务器的功能是否有问题是否能处理多批数据所以在这里我们一次性构建五个请求让后在发送给服务器端同样构建请求时需要对数据进行序列化和添加长度报头 Factory factory; std::string inbuffer;while (res) {sleep(1);// 构建请求std::string str;for (int i 0; i 5; i)// 一次性构建5个请求{auto req factory.BuildRequest();// 对请求进行序列化std::string send_str;req-Serialize(send_str);std::cout Serialize: \n send_str std::endl;// 添加长度报头send_str Encode(send_str);std::cout Encode: \n send_str std::endl;str send_str;}// len\r\n{}\r\ncli-Send(str);// 发送请求 }我们将求情发送之后客户端就静静等待服务器端返回的响应当然与服务器端接收消息相同客户端接收的每条应答一定就是完整的应答吗不一定所以我们将读取到的数据进行Decode()这样我们对所有的应答进行解析如果是一条完整的应答Decode接口就会返回一个response对象对象里就是解析过后一条完整的应答内容 while (res) {sleep(1);// 构建请求std::string str;for (int i 0; i 5; i){auto req factory.BuildRequest();// 对请求进行序列化std::string send_str;req-Serialize(send_str);std::cout Serialize: \n send_str std::endl;// 添加长度报头send_str Encode(send_str);std::cout Encode: \n send_str std::endl;str send_str;}// len\r\n{}\r\ncli-Send(str);// 读取应答int n cli-Recv(inbuffer);if (n 0)break;std::string package Decode(inbuffer);if (package.empty())continue; }那么此后我们获取的应答就一定是一条完整的应答但是这个应答此时还是序列化状态我们需要将其进行反序列化处理最后输出响应结果即可 while (res) {sleep(1);// 构建请求std::string str;for (int i 0; i 5; i){auto req factory.BuildRequest();// 对请求进行序列化std::string send_str;req-Serialize(send_str);std::cout Serialize: \n send_str std::endl;// 添加长度报头send_str Encode(send_str);std::cout Encode: \n send_str std::endl;str send_str;}// len\r\n{}\r\ncli-Send(str);// 读取应答int n cli-Recv(inbuffer);if (n 0)break;std::string package Decode(inbuffer);if (package.empty())continue;// 读到的package一定是一个完整的应答auto resp factory.BuildResponse();// 反序列化resp-Deserialize(package);// 拿到了结构化的应答std::cout resp-_result [ resp-_code ] std::endl; }为了更好地体现服务器端对报文的处理是否正确我们在TcpServerMain内的Service服务进行细微调整前面我们让客户端不断地对服务器端发出请求那么服务器端的Service也要不断地去处理请求并发送到客户端 void ServiceHelper(socket_sptr sockptr, InetAddr client) {int sockfd sockptr-SockFd();LOG(DEBUG, get a new link, info %s:%d, fd : %d, client.IP().c_str(), client.Port(), sockfd);std::string clientaddr [ client.IP() : std::to_string(client.Port()) ]# ;std::string inbuffer;while (true){sleep(5);Request req;// 1. 读取数据int n sockptr-Recv(inbuffer);if (n 0){LOG(DEBUG, client %s quit, clientaddr.c_str());break;}// 2. 分析数据std::string package;while (true){sleep(1);std::cout inbuffer inbuffer std::endl;package Decode(inbuffer);if (package.empty())break;std::cout -----------------------begin---------------------- std::endl;std::cout resq string:\n package std::endl;// 3.反序列化req.Deserialize(package);// 4. 业务处理Response resp _cb(req);// 5. 对应答进行序列化std::string send_str;resp.Serialize(send_str);std::cout send_str std::endl;// 6. 添加长度报头send_str Encode(send_str);// 7. 发送到对端sockptr-Send(send_str);}} }那么我们所有准备都已经做好了接下来就是通信时刻 这样网路版本计算器我们就实现完成了。在这个项目当中我们发现我们把从Tcp内读取的报文可能读到半个可能读到一个半或者其他特殊不完整报文情况这种情况我们称为 Tcp粘包问题。而我们使用Encode() 和 Decode() 接口就是为了解决tcp粘包问题的。 基于守护进程的改写 我们知道我们在连接远程服务器的时候实际上就是打开一个终端文件如果有多个连接就会打开多个终端文件我们从一台设备向另一台设备进行重定向的时候就是如此 并且我们可以将消息发送到另外一个终端文件当中使用如下命令进行重定向 echo message /dev/pts/n #这里n指的是任何一个存在的终端文件这里如果你是使用XShell来测试上面的命令你很可能不会成功因为版本升级的原因但是我们能知道这个现象就行。总而言之当我们连接Linux服务器的时候会给我们打开一个终端文件再启动bash命令行解释器。 首先是创建终端文件其次bash被启动而bash又作为所有进程的父进程bash则会打开创建的终端文件。而一般终端文件与启动的bash会被打包称为一个 会话(具体在以后守护进程章节中看到)而每个会话都会有自己的 会话id(sid)而一般 会话的id是终端中的第一个进程的pid也就是bash 但是只要在当前终端下启动的任何服务(进程)都属于当前的会话比如 所以会话就像是bash进程中的管理容器如果一个会话被销毁了那么会话里的所有进程也都会终止 但是今天我想要一种不受会话影响的进程也就是不受用户登录退出的影响独立于会话之外的进程比如我们的网络版计算器服务器端我们不想让其受用户注册销毁的影响所以我们可以编写代码将其变为 守护进程。 实际上这么做的意义就是创建一个新的会话在Linux中给我们提供了 setsid() 接口 setsid()会创建出一个新的会话不过有一个要求调用进程不能是进程组的组长。那么什么是进程组呢很简单每个进程组都有一个唯一的标识符通常是进程组的组长Leader的进程ID而组长就是他们之中第一个启动的进程 所以我们在程序中直接创建子进程并且退出父进程那么那么当前进程就可以调用setid()接口了这个进程也就独立出会话之外成为一个全新的会话我们称之为 守护进程(精灵进程)使用类似一下代码 if(fork() 0) exit(0); setsid();当然如果你嫌麻烦大可不必写长点的代码因为Linux早就给我们想好了给我们提供了一个 Daemon() 接口 nochdir 参数是否更改当前进程的工作目录。如果更改守护进程的目录就会切换为根目录如果不更改则在启动时的路径下。nocliose参数是否需要进行输入输出的处理。 Linux每个终端下都会存在一个null文件/dev/null如果去读取这个文件文件内是没有任何内容的如果对该文件进行写同样也不会保存任何信息而是立刻丢弃。我们知道当我们创建了守护进程也就意味着脱离了原本的会话所以也就没有原本的终端文件了而如果我们要使网络计算器变为守护进程而网络计算器中存在大量的IO操作为了避免因为没有对应的终端文件进行IO而出错我们可以将 012三个文件描述符全部重定向到 /dev/null 当中。 #include iostream #include unistd.hint main() {std::cout Pid is: getpid() std::endl;sleep(1);daemon(0, 0);while(true){std::cout hello test std::endl;sleep(1);}return 0; }以上是一个简单的测试样例daemon内部会自动的fork并且退出父进程 经过测试我们可以看到hello.exe 的TTY也就是终端文件变成了 “?” 也就表示已经不属于当前的会话了而SID同样与当前进程的SID不同并且SID为守护进程的pid。如果我们查看守护进程的工作目录 可以看到守护进程当前工作目录实际上就是在根目录如果我们同时查看该守护进程的文件fd就会发现 由此可见daemon接口的两个参数实际上是bool值类型的第一个参数表示是否更改工作目录第二个参数表示是否更改重定向如果我们把daemon参数设置为daemon(0, 0) 将daemon参数设置为(1, 1)就会导致我们输出的内容还是在上一个会话下并且Ctrl C 也无法终止进程可使用 kill -9 process_pid 杀死进程当我们查询进程工作目录时也能发现其在当前的工作目录下而fd也指向了第一个终端文件。 所以一般情况下我们直接调用 daemon(0, 0)即可但是我们网络版计算器不仅仅有许多的IO还写了很多很重要的日志信息啊这么设置守护进程我们就无法在终端上看到日志信息了不用担心因为早在编写日志之初我们就已经给日志设置为两个选择1. 将信息打印到显示器上。2. 将日志信息打印到终端文件上。我们可以将其打印到日志文件当中 随后启动服务器将其变为一个守护进程然后启动一个客户端连接服务器端 我们之前定义的文件路径就是在当前目录下而我们创建了守护进程并且将工作目录改为了根目录所以我们的log.txt文件只能出现在根目录了。 以上就是网络版计算器实现的全过程了如果这三篇文章对您有所帮助的话还望点赞支持~~
http://www.w-s-a.com/news/784552/

相关文章:

  • 天津建立网站营销设计window7用jsp做的网站要什么工具
  • 英文网站wordpress所有图片
  • 我做的网站怎么打开很慢网络营销典型企业
  • 新增备案网站python3网站开发
  • 诊断网站seo现状的方法与通信工程专业做项目的网站
  • 南京 微网站 建站alexa排名查询统计
  • 天津网站建设企业系统wordpress已发布不显示不出来
  • 大连网站前端制作公司局域网视频网站建设
  • 张家界建设局网站电话wordpress网站怎么建
  • 淄博网站建设有实力装修培训机构哪家最好
  • 彩票网站建设seo优化师是什么
  • 怎么做英文网站网站建设基本费用
  • dede网站名称不能保存wordpress运费设置
  • 出口网站制作好一点的网站建设
  • 在小说网站做编辑怎么找韶关市建设局网站
  • 网站策划怎么做内容旅游型网站建设
  • 东莞百度网站推广ppt模板免费下载的网站
  • 网站建设项目管理基本要求网站空间到期影响
  • 做奖杯的企业网站谁有推荐的网址
  • wordpress能做企业站吗wordpress收发邮件
  • 电子产品网站建设策划方案腾讯企业邮箱注册申请免费
  • 哪些网站可以免费做代码自己电脑做网站服务器广域网访问
  • 高端网站设计青海省教育厅门户网站学籍查询
  • 长春网站优化公司网站制作400哪家好
  • 县级门户网站建设的报告开发游戏的软件有哪些
  • 做电子商务的网站wordpress带会员中心
  • 网站域名不变网站可以从做吗网站建设步骤 文档
  • 网站建设中 gif互联网新项目在哪里找
  • 做外包网站猎头公司英文
  • 房屋结构自建设计 网站海淀教育互动平台