您的位置:首页 > 汽车 > 新车 > seo的工作流程_浙江正规网站建设配件_搜索引擎优化教材答案_西安百度推广怎么做

seo的工作流程_浙江正规网站建设配件_搜索引擎优化教材答案_西安百度推广怎么做

2025/5/19 16:09:46 来源:https://blog.csdn.net/2302_80378107/article/details/147202246  浏览:    关键词:seo的工作流程_浙江正规网站建设配件_搜索引擎优化教材答案_西安百度推广怎么做
seo的工作流程_浙江正规网站建设配件_搜索引擎优化教材答案_西安百度推广怎么做

1.上一篇篇代码改进

bind的绑定第一个是对象,其余的都是参数,传给一个类需要this指针,所以有&r

错误地方是智能指针的参数要加&,thread.name()要删除 

2.介绍需要用到函数

popen函数

FILE *popen(const char *command, const char *type);

  • command:这是一个指向以空字符结尾的字符串的指针,该字符串包含要执行的命令。这个命令可以是任何可以在命令行中运行的命令。

  • type:这是一个字符串,指定管道的方向。它只能是以下两种模式之一:

    • "r":表示从子进程的输出中读取数据。

    • "w":表示向子进程的输入中写入数据。

2. 参数解释

  • cmd.c_str()cmd 是一个 std::string 对象,cmd.c_str() 是一个成员函数,它返回一个指向以空字符结尾的字符串的指针。这个指针可以直接传递给 popen 函数。

  • "r":表示从子进程的输出中读取数据。这意味着子进程的标准输出会被重定向到父进程的文件流中。

3. 返回值

  • popen 函数返回一个指向 FILE 类型的指针,这个指针可以用于后续的 I/O 操作(如 freadfwritefgets 等)。

  • 如果 popen 调用失败,返回值为 NULL

listen函数

int listen(int sockfd, int backlog);

参数

  • sockfd:已经绑定到本地地址的套接字描述符。

  • backlog:未完成连接队列的最大长度。这个值表示操作系统可以为该套接字维护的未完成连接的最大数量。

返回值

  • 成功时返回 0

  • 失败时返回 -1,并设置 errno 以指示错误原因。

作用

  • 将套接字从主动模式转换为被动模式,使其能够接收客户端的连接请求。

  • 设置未完成连接队列的最大长度,当队列已满时,新的连接请求将被拒绝

connect函数

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数

  • sockfd:客户端的套接字描述符。

  • addr:指向 struct sockaddr 的指针,包含服务器的地址信息。

  • addrlenaddr 的长度。

返回值

  • 成功时返回 0

  • 失败时返回 -1,并设置 errno 以指示错误原因。

作用

  • 将客户端的套接字连接到指定的服务器地址。

  • 如果连接成功,套接字将进入已连接状态,可以进行数据传输。

accept函数

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

 

参数

  • sockfd:监听套接字描述符。

  • addr:指向 struct sockaddr 的指针,用于存储客户端的地址信息。如果不需要获取客户端的地址信息,可以将其设置为 NULL

  • addrlen:指向 socklen_t 的指针,表示 addr 的长度。在调用 accept 之前,需要将其设置为 addr 的大小。调用后,addrlen 会被设置为实际存储在 addr 中的地址的长度。

返回值

  • 成功时返回一个新的套接字描述符,用于与客户端通信。

  • 失败时返回 -1,并设置 errno 以指示错误原因。

作用

  • 从监听队列中取出一个已完成的连接请求,并为该连接创建一个新的套接字。

  • 新的套接字描述符可以用于与客户端进行数据传输。

fgets函数

char *fgets(char *str, int size, FILE *stream);

参数

  • str:指向字符数组的指针,用于存储读取到的字符串。

  • size:指定字符数组的最大长度。fgets 会读取最多 size - 1 个字符,以确保在数组末尾添加空字符 \0

  • stream:文件流指针,指向要读取的文件。

返回值

  • 如果成功读取到一行数据,返回值是 str,即指向存储读取到的字符串的指针。

  • 如果到达文件末尾或发生错误,返回值为 NULL

3.TcpServer文件

1.Init函数实现

开始在栈区上创建一个套接字,接着创建InetAddr类的对象,把端口号传过去,会构造一个sockaddr_in的结构体,并初始化这个结构体,接着把栈区上的套接字作为参数执行bind函数,进入到内核态中绑定,使套接字有效,因为是tcp通信就需要相互连接,就需要listen函数,把状态变为监听。

listen,connect和accept:

假设你正在经营一家餐厅,`listen`、`connect` 和 `accept` 的过程可以类比为以下场景:

1. **`listen`**:
   - 你告诉员工:“开始接听预订电话。”
   - 这时,你的电话系统(套接字)处于监听状态,准备接收客户的预订请求。

2. **`connect`**:
   - 客户拨打你的预订电话,尝试与你联系。
   - 这就好比客户端调用 `connect`,向服务器发起连接请求。

3. **`accept`**:
   - 你的员工接听电话,并记录下客户的预订信息。
   - 这就好比服务器调用 `accept`,接受客户端的连接请求,并为该客户分配一个新的资源

 void Init(){_listensockfd=socket(AF_INET,SOCK_STREAM,0);if(_listensockfd<0){LOG(LogLevel::FATAL)<<"socket error";exit(SOCKET_ERR);}LOG(LogLevel::INFO)<<"socket success:"<<_listensockfd;InetAddr local(_port);int n=bind(_listensockfd,local.NetAddrPtr(),local.NetAddrLen());if(n<0){LOG(LogLevel::FATAL)<<"bind error";exit(BIND_ERR);}LOG(LogLevel::INFO)<<"bind success:"<<_listensockfd;n=listen(_listensockfd,backlog);if(n<0){LOG(LogLevel::FATAL)<<"listen error";exit(LISTEN_ERR);}LOG(LogLevel::INFO)<<"listen sucess:"<<_listensockfd;}InetAddr(uint16_t port):_port(port),_ip(){memset(&_addr,0,sizeof(_addr));_addr.sin_family=AF_INET;_addr.sin_addr.s_addr=INADDR_ANY;_addr.sin_port=htons(_port);}

2.service函数实现

创建一个缓冲区buffer,调用read函数读取文件描述符为sockfd的文件,把读取到的消息存储到buffer中,返回值n为实际读取到的大小,大于0就日志显示是谁发送的,然后执行回调函数_func,并把结果通过write函数写到文件描述符为sockfd的文件上,因为read函数会阻塞,所以需要设置多个判断,为0就说明没有输入,就i显示退出,然后关闭文件,取它的就是出现异常,关闭文件并退出。

 void Service(int sockfd,InetAddr& peer){char buffer[1024];while(true){ssize_t n=read(sockfd,buffer,sizeof(buffer)-1);if(n>0){buffer[n]=0;LOG(LogLevel::DEBUG)<<peer.StringAddr()<<"#"<<buffer;std::string echo_string=_func(buffer,peer);write(sockfd,echo_string.c_str(),echo_string.size());}else if(n==0){LOG(LogLevel::DEBUG)<<peer.StringAddr()<<"退出了";close(sockfd);break;}else{LOG(LogLevel::DEBUG)<<peer.StringAddr()<<"异常";close(sockfd);break;}}}

3.Run函数实现

设置isrunning状态,创建sockaddr_in结构体,得到其大小,调用accept函数开始处理要连接的客服端,如果没有要连接的就会处于阻塞状态,连接成功就可以得到客服端的信息,放到类型为InetAddr的addr内部处理,可以打印出地址信息,接着就是处理任务了,这里方案是创建多线程,pthread_create函数创建线程,传入创建的tid,执行的函数Routine,以及this,执行函数内部是先设置为分离状态,避免阻塞(卡住主线程),创建ThreadData类原因是线程函数只能传一个参数,所以需要把所有的参数封装在一起,把结构体传过去,线程函数执行service函数,service函数内部有回调函数,所以就可以处理任务,拿到结果。

方案二:

通过fork函数创建子进程,id==0时是子进程执行的代码,然后关闭listensock,因为用不上避免错误,接着在创建一个子进程,也就是有爷子孙三个进程,把任务给孙子进程执行,然后关闭子进程,因为父进程要阻塞回收子进程资源,而关闭就不会了,孙子进程因为子进程关闭而变成孤儿进程,就会在任务执行完成后自动结束。

方案三:

线程池方案,使用lambda形式把任务入队列,提前设置线程最多数量,就可以把任务队列的任务取出来执行。

   class ThreadData{public:ThreadData(int fd,InetAddr& ar,TcpServer* s):sockfd(fd),addr(ar),tsvr(s){}public:int sockfd;InetAddr addr;TcpServer* tsvr;};static void* Routine(void* args){pthread_detach(pthread_self());//避免阻塞ThreadData* td=static_cast<ThreadData*>(args);td->tsvr->Service(td->sockfd,td->addr);delete td;return nullptr;}void Run(){_isrunning=true;while(_isrunning){struct sockaddr_in peer;socklen_t len=sizeof(sockaddr_in);int sockfd=accept(_listensockfd,CONV(peer),&len);if(sockfd<0){LOG(LogLevel::WARNING)<<"accept error";continue;}InetAddr addr(peer);LOG(LogLevel::INFO)<<"accept success ,peer addr:"<<addr.StringAddr();//多线程版本ThreadData* td=new ThreadData(sockfd,addr,this);pthread_t tid;pthread_create(&tid,nullptr,Routine,td);// //父进程// pid_t id=fork();// if(id<0)// {//     LOG(LogLevel::FATAL)<<"fork error";//     exit(FORK_ERR);// }// else if(id==0)// {//     //不让子进程访问listensock//     close(_listensockfd);//     if(fork()>0)//         exit(OK);//     Service(sockfd,addr);//     exit(OK);// }// else// {//     close(sockfd);//     pid_t rid=waitpid(id,nullptr,0);//     (void)rid;// }//      //线程池版本,适合于短服务// ThreadPool<task_t>::GetInstance()->Enqueue([this,sockfd,&addr](){// this->Service(sockfd,addr);// });}_isrunning=false;}

4.Server.cc文件

Usage函数目的是命令行参数不足时执行这个函数,主函数获取命令行参数,得到端口号,创建command对象,然后智能指针指向用make_unique创建的对象,这个对象的创建就可以看知道回调函数就是执行execute,参数为端口号和bind绑定的Command内部的函数,也可以不绑定,就是一个端口号和要执行什么任务作为参数,调用创建对象的init函数和run函数。

#include "Command.hpp"
#include "TcpServer.hpp"std::string defaulthandler(const std::string& word,InetAddr& addr)
{LOG(LogLevel::DEBUG)<<"回调到了defaulthander";std::string s="haha ";s+=word;return s;
}void Usage(std::string proc)
{std::cerr<<"Usage:"<<proc<<"port"<<std::endl;
}int main(int argc,char* argv[])
{if(argc!=2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port=std::stoi(argv[1]);Enable_Console_Log_Strategy();Command cmd;std::unique_ptr<TcpServer> tsvr=std::make_unique<TcpServer>(port,std::bind(&Command::Execute,&cmd,std::placeholders::_1,std::placeholders::_2));tsvr->Init();tsvr->Run();
}

5. Tcpclient.cc文件

读取命令行参数,获得端口号和ip地址,创建套接字,客服端的套接字不需要绑定,避免端口冲突,os会自动随机绑定空的端口,创建类型为InetAddr的serveraddr对象,会对地址和端口号进行处理,执行connect函数,发起连接请求,服务器执行accept函数就会完成连接。while一直循环,创建line读取cin输入流内容,输入流会完line里面写入,执行write函数把line内容写到文件描述符为sockfd的文件,创建buffer,调用read函数把sockfd对应的文件内容读到buffer里面,读取成功打印buffer内容,最后关闭文件。

#include <iostream>
#include "Common.hpp"
#include "InetAddr.hpp"void Usage(std::string proc)
{std::cerr<<"Usage::"<<proc<<"server_ip server_port"<<std::endl;
}int main(int argc,char* argv[])
{if(argc!=3){Usage(argv[0]);exit(USAGE_ERR);}std::string serverip=argv[1];uint16_t serverport=std::stoi(argv[2]);int sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){std::cerr<<"sock error"<<std::endl;exit(SOCKET_ERR);}InetAddr serveraddr(serverip,serverport);int n=connect(sockfd,serveraddr.NetAddrPtr(),serveraddr.NetAddrLen());if(n<0){std::cerr<<"connect error"<<std::endl;exit(CONNECT_ERR);}while(true){std::string line;std::cout<<"Please Enter@";getline(std::cin,line);write(sockfd,line.c_str(),line.size());char buffer[1024];ssize_t size=read(sockfd,buffer,sizeof(buffer)-1);if(size>0){buffer[size]=0;std::cout<<"server echo#"<<buffer<<std::endl;}}close(sockfd);return 0;
}

6.Common.hpp文件

实现了一个枚举,跟exit搭配,对应不同退出信息,NoCopy类是不可以靠谱构造和赋值重载的,于继承一起,就可以让禁止拷贝和赋值的类继承这个类,就可以不用在内部写多份(多个类都要满足禁止拷贝和赋值),只要作为NoCopy类的派生类就不可以拷贝和赋值。

#pragma once#include <iostream>
#include <functional>
#include <unistd.h>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>enum ExitCode
{OK=0,USAGE_ERR,SOCKET_ERR,BIND_ERR,LISTEN_ERR,CONNECT_ERR,FORK_ERR
};class NoCopy
{
public:NoCopy() {}~NoCopy() {}NoCopy(const NoCopy&) =delete;const NoCopy& operator=(const NoCopy&) =delete;
};#define CONV(addr) ((struct sockaddr*)&addr)

7.Command.hpp文件

远端执行远程命令,就需要设置一些限制,不然执行一些命令,比如删除,创建这类执行是危险的,成员变量为set的容器,把可以用的命令放到容器中,只用输入这些指令才是有效的,其余都是无用的,issafecommand函数可以判断,通过遍历容器。execute函数是具体的处理,先判断指令是否在容器中,接着执行popen函数,用于子进程来执行指定的命令,并通过管道于子进程的输出进行交互,使用fgets函数从fp指向的文件进行读取内容,按行读取直到没有为止,然后把读取的内容与格式话拼接,把结果返回,然后server端就可以得到最后返回的结果。

#pragma once#include <iostream>
#include <string>
#include <cstdio>
#include <set>
#include "Command.hpp"
#include "InetAddr.hpp"
#include "Log.hpp"using namespace LogModule;class Command
{
public:Command(){_WhiteListCommands.insert("ls");_WhiteListCommands.insert("pwd");_WhiteListCommands.insert("ls -l");_WhiteListCommands.insert("touch haha.txt");_WhiteListCommands.insert("who");_WhiteListCommands.insert("whoami");}bool IsSafeCommand(const std::string& cmd){auto iter=_WhiteListCommands.find(cmd);return iter!=_WhiteListCommands.end();}std::string Execute(const std::string& cmd,InetAddr& addr){if(!IsSafeCommand(cmd)){return std::string("违规");}std::string who=addr.StringAddr();FILE* fp=popen(cmd.c_str(),"r");if(nullptr==fp){return std::string("执行命令不存在:")+cmd;}std::string res;char line[1024];while(fgets(line,sizeof(line),fp)){res+=line;}pclose(fp);std::string result=who+"execute done,result is: \n"+res;LOG(LogLevel::DEBUG)<<result;return result;}~Command(){}
private:std::set<std::string> _WhiteListCommands;
};

 

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com