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

wordpress全站cdn怎么才能免费建网站

wordpress全站cdn,怎么才能免费建网站,整站排名服务,网站开发代码语言文章目录 Tcp协议的通讯流程一、协议定制与网络版计算器的实现二、json的使用总结 Tcp协议的通讯流程 上一篇文章我们讲解了如何实现Tcp服务器#xff0c;Tcp的接口也用了#xff0c;下面我们就看一下Tcp协议的通讯流程#xff1a; 在服务端#xff0c;我们首先要创建一个… 文章目录 Tcp协议的通讯流程一、协议定制与网络版计算器的实现二、json的使用总结 Tcp协议的通讯流程 上一篇文章我们讲解了如何实现Tcp服务器Tcp的接口也用了下面我们就看一下Tcp协议的通讯流程 在服务端我们首先要创建一个套接字这个套接字被称为监听套接字这个时候服务端处理关闭状态。有套接字后我们开始绑定服务端的ip和端口号然后我们调用listen接口一旦调用成功我们的服务器由close状态就变为监听状态一旦变成监听状态看允许客户端来连我们的服务器了只不过这个时候不获取客户端的链接。监听后我们就可以通过accept接口获取客户端的链接了这个时候accept接口会给我们返回一个套接字这个套接字才是真正用于和客户端通信的套接字。 客户端首先创建套接字然后在发起链接请求的时候会帮我们绑定connect这个接口是用来建立链接的而我们只是向服务器发起建立链接的请求中间三次握手的过程是由双方的操作系统自动来完成的。因为connect是链接建立的发起方所以我们用accept获取链接的时候一定是链接已经成功建立了我们才能获取。 服务器初始化 : 调用 socket, 创建文件描述符 ; 调用 bind, 将当前的文件描述符和 ip/port 绑定在一起 ; 如果这个端口已经被其他进程占用了 , 就会 bind 失败; 调用 listen, 声明当前这个文件描述符作为一个服务器的文件描述符 , 为后面的 accept 做好准备 ; 调用 accecpt, 并阻塞 , 等待客户端连接过来 建立连接的过程 : 调用 socket, 创建文件描述符 ; 调用 connect, 向服务器发起连接请求 ; connect 会发出 SYN 段并阻塞等待服务器应答 ; ( 第一次 ) 服务器收到客户端的 SYN, 会应答一个 SYN-ACK 段表示 同意建立连接 ; ( 第二次 ) 客户端收到 SYN-ACK 后会从 connect() 返回 , 同时应答一个 ACK 段 ; ( 第三次 ) 注意三次握手的过程我们会在讲tcp原理的时候详细的讲解现在只是一些流程和概念。 数据传输的过程 建立连接后 ,TCP 协议提供全双工的通信服务 ; 所谓全双工的意思是 , 在同一条连接中 , 同一时刻 , 通信双方可以同时写数据; 相对的概念叫做半双工 , 同一条连接在同一时刻 , 只能由一方来写数据 ; 服务器从 accept() 返回后立刻调用 read(), 读 socket 就像读管道一样 , 如果没有数据到达就阻塞等待 ; 这时客户端调用 write() 发送请求给服务器 , 服务器收到后从 read() 返回 , 对客户端的请求进行处理 , 在此期间客户端调用read() 阻塞等待服务器的应答 ; 服务器调用 write() 将处理结果发回给客户端 , 再次调用 read() 阻塞等待下一条请求 ; 客户端收到后从 read() 返回 , 发送下一条请求 , 如此循环下去 断开连接的过程: 如果客户端没有更多的请求了, 就调用close()关闭连接, 客户端会向服务器发送FIN段(第一次); 此时服务器收到 FIN 后 , 会回应一个 ACK, 同时 read 会返回 0 ( 第二次 ); read 返回之后 , 服务器就知道客户端关闭了连接 , 也调用 close 关闭连接 , 这个时候服务器会向客户端发送一个FIN; ( 第三次 ) 客户端收到 FIN, 再返回一个 ACK 给服务器 ; ( 第四次 ) 总结 1.建立链接是双方操作系统自动完成的三次握手。 2.建立链接是为了双方维护链接而创建的数据结构这个数据结构是有成本的这个成本主要体现在创建要花时间和空间。 3.断开链接四次挥手的目的是将曾经建立好的链接信息释放掉 一、协议定制与网络版计算器的实现 之前我们说过协议是一种约定。那么socket api的接口, 在读写数据时, 都是按 字符串 的方式来发送接收的. 如果我们要传输一些 结构化的数据 怎么办呢?结构化的数据我们可以理解为对象就像我们的qq发送消息的时候除了消息还有头像昵称时间这些数据肯定是不能分开发送给服务器的因为一旦用户基数很大的时候服务器根本无法区分哪个是哪个的消息。那么如何将这些数据打包成一个发送给服务器呢要想将多个字节流变成一个字节流这个过程就叫做序列化。当我们的服务器将这个数据收到后我们还需要将这个数据恢复为原来没有整合的样子此时恢复的过程就叫做反序列化。所以业务结构数据在发送到网络的时候先序列化再发送收到的一定是序列字节流要先进行反序列化然后才能使用这是实现网络协议的第一个细节。我们刚刚说数据发送到网络但实际上还有一个细节我们没有提到我们如何确定服务器会完整的收到我们发送的数据呢这就是我们实现协议的第二个细节。 网络版计算器的实现 例如 , 我们需要实现一个服务器版的计算器 . 我们需要客户端把要计算的两个加数发过去 , 然后由服务器进行计算 , 最后再把结果返回给客户端。 约定方案 客户端发送一个形如 11 的字符串 ; 这个字符串中有两个操作数 , 都是整形 ; 两个数字之间会有一个字符是运算符 数字和运算符之间有间隔 首先我们创建所需要的文件 之前的Tcp客户端和服务端我们直接拿过来用即可还有log.hpp等文件然后我们的Tcp服务端只保留多进程版本 现在我们要修改的是如何对数据做处理首先我们先把函数名改为handerEnter表示处理进入的逻辑我们将以前的任务文件改名为protocol也就是协议定制  准备工作做完后我们开始进行协议定制首先我们的协议中包含请求与响应请求就是客户让我们计算的内容响应就是我们给出的结果和返回码 pragma once #include iostreamclass Request { public:public:// x op yint x;int y;char op; };class Response { public:int exitcode; //返回码0表示计算成功非0表示计算失败int result; //计算结果 };  然后我们写个构造函数初始化一下  我们先默认初始化为0然后开始编写服务器中的回调函数hander: void handerEnter(int sock){// 1.读取// 1.1 如何保证读到的消息是一个完整的请求// 2.对请求Request 反序列化// 2.1 得到一个结构化的请求对象Request req // 3. 计算机处理req.x req.y req.op// 3.1 得到一个结构化的响应Response resp;// 4. 对响应Response进行序列化// 4.1 得到了一个字符串// 5.然后我们再发送响应}  我们有5个大步骤首先服务端收到客户端发来的请求就是让服务器计算某个表达式我们第一个要保证的是如何能完整的接收客户端发来的数据而要保证这一点可以有三个步骤1.数据定长2.特殊符号3.自描述这三点我们后面会讲到能保证第一步后我们再对这个请求做去掉报头然后做反序列化操作一旦进行反序列化操作我们就能拿到单个的x,y,op然后我们利用回调函数开始做计算这个函数我们在server.cc中实现直接传入start函数计算出来结果后我们还要发回给客户端所以还需要将结果进行序列化操作序列化后得到一个“x op y”的字符串我们将这个字符串发送给客户端即可。因为我们的第3步要得到一个响应所以我们直接搞一个回调函数   这个回调函数的第一个参数是一个输入型参数就是客户端发给我们的请求经过反序列化的对象第二个参数是一个输出型参数未来我们处理req的时候得到req.x,req.y,req.op然后用他们做计算将计算结果直接放到resp中。 void handerEnter(int sock, func_t func){// 1.读取// 1.1 如何保证读到的消息是一个完整的请求// 2.对请求Request 反序列化// 2.1 得到一个结构化的请求对象Request req // 3. 计算机处理req.x req.y req.op// 3.1 得到一个结构化的响应Response resp;func(req,resp); //req的处理结果全部放入了resp中// 4. 对响应Response进行序列化// 4.1 得到了一个字符串// 5.然后我们再发送响应}  有了这个回调函数那么我们在server.cc中就直接可以根据处理好的req去填充resp:  那么如何保证读到的消息是一个完整的请求呢因为tcp是面向字节流的所以我们可以明确报文和报文的边界明确报文和报文边界有三种方式1.定长 2.特殊符号 3.自描述如下图  tcp内部是有自己的发送缓冲区和接收缓冲区的所以我们只需要规定每个报文的大小的都是一样的这样就明确了报文和报文边界当然我们也可以在报文中加一个特殊的符号用这些符号去表示报文边界那么自描述是什么呢其实就是我们可以规定报文的前四个字节是什么什么后面是什么什么。我们今天所演示的自描述就是添加报头这个报头可以是符号也可以代表一些含义 有了以上概念我们就可以写序列化与反序列化函数了未来这两个函数一定是能让req和resp使用的所以我们先给req这个类实现序列化与反序列化函数 #define SEP #define SEP_LEN strlen(SEP)bool serialize(std::string *out){*out ;std::string x_string std::to_string(x);std::string y_string std::to_string(y);*out x_string;*out SEP;*out op;*out SEP;*out y_string;return true;} 首先我们的规定是序列化后的字符串是 x op y的形式所以我们将分隔符定义为空格然后定义空格符的长度这样以后更改会很方便。首先对out指针做初始化因为我们不能保证给我们传的字符串是空字符串然后我们将x和y转化为string类型让out字符串先x再加分隔符再加运算符再加分隔符最后再加y即可这样外部的out参数就得到了序列化的数据然后我们返回true。 bool deserialize(const std::string in){auto left in.find(SEP);auto right in.rfind(SEP);if (leftstd::string::npos || rightstd::string::npos){return false;}if (left right){return false;}if (right - (leftSEP_LEN) ! 1){return false;}std::string x_string in.substr(0,left);std::string y_string in.substr(rightSEP_LEN);if (x_string.empty()) return false;if (y_string.empty()) return false;x stoi(x_string);y stoi(y_string);op in[leftSEP_LEN];return true;} 反序列化也很简单我们首先找到字符串中左边的空格和右边的空格然后判断是否满足我们要求的格式不满足直接返回false。满足后我们就获取序列化字符串中的x,y,op得到后保存在类内的成员变量中然后返回true。 有了对请求的序列化和反序列化我们就可以计算了 bool cal(const Request req,Response resp) {//req已经有结构化完成的数据了可以直接使用resp.exitcode OK;resp.result OK;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.y0){resp.exitcode DIV_ZERO;}else {resp.result req.x / req.y;}}break;case %:{if (req.y0){resp.exitcode MOD_ZERO;}else {resp.result req.x % req.y;}}break;default:resp.exitcode OP_ERROR;break;}return true; }  计算也很简单首先将回应类中的返回码和结果初始化然后利用switch判断运算符注意switch语句中是可以在case语句后面加大括号的但是我们的break一定要写在大括号外面否则就犯了C语言的错误对于除0错误我们可以在协议头文件中设置一个错误码 enum {OK 0,DIV_ZERO,MOD_ZERO,OP_ERROR }; 上面我们只是完成了请求的序列化和反序列化我们还需要对结果的相应也做序列化与反序列化 //exitcode resultbool serialize(std::string* out){*out ;*out to_string(exitcode);*out SEP;*out to_string(result);return true;}bool deserialize(const std::string in){auto pos in.find(SEP);if (posstring::npos){return false;}exitcode stoi(in.substr(0,pos));result stoi(in.substr(posSEP_LEN));return true;} 因为我们规定序列化的结果必须是exitcode result的形式所以响应的序列化中只需要把返回码和空格和结果拼接起来即可。反序列化也很简单就是先找到空格然后通过空格分割出返回码和结果的字符串然后将字符串转化为int赋值给响应类的私有成员即可这样响应类中就保存了计算出的结果和返回码。 下面我们再写添加报头的函数由于这个函数响应和请求都需要所以我们直接写在协议头文件中 #define SEP #define SEP_LEN strlen(SEP)#define LINE_SEP \r\n #define LINE_SEP_LEN strlen(LINE_SEP) // 自定义报头x op y - content_len\r\nx op y\r\n // exitcode result - content_len(正文长度)\r\nexitcode result\r\n const string enLength(const std::string text) //添加报头 {string send_string to_string(text.size());send_stringLINE_SEP;send_stringtext;send_stringLINE_SEP;return send_string; } 我们规定一个完整的报文必须是x op y - content_len\r\nx op y\r\n这样的格式就是正文长度 \r\n(特殊符号正文 \r\n而这个函数给我们传的参数一定是正文x op y这样的字符串所以我们只需要计算正文长度然后将正文长度转化为字符串将正文长度加上特殊符号加上正文加上特殊符号这样就是我们规定的报文了然后返回即可。 有了添加报头我们还需要去掉报头的函数 bool deLength(const std::string package,string* text) {auto pos package.find(LINE_SEP);if (posstring::npos){return false;}int len stoi(package.substr(0,pos));*text package.substr(posLINE_SEP_LEN,len);return true; } package参数就是一个完整报文text是输出型参数就是我们将去掉报头的结果放在text中返回给外界。去掉报头也很简单先找到特殊符号然后我们获取正文的长度注意我们规定完整的报文开头到第一个\r\n就是报文长度。有了报文长度后直接用substr函数就拿到了正文。拿到后返回true即可。 下面我们完善hander函数 void handerEnter(int sock, func_t func){while (true){// 1.读取// 1.1 如何保证读到的消息是一个完整的请求std::string req_str;//?????????cout处理前请求的完整报文\nreq_strendl;// 如果走到这里那么可以保证req_str得到的是一个完整的请求string ret;if (!deLength(req_str, ret)){return;}cout去掉报头的请求报文\nretendl;// 2.对请求Request 反序列化// 2.1 得到一个结构化的请求对象Request req;if (!req.deserialize(ret)){// 反序列化失败就returnreturn;}// 3. 计算机处理req.x req.y req.op// 3.1 得到一个结构化的响应Response resp;func(req, resp);// 4. 对响应Response进行序列化// 4.1 得到了一个字符串std::string resp_str;resp.serialize(resp_str);cout进行序列化后的响应的字符串\nresp_strendl;// 5.然后我们再发送响应// 5.1构建成为一个完整的报文string send_string enLength(resp_str);cout对序列化后的响应添加报头的字符串\nsend_stringendl;send(sock, send_string.c_str(), send_string.size(), 0);}} 因为我们是多次给客户端发送响应所以这里应该是个循环。然后我们除了没有保证读到的消息是一个完整的报文其他的阶段都搞定了。假设我们已经读到一个完整的报文了然后对请求做去掉报头操作去掉报头后再进行反序列化操作这样就拿到了x,y,op并且保存在req中然后我们创建一个响应对象调用回调函数将req中x,y,op的结果计算出来把结果和返回码填到resp中。然后我们对resp中的返回码和结果进行序列化操作序列化后添加报头就可以发送给客户端了下面我们编写保证得到的是完整报文的函数 bool recvPackage(int sock, string inbuffer, string *text) {//因为是从sock中读取数据放到text中所以为了保险先对text清空(*text).clear();char buffer[1024];while (true){ssize_t n recv(sock,buffer,sizeof(buffer)-1,0);if (n0){buffer[n] 0;inbufferbuffer;//分析处理得到一个完整的报文auto pos inbuffer.find(LINE_SEP);if (posstring::npos){//没找到分隔符一定不是完整的报文需要继续往inbuffer中读continue;}int text_len stoi(inbuffer.substr(0,pos));int connumber_len to_string(text_len).size();int total_len connumber_len text_len 2*LINE_SEP_LEN;if (total_len inbuffer.size()){continue;}//inbuffer.size()大于一个完整报文长度那么inbuffer中至少有一个完整的报文*text inbuffer.substr(0,total_len);inbuffer.erase(0,total_len);//读到一个完整的报文就退出break;}else{return false;}}return true; } 首先我们要从文件描述符中读到完整的报文放在text中所以我们保险起见先将text清空。inbuffer参数是一个支持我们持续存放报文的字符串因为我们读取是有可能读不到一个完整的报文的所以这些暂时用不到的报文都会放在inbuffer中。所以我们保险起见先将text清空。然后我们定义一个 缓冲区这个缓冲区会存放我们从文件描述符中读出来的数据这里我们用recv接口读取返回值与read一样都是读到的字节数。如果n0说明客户端退出这个时候我们返回false即可。如果n大于0说明读取成功然后我们在缓冲区最后一个位置加上\0然后把数据加到inbuffer中注意是因为会持续的读取。然后我们在inbuffer中查找特殊字符\r\n如果没找到那么一定不会有完整的报文所以我们continue继续读取读取到特殊符号后我们首先保存正文长度和正文数字的长度比如 4\r\nhoow\r\n,首先正文的长度是4前面这个4的长度是11就是正文数字的长度。有了这两个长度我们就能计算一个完整报文的长度完整报文  正文数组长度 2*特殊字符长度  正文长度。然后我们判断inbuffer中的长度是否大于完整报文的长度如果不大于则说明inbuffer没有完整报文需要继续读取如果有完整报文我们就用substr函数拿到完整报文并且将inbuffer中刚刚拿掉的报文删除这样就不会影响下一个报文。注意我们读到一个报文就退出处理。 然后我们的hander方法就全部搞定了注意在hander中定义一个inbuffer用来存放循环读取到的客户端报文。 服务端我们搞定后现在去搞定客户端让客户端发送请求 void start(){struct sockaddr_in server;bzero(server,sizeof(server));server.sin_family AF_INET;server.sin_port htons(_serverport);server.sin_addr.s_addr inet_addr(_serverip.c_str());//connet的时候操作系统会帮客户端bind 返回值等于0成功if (connect(_sock,(struct sockaddr*)server,sizeof(server))!0){cerrsocket connect errorendl;}else {string msg;string inbuffer;while (true){coutmycal ;getline(cin,msg);//将字符串中转换为一个请求}}} 我们客户端肯定是输入要计算的字符串那么我们如何将字符串转化为一个响应呢其实很简单我们规定只是11 或者 10/12这样的格式 Request ParseLine(const string line){//规定表达式 11 12/0int status 0; //status是一个标志0表示操作符之前1表示操作符2表示操作符之后int i 0;int cnt line.size();string left,right;char op;while (icnt){switch (status){case 0:{if (!isdigit(line[i])){op line[i];status 1;}else {left.push_back(line[i]);}}break;case 1:i;status 2;break;case 2:right.push_back(line[i]);break;}}return Request(stoi(left),stoi(right),op);} 我们实现的原理很简单首先大循环是遍历字符串在遍历的过程中我们设置一个标志标志为0代表这个字符属于左操作数标志为1代表属于运算符标志为2代表属于右操作数。然后我们通过switch语句去判断最后拿到左操作数和右操作数和运算符然后我们要想构造请求对象还必须给request类多加一个构造函数 同样如果响应类也需要的话我们也加一个。 有了构造函数我们直接匿名对象返回即可这样我们就将一个字符串转换为请求对象了。 void start(){struct sockaddr_in server;bzero(server,sizeof(server));server.sin_family AF_INET;server.sin_port htons(_serverport);server.sin_addr.s_addr inet_addr(_serverip.c_str());//connet的时候操作系统会帮客户端bind 返回值等于0成功if (connect(_sock,(struct sockaddr*)server,sizeof(server))!0){cerrsocket connect errorendl;}else {string msg;string inbuffer;while (true){coutmycal ;getline(cin,msg);//先有结构化的数据然后序列化后发送给服务端Request req ParseLine(msg);//得到序列化的结果string req_s;req.serialize(req_s);//给序列化的结果添加报头string send_string enLength(req_s);send(_sock,send_string.c_str(),send_string.size(),0);//读取服务器返回的序列化结果string total_text;if (!recvPackage(_sock,inbuffer,total_text)){continue;}//走到这total_text中一定是一个完整的报文Response resp;string text;if (!deLength(total_text,text)){continue;}//走到这text中一定是一个去掉报头的报文resp.deserialize(text);coutexitCode: resp.exitcodeendl;coutresult: resp.resultendl;}}} 然后我们从刚刚那一步开始有了请求对象后我们需要对这个请求做序列化操作序列化后添加报头然后发送给服务器。同时我们还要接受服务器发给我们的相应所以我们继续使用recvPackage函数读取一个完整的报文如果我们读取失败那么就继续读取直到读取成功我们将这个报文去除报头然后进行反序列化操作这样resp中就保存了返回码和结果然后我们将结果打印出来下面我们运行起来演示一下 可以看到我们的网页版计算器是没问题的对于除0错误我们只关心错误码。这样我们就完成了网页版计算器。 二、json的使用 对于上面我们自己写的序列化和反序列化操作实际上在日常生活中我们是不用的一般都是用现成的下面我们讲一下最简单的json c版本的使用 首先linux安装json: yum install -y jsoncpp-devel 注意没有权限的用sudo提权。 class Request { public:Request():x(0),y(0),op(0){}Request(int _x,int _y,char _op):x(_x),y(_y),op(_op){}bool serialize(std::string *out){Json::Value root;root[first] x;root[second] y;root[oper] op;Json::FastWriter writer;*out writer.write(root);return true;}bool deserialize(const std::string in){Json::Value root;Json::Reader read;read.parse(in,root);x root[first].asInt();y root[second].asInt();op root[oper].asInt();return true;} public:// x op yint x;int y;char op; };json的使用非常简单首先定义一个万能的Value然后因为是做序列化操作所以需要将我们的x,y,op和root中进行一个映射json使用的是键值对方式所以我们就让x和first做一个映射y和second做一个映射op同理做好映射我们定义一个Fastwriter对象注意writer就是json中用来序列化的然后writer中的write函数可以直接将root进行序列化并且返回的是一个字符串。 反序列化同样定义万能的Value然后json中reader是进行反序列化的我们利用read中的parse函数这个函数第一个参数是从哪个流进行反序列化第二个参数就是我们的root。然后我们就可以将root中的x,y,op都拿出来因为root中是字符串类型而我们要的是整形所以用asInt转化为整形op并不会受影响因为char的本质是ascll值。 response的序列化和反序列化同理大家可以试试 class Response { public:Response():exitcode(0),result(0){}Response(int exitcode_,int result_):exitcode(exitcode_),result(result_){}bool serialize(std::string* out){Json::Value root;root[exitcode] exitcode;root[result] result;Json::FastWriter writer;*out writer.write(root);return true;}bool deserialize(const std::string in){Json::Value root;Json::Reader read;read.parse(in,root);exitcode root[exitcode].asInt();result root[result].asInt();return true;} public:int exitcode; //返回码0表示计算成功非0表示计算失败int result; //计算结果 }; 下面我们运行起来看看json的效果 可以看到换上json我们的代码也是没有问题的我们在json和自己写的直接可以用条件编译这样的话就可以想用哪个用哪个 然后我们的makefile也可以修改一下对于条件编译我们可以直接编译的时候添加MYSELF,这样就会用我们自己写的序列化和反序列化代码。 红框的部分效果是一样的只不过用LD变量会方便切换json和我们自己写的序列化和反序列化代码。 以上就是网页版计算器的全部内容了。 总结 自定义协议就是定义一个结构化的对象有了结构化的对象未来客户端和服务端就可以来回进行发送和接收约定在上面的代码中体现在规定必须是x op y这样的格式这就是约定。注意没有规定我们网络通信的时候只能有一种协议实际上我们完全可以将上述代码中规定前面是正文长度替换为协议编码通过协议编码就可以选择要执行哪种协议了。
http://www.zqtcl.cn/news/151308/

相关文章:

  • 天津建站平台网页制作免费的素材网站
  • 建设网站需要专业哪个企业提供电子商务网站建设外包
  • 公司网站建设及维护网站建设思维
  • 那个网站可以学做西餐17做网站广州沙河
  • 品牌网站建设哪里好京东网站建设案例
  • 亚马逊海外版网站深圳市工商注册信息查询网站
  • 新乐做网站优化网站上漂亮的甘特图是怎么做的
  • 新网站应该怎么做seo品牌推广方案思维导图
  • 想要网站导航推广页浅谈中兴电子商务网站建设
  • 免费引流在线推广成都网站优化费用
  • 老河口市网站佛山市点精网络科技有限公司
  • word模板免费网站seo引擎优化是做什么的
  • 办网站怎么赚钱鄠邑建站 网站建设
  • 宜春网站建设推广微信小程序开发
  • 巴南城乡建设网站免费网站建设软件大全
  • 湖南网站建设公公司没有自己的网站
  • 刚建设的网站如何推广网站恢复正常
  • 怎么做制作网站的教程永久免费空间免备案
  • 网站维护运营怎么做简单的手机网址大全
  • 网站建设规划设计公司排名使用模块化的网站
  • 南宁网站seo大概多少钱门户网站建设公司渠道
  • 如何建国际商城网站海门做网站公司
  • 做网站应该画什么图注册子公司流程及所需资料
  • 嵊州市建设银行网站怎么自己做游戏软件
  • 用模板快速建站中园建设银行网站
  • 网站建设罒金手指下拉壹陆韩国最新新闻消息
  • 东莞企业网站推广技巧wordpress怎么汉化
  • 17网站一起做网店如何下单iis服务器网站301重定向怎么做
  • 网站如何做线上支付功能seo网站推广优化费用
  • 贵州灵溪seo整站优化wordpress进行不