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

苏州网站开发建设广州天河酒店网站建设

苏州网站开发建设,广州天河酒店网站建设,南京建设公司网站,网站建设公司(深圳信科)目录 一#xff0c;服务器初始化 1.0 部分文件代码 1.1 关于Tcp协议 1.2 创建和绑定套接字 1.3 监听 二#xff0c;服务器启动 2.1 获取连接 2.2 提供服务 2.3 客户端启动源文件 Main.cc 二#xff0c;客户端编写 2.1 关于Tcp客户端 2.2 客户端代码 2.3 效果…目录 一服务器初始化 1.0 部分文件代码 1.1 关于Tcp协议 1.2 创建和绑定套接字  1.3 监听 二服务器启动 2.1 获取连接 2.2 提供服务 2.3 客户端启动源文件 Main.cc  二客户端编写 2.1 关于Tcp客户端 2.2 客户端代码  2.3 效果演示 2.4 优化 三字段翻译的应用场景 3.1 翻译功能实现 3.2 效果演示 四守护进程 4.1 理解“会话”“前台”和“后台” 4.1 关于守护进程 4.4 将服务器实现成守护进程版本 一服务器初始化 1.0 部分文件代码 代码文件计算机网络/网络编程套接字/Tcp · 小堃学编程/Linux学习 - 码云 - 开源中国 (gitee.com) 此网络程序用到的头文件有这几个可以先全部创建出来 Log.hpp 日志文件 #pragma once#include iostream #include time.h #include stdarg.h #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h #include stdlib.h#define SIZE 1024#define Info 0 #define Debug 1 #define Warning 2 #define Error 3 #define Fatal 4#define Screen 1 #define Onefile 2 #define Classfile 3#define LogFile log.txtclass Log { public:Log(){printMethod Screen;path ./log/;}void Enable(int method){printMethod method;}std::string levelToString(int level){switch (level){case Info:return Info;case Debug:return Debug;case Warning:return Warning;case Error:return Error;case Fatal:return Fatal;default:return None;}}void printLog(int level, const std::string logtxt){switch (printMethod){case Screen:std::cout logtxt std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string logname, const std::string logtxt){std::string _logname path logname;int fd open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // log.txtif (fd 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string logtxt){std::string filename LogFile;filename .;filename levelToString(level); // log.txt.Debug/Warning/FatalprintOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t time(nullptr);struct tm *ctime localtime(t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), [%s][%d-%d-%d %d:%d:%d], levelToString(level).c_str(),ctime-tm_year 1900, ctime-tm_mon 1, ctime-tm_mday,ctime-tm_hour, ctime-tm_min, ctime-tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式默认部分自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), %s %s, leftbuffer, rightbuffer);// printf(%s, logtxt); // 暂时打印printLog(level, logtxt);}private:int printMethod;std::string path; };Log log; 然后是makefile文件 .PHONY:all all:tcpserver tcpclient tcpserver:Main.ccg -o $ $^ -stdc11 -lpthread tcpclient:TcpClient.ccg -o $ $^ -stdc11.PHONY:clean clean:rm -f tcpserver tcpclient 然后是线程池文件ThreadPool这个文件其实就是我们之前写的线程池”Linux系统编程——线程池_linux系统编程 线程池-CSDN博客 #pragma once#include vector #include queue #include unistd.h#include thread.hpp #include lockGuard.hpp #include log.hppconst int g_thread_num 3; // 表示默认创建线程个数// 线程池本质是一个生产消费模型 template class T class ThreadPool { public:pthread_mutex_t *getMutex() // 获取锁的地址{return _lock;}bool isEmpty() // 判断队列是否为空{return task_queue_.empty();}void waitCond(){pthread_cond_wait(_cond, _lock); // 等待的时候释放锁唤醒时再申请锁}T getTask(){T t task_queue_.front();task_queue_.pop();return t;}public:void run() // 线程池启动{for (int i 1; i _num; i){_threads.push_back(new Thread(i, routine, this)); // 传this指针让回调方法能够访问类}for (auto iter : _threads){iter-start(); // 执行thread_create函数创建线程创建的数量由数组大小来定而数组大小在构造函数定义好了// std::cout iter-GetName() 启动成功 std::endl;logMessage(NORMAL, %s%s, iter-GetName().c_str(), 启动成功);}}// 取任务// 如果定义在类内会有隐藏this指针从而影响使用所以加上static// 如果一个类内部成员用static那么它只能使用静态成员再调用静态方法无法使用类内的成员属性和方法// 如果这个静态的routine是所谓的消费线程那么要pop队列但是编译时会报错这就坑了// 所以为了能让routine拿到类内属性我们再上面push_back的插入Thread对象时可以把this指针传过来通过函数来进行访问与其让它拿到task_queue不如让它拿到整体对象static void *routine(void *args){ThreadData *td static_castThreadData *(args); // 该操作形象点说就是改文件后缀这里的后缀是args指针ThreadPoolT *tp static_castThreadPoolT *(td-_args); // 然后这一步相当于解压操作拿到指针指向对象的线程池指针// 消费逻辑// 先加锁while(task_queue_.empty()) wait(); 如果任务队列为空就等待// 不为空就获取任务然后处理处理完就解锁while (true){T task;{lockGuard lockguard(tp-getMutex()); // 通过this指针调用getMutex获得锁的地址实现加锁保证该代码块是安全的代码块while (tp-isEmpty())tp-waitCond(); // 判断队列是否为空为空就等待// 读取任务task tp-getTask(); // 任务队列是共享的这句话就是将任务从共享拿到自己的私有空间}task(td-_name); // 执行任务task是队列里的数据也就是Task类改类重载了operator()所以可以直接使用圆括号执行任务// 测试能否传入this指针// tp-show();// sleep(1);}}// 往队列里塞任务void pushTask(const T task){lockGuard lockguard(_lock); // 只单纯加锁加了任务后还应该要唤醒对应的消费线程来消费task_queue_.push(task);pthread_cond_signal(_cond);}static ThreadPoolT *GetInstance(){if (nullptr _tp) // 首次使用时创建对象并且在加锁前先判断一次能减少加锁解锁的次数提高效率{pthread_mutex_lock(_mutex);if (nullptr _tp){std::cout 创建单例 std::endl;_tp new ThreadPoolT();}pthread_mutex_unlock(_mutex);}return _tp;}private:ThreadPool(int thread_num g_thread_num): _num(thread_num){pthread_mutex_init(_lock, nullptr); // 初始化锁pthread_cond_init(_cond, nullptr); // 初始化条件变量}~ThreadPool(){for (auto iter : _threads){iter-join(); // 在释放前join下delete iter;}pthread_mutex_destroy(_lock);pthread_cond_destroy(_cond);}ThreadPool(const ThreadPoolT ) delete;const ThreadPoolT operator(const ThreadPoolT ) delete; // abcprivate:std::vectorThread * _threads; // 这个数组存的是将来要创建的线程int _num;std::queueT task_queue_; // 别人发任务来放到队列里然后派发给指定线程去执行所以只要添加到队列里就自动叫醒一个线程来处理pthread_mutex_t _lock;pthread_cond_t _cond;// 另一种方案// 我们一开始定义两个队列queue1queue2// 然后再定义两个制作std::queueT *p_queue, *c_queue// 然后p_queue-queue1, c_queue-queue2// 当生产一批任务后我们放到queue1里然后swap(p_queue, c)queue);// 然后消费者处理完毕后再swap(p_queue, c_queue);// 所以因为我们生产和消费用的是不同的队列未来我们进行资源任务处理的时候仅仅只需要交换制作而且也只要把这个交换这一句加锁即可static ThreadPoolT *_tp;static pthread_mutex_t _mutex; };template class T ThreadPoolT *ThreadPoolT::_tp nullptr; // 静态成员一般在类外面进行初始化template class T pthread_mutex_t ThreadPoolT::_mutex PTHREAD_MUTEX_INITIALIZER; 其余的文件在后面的讲解中会一一讲解的  1.1 关于Tcp协议 首先我们把服务器封装成一个类这个类包含服务器的初始化函数和启动函数 Tcp协议服务器初始化的基本步骤和Udp是一样的只是Tcp多了一点东西 在创建套接字是协议家族选择AF_INET表示进行网络通信创建套接字时服务类型选择SOCK_STREAM表示有序的可靠的全双工的已经基于连接的流式服务也就是Tcp协议 1.2 创建和绑定套接字  下面是 #pragma once #include iostream #include string.h #include unistd.h #include sys/wait.h #include signal.h #include pthread.h #include signal.h // 下面四个是套接字编程基本头文件 #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h#include ThreadPool.hpp #include Log.hpp #include Task.hpp #include daemon.hppextern Log log;const int defaultfd -1; // 套接字初始化为-1 const std::string defaultip 0.0.0.0; const int backlog 10; // 这个是listen第二个参数一般设置的时候不要设置太大该参数和Tcp协议内部的一个等待队列有关目前只要知道这个队列不能太长就可以了以后详细解释Tcp协议时会讲解enum {UsageError 1,SOCKET_ERR,BIND_ERR,ListenError };class TcpServer;class ThreadData { public:ThreadData(int fd, const std::string ip, const uint16_t port, TcpServer *t): sockfd(fd), clientip(ip), clientport(port), tsvr(t){}public:int sockfd;std::string clientip;uint16_t clientport;TcpServer *tsvr; };class TcpServer { public:TcpServer(const uint16_t port 8888, const std::string ip defaultip): _listensockfd(defaultfd), _port(port), _ip(ip){}void InitServer(){// 1创建Tcp套接字_listensockfd socket(AF_INET, SOCK_STREAM, 0); // SOCK_STREAM表示可靠的双向的基于连接的字节流服务就是Tcp协议if (_listensockfd 0) // 创建失败{log(Fatal, listensocket create error: %d, errorstring: %s, errno, strerror(errno));exit(SOCKET_ERR);}log(Info, listensocket create success,_listensockfd: %d, _listensockfd); // 创建成功输出日志int opt 1;setsockopt(_listensockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, opt, sizeof(opt)); // 防止偶发性的服务器挂掉后无法立即重启Tcp协议理论再详细了解// 2绑定struct sockaddr_in local;memset(local, 0, sizeof(local));local.sin_family AF_INET;local.sin_port htons(_port);// local.sin_addr.s_addr inet_addr(_ip.c_str()); //这两个的效果一样把字符串转四字节inet_aton(_ip.c_str(), (local.sin_addr));// local.sin_addr.s_addr INADDR_ANY;int n bind(_listensockfd, (struct sockaddr *)local, sizeof(local));if (n 0){log(Fatal, bind errno: %d, errorstring: %s, errno, strerror(errno));exit(BIND_ERR);}log(Info, bind success, errno: %d, errorstring: %s, errno, strerror(errno));// Tcp是面向连接的所以服务器一般是比较“被动的”必须随时应为来自客户端的请求所以服务器要一直处于一种等待连接到来的状态// 3监听后面实现}void Start(){// 后面实现 }~TcpServer() {}private:int _listensockfd; // 套接字uint16_t _port; // 端口号std::string _ip; // IP }; 1.3 监听 Udp服务器的初始化只有上面创建套接字和绑定套接字两步但是Tcp不一样 服务器一般都是比较“被动的”因为服务器必须随时应对来自客户端的请求所以服务器要一直处于一种等到连接到来的状态所以就需要将Tcp服务器从创建的套接字设置为监听状态。 问题你这上面说了跟没说一样我就想知道监听到底是啥为什么Tcp需要监听Udp不需要 场景有一家饭店甲开在景区每到放假就有很多人来但是店对面也开了一家饭店乙所以为了提高竞争力所以饭店甲派出了宣传员在店门口招揽客人当有客人来时宣传员就把客人请进店里然后招呼店里的服务员来提供服务然后宣传员就又跑店门口宣传去了。 解答在上面的场景中我们把宣传员在店门口“拉客”这种行为就叫做“监听”对应到程序中“监听”的作用就是将连到我这个服务器的套接字从底层“拉”上来方便服务器提供服务因为Tcp是面向连接的对于连接的各方面的细节把控都要比Udp严格。 监听的sock API和它的英文翻译一样就是listen 第一个参数就是需要设置成监听的套接字而对于第二个参数我们在后面讲Tcp协议报头的时候再讲现在只需要知道这个东西和一个队列有关而这个队列不能太长目前我们设置为10即可 // Tcp是面向连接的所以服务器一般是比较“被动的”必须随时应为来自客户端的请求所以服务器要一直处于一种等待连接到来的状态 // 3listen监听man 2 listen表示将套接字设置为监听状态成功返回0错误返回-1错误码被设置 if (listen(_listensockfd, backlog) 0) {log(Fatal, listen error: %d, errorstring: %s, errno, strerror(errno));exit(ListenError); } log(Info, listen success, errno: %d, errorstring: %s, errno, strerror(errno)); 二服务器启动 2.1 获取连接 服务器启动之后要做的事情主要也就是两个获取连接然后进行处理 Tcp是面向连接的所以Tcp在真正进行数据通信前都要先与客户端建立连接才能通信用到的sock API也和连接的英文翻译一样accept函数 sockfd这个就是我们前面listen监听函数使用的那个因为_listrnsockfd核心工作是在底层获取新连接真正提供数据通信服务的是accept返回的sockfdaddr我们的老朋友sockaddr的结构体指针作为输出型参数保存客户端的各种信息方便后面返回信息给客户端addrlen表示sockaddr的大小 void Start() {log(Info, tcpserver is running...);while (true){// 1获取新连接struct sockaddr_in client;socklen_t len sizeof(client);int sockfd accept(_listensockfd, (struct sockaddr *)client, len);// 问题Udp只要一个套接字为啥Tcp有上面的sockfd和类的_listensockfd两个甚至以后会有多个呢//_listensockfd核心工作就是在底层获取新的连接真正提供数据通信服务的是accept返回的sockfd。所以我们会有两个套接字获取新链接的套接字叫做“监听套接字”if (sockfd 0){log(Warning, accept errno: %d, errorstring: %s, errno, strerror(errno));continue; // 一次获取失败就重新获取}uint16_t clientport ntohs(client.sin_port); // 获取客户端port、char clientip[32];inet_ntop(AF_INET, (client.sin_addr), clientip, sizeof(clientip)); // 获取客户端的ip地址// 2根据新连接来进行通信log(Info, get a new link..., sockfd: %d, client ip: %s, client port: %d, sockfd, clientip, clientport);Service(sockfd, clientip, clientport); // 给连接过来的ip进行服务// 问题1客户端退了服务器咋办 2客户端断线了咋办close(sockfd);} } 2.2 提供服务 确认连接成功之后就是提供服务了提供服务也分为三步 读取客户端发来的数据处理好数据将结果返回给客户端 后面两点和Udp一样也很好理解但是第一点读取数据Tcp的处理方式和Udp有很大差别  Udp服务器是客户端直接发给服务器的所以Udp服务器需要用recvfrom函数去阻塞式地接受信息但是Tcp协议在底层做了很多工作就比如Tcp是直接维护了网卡文件将客户端发来的数据直接保存在了网卡文件里所以我们Tcp服务器要想获取链接可以用recvfrom阻塞式读取也可以直接用系统的read接口以sockfd为文件描述符直接像读文件那样读取客户端发来的消息即可 下面是处理函数Service的代码 void Service(int sockfd, const std::string clientip, const uint16_t clientport) {char buffer[4096];while (true){ssize_t n read(sockfd, buffer, sizeof(buffer)); // 可以用文件的接口从sockfd里面读数据if (n 0){buffer[n] 0;std::cout client say# buffer std::endl;std::string echo_string tcpserver echo# ;echo_string buffer;write(sockfd, echo_string.c_str(), echo_string.size()); // 也可以用文件的接口往sockfd写回数据}else if (n 0) // 客户端退出会关闭套接字那么read会读出错返回值n会赋值为0{log(Info, %s:%d quit, server close sockfd: %d, clientip.c_str(), clientport, sockfd);break;}else // 读取出错{log(Warning, read error, sockfd: %d, client port: %d, sockfd, clientip.c_str(), clientport);break;}} } 2.3 客户端启动源文件 Main.cc  作用主要是读取命令行输入的IP和Port创建服务器对象初始化服务器运行服务器  #include TcpServer.hpp #include memoryvoid Usage(std::string proc) {std::cout \n\rUsage: proc port[1024]\n std::endl; }// ./tcpserver 8080 int main(int argc, char *argv[]) {if (argc ! 2){Usage(argv[0]);exit(UsageError);}uint16_t port std::stoi(argv[1]);std::unique_ptrTcpServer tcp_svr(new TcpServer(port));tcp_svr-InitServer();tcp_svr-Start();return 0; } 二客户端编写 2.1 关于Tcp客户端 客户端大部分内容和Udp客户端差不多作用很简单就是发送数据给服务器服务器处理好数据后发回来最后客户端打印数据但是也有下面几个需要注意的地方 由于是Tcp协议服务器和客户端所以客户端也要和服务器一样建立连接才能进行通信所以客户端确认连接需要用到connect函数客户端必须要有“断线重连机制”因为Tcp服务是“保证通信可靠的服务”而为了保证“可靠”所以需要做更多的工作花费更多的成本但是也能理解毕竟“世上没有免费的午餐” 断线重连主要涉及两个地方一个是连接之前一个是连接之后客户端和服务器对这两个时段的断线处理机制都不一样 连接之前 服务器就是一直阻塞着等待连接到来客户端客户端在建立链接时如果第一次没连上一般不会立即break退出而是等待一秒再连一次这样依次进行下去当超过重连次数后客户端才会提示说服务器断线或者网络连接断开 连接之后  连接成功之后客户端和服务器就都是对网络文件进行读写所以会直接在读取网络文件时顺便处理断线问题因为只要有一方退出了网络文件就都没了那么服务器就会读取失败 2.2 客户端代码  下面是客户端的代码包括断线重连机制 #include iostream #include cstring #include unistd.h #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.hvoid Usage(const std::string proc) {std::cout \n\rUsage: proc serverip serverport\n std::endl; }int main(int argc, char *argv[]) {int x 0;if (argc ! 3){Usage(argv[0]);exit(1);}std::string serverip argv[1];uint16_t serverport std::stoi(argv[2]);// 填写套接字信息struct sockaddr_in server;memset(server, 0, sizeof(server));server.sin_family AF_INET;server.sin_port htons(serverport);// server.sin_addr.s_addr inet_addr(serverip.c_str()); // 字符串转四字节效果和inet_pton是一样的inet_pton(AF_INET, serverip.c_str(), (server.sin_addr));while (true) // 每次进行翻译后都要重新创建套接字和建立连接因为目前服务器只会提供一次服务{// 创捷套接字int sockfd socket(AF_INET, SOCK_STREAM, 0);if (sockfd 0){std::cerr socket error std::endl;return 1;}int cnt 5; // 断线重连次数int isreconnect false;do{// tcp客户端要bind但是不需要显示bind客户端发起连接的时候系统会自动进行bind随机端口这点和Udp是一样的int n connect(sockfd, (struct sockaddr *)server, sizeof(server));if (n 0) // 连接错误{std::cerr connect error..., reconnect: cnt std::endl;isreconnect true; // 重连失败继续重连cnt--;x 1;sleep(2);}else{isreconnect false; // 重连几次后如果成功就不再重连if (x 1){std::cout reconnect success! std::endl;x 0;}}// 连接成功} while (cnt isreconnect);if (cnt 0) // 超过断线重连次数就直接break退出{std::cerr user offline... std::endl;break;}// 上面是建立确定连接过程下面是正常提供服务std::string message;std::cout Please Enter# ;std::getline(std::cin, message);int n write(sockfd, message.c_str(), message.size()); // 发消息if (n 0){std::cerr write error... std::endl;}char inbuffer[4096];n read(sockfd, inbuffer, sizeof(inbuffer)); // 收消息if (n 0){inbuffer[n] 0;std::cout inbuffer std::endl;}else{std::cerr read error std::endl;}close(sockfd);}return 0; } 2.3 效果演示 我们先演示正常的通信再演示断线机制下面是正常通信 接下来我们演示断线重连的场景 2.4 优化 就拿我们这个处理方式作为例子其实细想一下可以发现单进程处理任务是有问题的 单线程处理任务直接导致我们服务器的执行代码是线性的如果处理时间过长会直接阻塞住而这个时候其他客户端再来连接的话就直接访问失败了因为服务器阻塞着所以服务器不要这样搞 对于这种情况下面有几种处理方法 ①多进程处理  // ②多进程------------------------------------------ pid_t id fork(); if (id 0) // 子进程 {close(_listensockfd); // sockfd是前面打开的描述符所以正常情况下_listensockfd子进程用不到所以可以关闭if (fork() 0) // 创建孙子进程{// 父进程// exit(0);// 这样一写后面的wait等待就不会被阻塞了因为在子进程里面又fork了一次这个父进程退了相当于子进程退了,// 但是这个小的父进程的子进程没有退所以到下面的代码时其实是孙子进程最后提供的服务}// 孙子进程而孙子继承的父进程直接挂掉了最后就会被系统“领养”最后被系统自动回收Service(sockfd, clientip, clientport); // 给连接过来的ip进行服务close(sockfd);exit(0); } else // 父进程 {// 前面父进程获取到的sockfd已经给子进程继承下去给子进程用了所以父进程就不再关心sockfd了//和子进程不关心_listensockfd一样如果不关就会导致系统里面有很多打开的文件没有关所以要关闭不必要的文件描述符close(sockfd);// 这个步骤和管道重定向有相似之处可以重复关因为会有引用计数关掉了只是把计数-1pid_t rid waitpid(id, nullptr, 0); // 阻塞等待但是阻塞不满足要求所以有了孙子进程(void)rid; // 可以直接用signal忽略取消等待 } ​​​ ②多线程处理  // ③多线程 // 创建进程是需要代价的所以多进程版了解一下即可实际开发中不会用多进程模式去搞一般都是用线程去搞 ThreadData *td new ThreadData(sockfd, clientip, clientport, this); pthread_t tid; pthread_create(tid, nullptr, Routine, td); // 多线程这里不能和多进程那样关闭文件描述符因为所有的线程都公用的一个当前进程的文件描述符表 ③最实用的就是利用线程池去搞 首先我们会构建任务然后创建初始化和启动线程池然后把任务放进线程池里这样线程池就会自动帮我们处理任务了 任务头文件Task.hpp的代码如下对于线程池优化我们会结合后面的翻译场景一起搞所以所以Init.hpp会在后面实现 #pragma once #include iostream #include string #include string.h #include Log.hpp #include Init.hppextern Log log; Init init;class Task { public:Task(int sockfd, const std::string clientip, const uint16_t clientport): _sockfd(sockfd), _clientip(clientip), _clientport(clientport){}Task(){}void run(){char buffer[4096];// Tcp是面向字节流的你怎么保证你读取上来的数据是一个 完整 的报文呢ssize_t n read(_sockfd, buffer, sizeof(buffer)); // BUG?if (n 0){buffer[n] 0;std::cout client key# buffer std::endl;std::string echo_string init.translation(buffer); // 执行翻译任务该操作由线程池执行// echo_string buffer; //对话打开翻译去掉int n write(_sockfd, echo_string.c_str(), echo_string.size()); // 写的时候万一对应的客户端断开连接了那么写会崩溃if (n 0){log(Warning, write error, errno: %d, errstring: %s, errno, strerror);}}else if (n 0){log(Info, %s:%d quit, server close sockfd: %d, _clientip.c_str(), _clientport, _sockfd);}else{log(Warning, read error, sockfd: %d, client ip: %s, client port: %d, _sockfd, _clientip.c_str(), _clientport);}close(_sockfd);}void operator()(){run();}~Task(){}private:int _sockfd;std::string _clientip;uint16_t _clientport; }; // ④线程池 --------------------- Task t(sockfd, clientip, clientport); ThreadPoolTask::GetInstance()-Push(t); 三字段翻译的应用场景 3.1 翻译功能实现 客户端什么都不变比较我们这个客户端的作用很简单就是发消息接受消息所以实现翻译功能只要在服务器处理客户端发来的数据的方式上做点事情就好了上面优化的步骤已经将线程池和任务对象搞好了然后我们只需要直线翻译的具体实现即可 翻译需要两个文件 一个是字典文件负责存放部分英文单词和对应的中文翻译的键值对dict.txt一个是查询程序负责读取字典文件里的数据并负责查询并返回Init.hpp dict.txt字典文件负责存放部分英文单词和中文翻译  apple:苹果... banana:香蕉... red:红色... yellow:黄色... the: 这 be: 是 to: 朝向/给/对 and: 和 I: 我 in: 在...里 that: 那个 have: 有 will: 将 for: 为了 but: 但是 as: 像...一样 what: 什么 so: 因此 he: 他 her: 她 his: 他的 they: 他们 we: 我们 their: 他们的 his: 它的 with: 和...一起 she: 她 he: 他宾格 it: 它 Init.hpp查询程序头文件使用unoedered_map #pragma once#include iostream #include string #include fstream #include unordered_map #include Log.hppconst std::string dictname ./dict.txt; const std::string sep :;// yellow:黄色... static bool Split(std::string s, std::string *part1, std::string *part2) {auto pos s.find(sep);if (pos std::string::npos)return false;*part1 s.substr(0, pos);*part2 s.substr(pos 1);return true; }class Init { public:Init(){std::ifstream in(dictname);if (!in.is_open()){log(Fatal, ifstream open %s error, dictname.c_str());exit(1);}std::string line;while (std::getline(in, line)) // 从文件流中读一行{std::string part1, part2;Split(line, part1, part2); // 以冒号为分隔符把字典里的英文和中文隔开然后分别加载到part1和part2里dict.insert({part1, part2}); // 然后再把两个东东放到unordered_map和键值对里去}in.close();}std::string translation(const std::string key){auto iter dict.find(key);if (iter dict.end()) // 找到迭代器结尾表示没找到return Unknow;elsereturn iter-second;}private:std::unordered_mapstd::string, std::string dict; }; 3.2 效果演示 四守护进程 4.1 理解“会话”“前台”和“后台” 一个用户尝试在Linux登录时Linux会形成一个“会话session”每一个会话都会启动一个bash进程这个bash和我们的键盘和显示器相关。 我们执行程序时可以在后面加上“ ”表示将此进程放到后台运行jobs命令可以查看后台任务fg 后套进程编号可以将后台进程重新放回前台 问题如何理解前台和后台 解答哪个进程和标准输入键盘文件关联哪个进程就是前台 我们自己创建的单进程pid和pgid是一样的叫做“自成一组” 而上面我们创建的三个sleep进程pid和pgid是一样的叫做“三个自成一组”这三个sleep合起来就叫做进程组 问题SID是什么 解答用户登录时会创建session会话当登录的用户多了的时候session就多了起来所以Linux需要把这些session也管理起来“先描述再组织”所以系统就会维护一些session结构体同时为了区分各个session结构体就会给它们编号最后就是我们的session id也就是上面的SID 问题上面是创建session会话那么退出会话的时候是什么样的呢 解答当终端直接关掉再重新开个终端重新查的时候之前的后台pppid全变成1了TTY变成问号了TPGID变成-1了。这是因为退了后这几个进程的父进程是bashbash退了变孤儿进程了被1号进程领养所以这种进程是受到了用户登录和退出的影响的 引出守护进程如果我们想让一个进程不受用户登录和注销的影响就要让一个进程守护进程化  4.1 关于守护进程 这里应该是第一次接触到守护进程的这样一个概念要想学一个一个概念还是离不开那三个问题 是什么有什么用咋做到的 守护进程是运行在后台的一种特殊进程独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件 Linux大多数服务器都是守护进程化的Internet服务器的inetdWeb服务器的httpd等。Linux启动时会启动很多系统服务进程这些进程都是在Linux启动时创建因此不受用户登录注销的影响ps ajx可以查看系统中的进程  问题如何做到守护进程 解答 有个接口作用是创建一个会话setsid函数  调用这个函数的进程不能是一个进程组的组长但是如果是自成进程组长的进程就难搞了需要保证自己不是组长第一个进程是组长那么让我不是第一个进程就好了这里就要用到fork父进程直接exit子进程调用setsid(); 所以守护进程的本质也是孤儿进程但是这个孤儿拒绝被领养自强成为一个会话 4.4 将服务器实现成守护进程版本 下面是Deamon.hpp头文件的实现该文件作用就是将调用这个函数的进程实现为守护进程 #pragma once#include iostream #include unistd.h #include cstdlib #include signal.h #include string #include sys/types.h #include sys/stat.h #include fcntl.hconst std::string nullfile /dev/null;void Daemon(const std::string cwd ) // 不传参数的话就是默认把守护进程工作目录放到根目录去 {// 1守护进程需要忽略其它信信号signal(SIGCLD, SIG_IGN); // 直接忽略17号信号为了防止万一出现一些读端关掉了写端还在写的情况守护进程signal(SIGPIPE, SIG_IGN); // 直接忽略13号信号signal(SIGSTOP, SIG_IGN); // 忽略19号暂停信号// 2将自己变成独立的会话if (fork() 0)exit(0); // 直接干掉父进程setsid(); // 子进程自成会话// 3更改当前调用进程的工作目录if (!cwd.empty())chdir(cwd.c_str());// 4不能直接关闭三个输入流打印时会出错Linux中有一个/dev/null 字符文件类似垃圾桶所有往这个文件写的数据会被直接丢弃读也读不到// 所以可以把标准输入输出错误全部重定向到这个文件中// 如果需要就往文件里写反正不能再打印到屏幕上了int fd open(nullfile.c_str(), O_RDWR); // 以读写方式打开if (fd 0) // 打开成功{// 把三个默认流全部重定向到垃圾桶的null的套接字里去dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);close(fd);} } 然后直接在服务器启动函数开头调用Daemon函数即可将服务器实现为守护进程
文章转载自:
http://www.morning.ghwdm.cn.gov.cn.ghwdm.cn
http://www.morning.qfcnp.cn.gov.cn.qfcnp.cn
http://www.morning.pphgl.cn.gov.cn.pphgl.cn
http://www.morning.rxfgh.cn.gov.cn.rxfgh.cn
http://www.morning.lywpd.cn.gov.cn.lywpd.cn
http://www.morning.hncrc.cn.gov.cn.hncrc.cn
http://www.morning.rxzcl.cn.gov.cn.rxzcl.cn
http://www.morning.fbdkb.cn.gov.cn.fbdkb.cn
http://www.morning.xqtqm.cn.gov.cn.xqtqm.cn
http://www.morning.lhldx.cn.gov.cn.lhldx.cn
http://www.morning.ctrkh.cn.gov.cn.ctrkh.cn
http://www.morning.pjrql.cn.gov.cn.pjrql.cn
http://www.morning.nlygm.cn.gov.cn.nlygm.cn
http://www.morning.bwkzn.cn.gov.cn.bwkzn.cn
http://www.morning.tnnfy.cn.gov.cn.tnnfy.cn
http://www.morning.ndynz.cn.gov.cn.ndynz.cn
http://www.morning.cjnfb.cn.gov.cn.cjnfb.cn
http://www.morning.tjmfz.cn.gov.cn.tjmfz.cn
http://www.morning.bzlfw.cn.gov.cn.bzlfw.cn
http://www.morning.mrxqd.cn.gov.cn.mrxqd.cn
http://www.morning.skqfx.cn.gov.cn.skqfx.cn
http://www.morning.phxdc.cn.gov.cn.phxdc.cn
http://www.morning.ktntj.cn.gov.cn.ktntj.cn
http://www.morning.dgpxp.cn.gov.cn.dgpxp.cn
http://www.morning.wnhgb.cn.gov.cn.wnhgb.cn
http://www.morning.xrmwc.cn.gov.cn.xrmwc.cn
http://www.morning.tpchy.cn.gov.cn.tpchy.cn
http://www.morning.jfjpn.cn.gov.cn.jfjpn.cn
http://www.morning.rsbqq.cn.gov.cn.rsbqq.cn
http://www.morning.wslr.cn.gov.cn.wslr.cn
http://www.morning.gsqw.cn.gov.cn.gsqw.cn
http://www.morning.lkhgq.cn.gov.cn.lkhgq.cn
http://www.morning.kqzt.cn.gov.cn.kqzt.cn
http://www.morning.qrpx.cn.gov.cn.qrpx.cn
http://www.morning.rlwcs.cn.gov.cn.rlwcs.cn
http://www.morning.mqxzh.cn.gov.cn.mqxzh.cn
http://www.morning.jrqcj.cn.gov.cn.jrqcj.cn
http://www.morning.rhmt.cn.gov.cn.rhmt.cn
http://www.morning.rzczl.cn.gov.cn.rzczl.cn
http://www.morning.scrnt.cn.gov.cn.scrnt.cn
http://www.morning.ljzgf.cn.gov.cn.ljzgf.cn
http://www.morning.ndxss.cn.gov.cn.ndxss.cn
http://www.morning.rqqkc.cn.gov.cn.rqqkc.cn
http://www.morning.hmxb.cn.gov.cn.hmxb.cn
http://www.morning.ybyln.cn.gov.cn.ybyln.cn
http://www.morning.1000sh.com.gov.cn.1000sh.com
http://www.morning.slqzb.cn.gov.cn.slqzb.cn
http://www.morning.nxzsd.cn.gov.cn.nxzsd.cn
http://www.morning.qytyt.cn.gov.cn.qytyt.cn
http://www.morning.nrwr.cn.gov.cn.nrwr.cn
http://www.morning.trplf.cn.gov.cn.trplf.cn
http://www.morning.kgphd.cn.gov.cn.kgphd.cn
http://www.morning.gzttoyp.com.gov.cn.gzttoyp.com
http://www.morning.nmkbl.cn.gov.cn.nmkbl.cn
http://www.morning.btgxf.cn.gov.cn.btgxf.cn
http://www.morning.jbxmb.cn.gov.cn.jbxmb.cn
http://www.morning.rmrcc.cn.gov.cn.rmrcc.cn
http://www.morning.hnmbq.cn.gov.cn.hnmbq.cn
http://www.morning.lbhck.cn.gov.cn.lbhck.cn
http://www.morning.bwkhp.cn.gov.cn.bwkhp.cn
http://www.morning.fllfz.cn.gov.cn.fllfz.cn
http://www.morning.gtqws.cn.gov.cn.gtqws.cn
http://www.morning.mltsc.cn.gov.cn.mltsc.cn
http://www.morning.mrxgm.cn.gov.cn.mrxgm.cn
http://www.morning.lhqw.cn.gov.cn.lhqw.cn
http://www.morning.lpgw.cn.gov.cn.lpgw.cn
http://www.morning.hkcjx.cn.gov.cn.hkcjx.cn
http://www.morning.qmbgb.cn.gov.cn.qmbgb.cn
http://www.morning.hrjrt.cn.gov.cn.hrjrt.cn
http://www.morning.mwkwg.cn.gov.cn.mwkwg.cn
http://www.morning.zsfooo.com.gov.cn.zsfooo.com
http://www.morning.ltksw.cn.gov.cn.ltksw.cn
http://www.morning.ykrkq.cn.gov.cn.ykrkq.cn
http://www.morning.bqyb.cn.gov.cn.bqyb.cn
http://www.morning.mzskr.cn.gov.cn.mzskr.cn
http://www.morning.fpzpb.cn.gov.cn.fpzpb.cn
http://www.morning.rfhwc.cn.gov.cn.rfhwc.cn
http://www.morning.pcgrq.cn.gov.cn.pcgrq.cn
http://www.morning.qngcq.cn.gov.cn.qngcq.cn
http://www.morning.srzhm.cn.gov.cn.srzhm.cn
http://www.tj-hxxt.cn/news/243355.html

相关文章:

  • 网站建设资源wordpress 多站点模式
  • 深圳宝安网站建设公司推荐营销软文范例大全100字
  • wordpress全站静态化下载一个网站的源码下载
  • 网站开发p6云南省建设厅网站人员查询
  • 百度seo关键词排名推荐韶关seo
  • 科技网站小编网站县区分站点建设
  • 尚品网站建设怎么修改收录网站的标题
  • 在浏览器播放视频成都seo整站
  • 政协 网站建设预约网站模板
  • 聊城做网站的公司新闻30个适合大学生创业的项目
  • 外贸网站 英文摄影作品网站源码
  • 网站开发与设计维护的收费标准网站排名易下拉排名
  • 查询网站域名上海平台网站建设哪家好
  • 竹子建站怎么样网站建设怎么学习
  • 个人的网站怎么备案域名wordpress
  • 什么时候能用ipv6做网站theme my login wordpress
  • 网站布局模式牛魔王网站建设
  • 做网站公司价格网页制作的常用技术
  • 苏州网站建设代理网站漏洞原理
  • 2024年阳性最新症状企业优化推广
  • 做网站建设销售东营企业网站seo
  • 网站建设是固定资产还是列费用淘宝直播要先建设个网站吗
  • 西宁做腋臭北大网站l做一份完整的网站规划书
  • 吉安微信网站网络设计与实施一般包括哪几个阶段
  • 药品网站建设建站技巧
  • 预付做网站定金如何金华网站建设方案咨询
  • 有什么网站是做名片印刷的wordpress调用api接口
  • 网站域名如何备案信息ftp空间网站
  • 东城建设网站商丘网站制作的流程
  • 做的比较唯美的网站网络建设规范和网络维护管理规范属于选择题