中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

新蒲建設(shè)集團網(wǎng)站數(shù)據(jù)交換平臺

新蒲建設(shè)集團網(wǎng)站,數(shù)據(jù)交換平臺,wordpress 100w 數(shù)據(jù),基于html5的購物商城網(wǎng)站文章目錄: 什么是協(xié)議結(jié)構(gòu)化的數(shù)據(jù)傳輸序列化和反序列化網(wǎng)絡(luò)版本計算器 什么是協(xié)議 在計算機網(wǎng)絡(luò)中,協(xié)議是指在網(wǎng)絡(luò)中進行通信和數(shù)據(jù)交換時,雙方遵循的規(guī)則和約定集合。它定義了數(shù)據(jù)的傳輸格式、順序、錯誤處理、認(rèn)證和安全性等方面的規(guī)范。 …

文章目錄:

  • 什么是協(xié)議
  • 結(jié)構(gòu)化的數(shù)據(jù)傳輸
  • 序列化和反序列化
  • 網(wǎng)絡(luò)版本計算器

什么是協(xié)議

在計算機網(wǎng)絡(luò)中,協(xié)議是指在網(wǎng)絡(luò)中進行通信和數(shù)據(jù)交換時,雙方遵循的規(guī)則和約定集合。它定義了數(shù)據(jù)的傳輸格式、順序、錯誤處理、認(rèn)證和安全性等方面的規(guī)范。

協(xié)議的設(shè)計和實現(xiàn)是計算機網(wǎng)絡(luò)能夠正常運行的基礎(chǔ)。它確保了不同設(shè)備和系統(tǒng)之間能夠相互理解和協(xié)作。協(xié)議定義了數(shù)據(jù)的結(jié)構(gòu)和編碼方式,規(guī)定了數(shù)據(jù)傳輸?shù)姆绞胶晚樞?#xff0c;以及雙方之間進行通信的交互規(guī)則。協(xié)議的標(biāo)準(zhǔn)化和普遍采用,使得計算機網(wǎng)絡(luò)得以互聯(lián)互通,并支持各應(yīng)用和服務(wù)的實現(xiàn)。

為了滿足不同的應(yīng)用場景和需求,已經(jīng)存在許多成熟的應(yīng)用層協(xié)議。這些協(xié)議定義了在特定應(yīng)用中進行通信和數(shù)據(jù)交換的規(guī)則和格式。(http、https、DNS、ftp、smtp…)。

結(jié)構(gòu)化的數(shù)據(jù)傳輸

協(xié)議是一種 “約定”,socket api的接口, 在讀寫數(shù)據(jù)時, 都是按 “字符串” 的方式來發(fā)送接收的. 如果我們要傳輸一些 “結(jié)構(gòu)化的數(shù)據(jù)” 怎么辦呢?

例如,我們需要實現(xiàn)一個網(wǎng)絡(luò)版本的簡易計算器。此時,客戶端給服務(wù)端發(fā)送的數(shù)據(jù)中,包含一個左操作數(shù)、操作符、右操作數(shù)。然后由服務(wù)端接收處理之后再將結(jié)果發(fā)送給客戶端。

此時,就遇到了一個問題,客戶端發(fā)送給服務(wù)端的不是一個簡單的字符串,而是一組結(jié)構(gòu)化的數(shù)據(jù)。如果客戶端將這些結(jié)構(gòu)化的數(shù)據(jù)單獨的通過網(wǎng)絡(luò)發(fā)送給服務(wù)端,那么服務(wù)端很難將收到的數(shù)據(jù)進行排列形成正確的數(shù)據(jù)。因此,客戶端最好將這些數(shù)據(jù)一次性進行發(fā)出,此時服務(wù)端獲取到的就是一個完整的數(shù)據(jù)請求,客戶端常見的方案有以下兩種。

約定方案一:將結(jié)構(gòu)化的數(shù)據(jù)組合成為一個字符串

  • 客戶端發(fā)送一個形如 “5+7” 的字符串;
  • 這個字符串中有兩個操作數(shù),都是整形;
  • 兩個數(shù)字之間會有一個字符是運算符;
  • 數(shù)字和運算符之間沒有空格;

這種將結(jié)構(gòu)化的數(shù)據(jù)組合成一個字符串的方式可以是一個簡單的方法,適用于簡單的數(shù)據(jù)結(jié)構(gòu)和通信需求。通過約定字符串的格式和規(guī)則,可以將結(jié)構(gòu)化數(shù)據(jù)轉(zhuǎn)換為字符串進行傳輸。

約定方案二:將結(jié)構(gòu)數(shù)據(jù)序列化和反序列化

  • 定義結(jié)構(gòu)體來表示我們需要交互的信息;
  • 發(fā)送數(shù)據(jù)時將這個結(jié)構(gòu)體按照一個規(guī)則轉(zhuǎn)換成字符串,接收到數(shù)據(jù)的時候再按照相同的規(guī)則把字符串轉(zhuǎn)化回結(jié)構(gòu)體;
  • 這個過程叫做 “序列化” 和 “反序列化”。

通過序列化和反序列化可以更靈活地處理結(jié)構(gòu)化數(shù)據(jù),并支持復(fù)雜的數(shù)據(jù)類型和結(jié)構(gòu)。通過定義一個結(jié)構(gòu)體或?qū)ο髞肀硎拘枰换サ男畔?#xff0c;可以使用序列化算法將結(jié)構(gòu)體轉(zhuǎn)換為字符串形式進行傳輸。接收端則可以使用相同的序列化算法進行反序列化,將字符串轉(zhuǎn)化為原始的數(shù)據(jù)結(jié)構(gòu)。

序列化和反序列化

序列化和反序列化是將數(shù)據(jù)在不同表示形式之間進行轉(zhuǎn)化的過程。

  • 序列化是指將數(shù)據(jù)從內(nèi)存中的對象或數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)化為可存儲或傳輸?shù)母袷?#xff0c;例如字符串、字節(jié)流或二進制數(shù)據(jù)。
  • 反序列化是將序列化之后的數(shù)據(jù)重新轉(zhuǎn)化為內(nèi)存中的對象或數(shù)據(jù)結(jié)構(gòu)。

在這里插入圖片描述

網(wǎng)絡(luò)版本計算器

接下來我們使用自己制定的協(xié)議來寫一個網(wǎng)絡(luò)版本的計算器。

首先,將后續(xù)代碼需要的日志文件引入,日志 log.hpp 代碼如下:

#pragma once
#include <stdio.h>
#include <cstdarg>
#include <ctime>
#include <cassert>
#include <stdlib.h>
#include <cstring>
#include <cerrno>#define DEBUG 0
#define NOTICE 1
#define WARINING 2
#define FATAL 3const char *log_level[] = {"DEBUG", "NOTICE", "WARINING", "FATAL"};void logMessage(int level,const char *format, ...)
{assert(level >= DEBUG);assert(level <= FATAL);char *name = getenv("USER");char logInfo[1024];va_list ap; // ap -> char*va_start(ap, format);vsnprintf(logInfo, sizeof(logInfo) - 1, format, ap);FILE *out = (level == FATAL) ? stderr : stdout;fprintf(out, "%s | %u | %s | %s\n",log_level[level],(unsigned int)time(nullptr),name == nullptr ? "unknow" : name,logInfo);va_end(ap); // ap = NULL
}

協(xié)議定制

要實現(xiàn)一個網(wǎng)絡(luò)版本的計算器,就必須確保通信雙方能夠遵守某種協(xié)議約定。這里我們制定一套簡單的協(xié)議約定。數(shù)據(jù)分為請求數(shù)據(jù)和響應(yīng)數(shù)據(jù)。因此我們可以定義請求類和響應(yīng)類來實現(xiàn)協(xié)議約定。

// 請求類
class Request {
public:int x_;int y_;char op_;
};// 響應(yīng)類
class Response {
public:int exitCode_; // 狀態(tài)字段int result_;   // 計算結(jié)果
};

定義了一個名為 Request 的請求類和一個名為 Response 的響應(yīng)類。這些類具有公共的成員變量來存儲請求和響應(yīng)的數(shù)據(jù)。

請求類中包括兩個操作數(shù)和一個操作符,響應(yīng)類中包含一個計算結(jié)果以及該次計算的狀態(tài)字段。狀態(tài)字段用于表示該次的計算是否符合計算要求。

約定狀態(tài)字段對應(yīng)的含義如下:

  • 0 - 表示計算成功;
  • -1 - 表示除0錯誤;
  • -2 - 表示模0錯誤;
  • -3 - 表示非法操作符;

注意,計算結(jié)果只有在狀態(tài)碼為0的時候才有意義,否則計算的結(jié)果是沒有意義的。

序列化和反序列化

下面代碼實現(xiàn)了一個簡單的請求類和響應(yīng)類,并提供了序列化和反序列化的方法來將結(jié)構(gòu)化的數(shù)據(jù)轉(zhuǎn)換為字符串,并將字符串轉(zhuǎn)換會結(jié)構(gòu)化的數(shù)據(jù)。根據(jù)定義的宏 MY_SELF 的是否定義,可以使用自定義的序列化方法或者使用 JSON 庫進行序列化。

#pragma once
#include <iostream>
#include <string>
#include <cassert>
#include <jsoncpp/json/json.h>
#include "util.hpp"#define SPACE " "
#define SPACE_LEN strlen(SPACE)
#define CRLF "\r\n"
#define CRLF_LEN strlen(CRLF) // 坑:sizeof(CRLF)
#define OPS "+-*/%"
#define BUFFER_SIZE 1024
// #define MY_SELF 1// encode,整個序列化之后的字符串進行添加長度
std::string encode(const std::string &in, uint32_t len)
{std::string encodein = std::to_string(len);encodein += CRLF;encodein += in;encodein += CRLF;return encodein;
}// decode,這個序列化之后的字符串進行提取長度
// 1.必須具有完整的長度   2.必須具有和len相符合的有效載荷
// 3.具備上述兩個長度才返回有效載荷和len,否則,就是一個檢測函數(shù)
std::string decode(std::string &in, uint32_t *len)
{assert(len);// 1.確認(rèn)是否是一個包含len的有效字符串*len = 0;std::size_t pos = in.find(CRLF);if (pos == std::string::npos)return "";// 2.提取長度std::string Len = in.substr(0, pos);int intLen = atoi(Len.c_str());// 3.確認(rèn)有效載荷也是符合要求的int surplus = in.size() - 2 * CRLF_LEN - pos;if (surplus < intLen)return "";// 4.確認(rèn)有完整的報文結(jié)構(gòu)std::string package = in.substr(pos + CRLF_LEN, intLen);*len = intLen;// 5.將當(dāng)前的報文完整的從in中移除掉int removeLen = Len.size() + package.size() + 2 * CRLF_LEN;in.erase(0, removeLen);// 6.正常返回return package;
}class Request
{
public:Request() {}~Request() {}// 序列化 - 結(jié)構(gòu)化的數(shù)據(jù) -> 字符串void serialize(std::string *out){
#ifdef MY_SELFstd::string xStr = std::to_string(x_);std::string yStr = std::to_string(y_);*out = xStr;*out += SPACE;*out += op_;*out += SPACE;*out += yStr;
#else// 1.Value對象,json基于KV,json是有兩套操作方法的Json::Value root;root["x"] = x_;root["y"] = y_;root["op"] = op_;Json::FastWriter fw;*out = fw.write(root);
#endif}// 反序列化 - 字符串 -> 結(jié)構(gòu)化的數(shù)據(jù)bool deserialize(std::string &in){
#ifdef MY_SELF// 100 + 200std::size_t spaceOne = in.find(SPACE);if (std::string::npos == spaceOne)return false;std::size_t spaceTwo = in.rfind(SPACE);if (std::string::npos == spaceTwo)return false;std::string dataOne = in.substr(0, spaceOne);std::string dataTwo = in.substr(spaceTwo + SPACE_LEN);std::string oper = in.substr(spaceOne + SPACE_LEN, spaceTwo - (spaceOne + SPACE_LEN));if (oper.size() != 1)return false;x_ = atoi(dataOne.c_str());y_ = atoi(dataTwo.c_str());op_ = oper[0];return true;
#elseJson::Value root;Json::Reader rd;rd.parse(in, root);x_ = root["x"].asInt();y_ = root["y"].asInt();op_ = root["op"].asInt();return true;
#endif}void debug(){std::cout << "debug---------------------" << std::endl;std::cout << x_ << " " << op_ << " " << y_ << std::endl;std::cout << "debug---------------------" << std::endl;}public:int x_;int y_;char op_;
};class Response
{
public:Response() {}~Response() {}// 序列化void serialize(std::string *out){
#ifdef MY_SELF// "exitCode_ result_"std::string ec = std::to_string(exitCode_);std::string res = std::to_string(result_);*out = ec;*out += SPACE;*out += res;
#elseJson::Value root;root["exitcode"] = exitCode_;root["result"] = result_;Json::FastWriter fw;*out = fw.write(root);
#endif}// 反序列化 - 不僅僅是在網(wǎng)路中應(yīng)用,本地也是可以直接使用的bool deserialize(std::string &in){
#ifdef MY_SELF// "0 300"std::size_t pos = in.find(SPACE);if (std::string::npos == pos)return false;std::string codeStr = in.substr(0, pos);std::string restStr = in.substr(pos + SPACE_LEN);exitCode_ = atoi(codeStr.c_str());result_ = atoi(restStr.c_str());return true;
#elseJson::Value root;Json::Reader rd;rd.parse(in, root);exitCode_ = root["exitcode"].asInt();result_ = root["result"].asInt();return true;
#endif}void debug(){std::cout << "debug---------------------" << std::endl;std::cout << "exitCode = " << exitCode_ << " result = " << result_ << std::endl;std::cout << "debug---------------------" << std::endl;}public:// 退出狀態(tài),0表示運算結(jié)果合法,非0表示表示運行結(jié)果是非法的,!0是幾就表示是因為什么原因錯了。int exitCode_;int result_;
};bool makeRequest(std::string &str, Request *req)
{// 1+1char strcmp[BUFFER_SIZE];snprintf(strcmp, sizeof strcmp, "%s", str.c_str());char *left = strtok(strcmp, OPS);if (!left)return false;char *right = strtok(nullptr, OPS);if (!right)return false;char mid = str[strlen(left)];req->x_ = atoi(left);req->y_ = atoi(right);req->op_ = mid;return true;
}
  • debug 方法用于調(diào)試,打印對象的成員變量值。
  • makeRequese 函數(shù)用于解析字符串并生成請求對象。
  • 此外,代碼還定義了一些變量和輔助函數(shù)來處理字符串的拼接和分割。
  • 若要使用第三方庫 jsoncpp 來處理 JSON 數(shù)據(jù)的序列化和反序列化。需要確保項目中包含了這個庫,并在編譯時鏈接到正確的庫文件。

服務(wù)端代碼

TCP 服務(wù)器我們使用多線程版本的,當(dāng)服務(wù)器初始化完成并啟動之后。當(dāng)有一個客戶端來請求服務(wù)器時,服務(wù)器就為其創(chuàng)建一個新的線程用于服務(wù)該客戶端。這里,我們?yōu)榭蛻舳颂峁┑氖呛唵蔚挠嬎闫鞴δ?#xff0c;服務(wù)端完成客戶端給出的計算并給客戶端返回結(jié)果。

class ServerTcp; // 聲明一下static Response calculator(const Request &req)
{Response resp;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.y_ == 0)resp.exitCode_ = -1; // -1,除0elseresp.result_ = req.x_ / req.y_;}break;case '%':{if (req.y_ == 0)resp.exitCode_ = -2; // -2,模0elseresp.result_ = req.x_ % req.y_;}break;default:resp.exitCode_ = -3; // -3,非法操作符break;}return resp;
}void netCalc(int sock, const std::string &clientIp, uint16_t clientPort)
{assert(sock >= 0);assert(!clientIp.empty());assert(clientPort >= 1024);// 9\r\n100 + 200\r\n    9\r\n100 + 200\r\nstd::string inbuffer;while (true){// 定義一個請求對象Request req;char buffer[128];ssize_t s = read(sock, buffer, sizeof(buffer) - 1);if (s == 0){logMessage(NOTICE, "client[%s:%d] close sock,service done.", clientIp.c_str(), clientPort);break;}else if (s < 0){logMessage(WARINING, "read client[%s:%d] error,errorCode: %d,errorMessage: %s", clientIp.c_str(), clientPort, errno, strerror(errno));break;}// read successbuffer[s] = 0;inbuffer += buffer;// 1.檢查inbuffer是否已經(jīng)具有了一個strPackageuint32_t packgeLen = 0;std::string package = decode(inbuffer, &packgeLen);if (packgeLen == 0)continue; // 無法提取一個完整的報文,繼續(xù)提取// 2.已經(jīng)獲取了一個完整的packageif (req.deserialize(package)){// 3.處理邏輯,輸入的是一個req,得到一個respResponse resp = calculator(req); // resp是一個結(jié)構(gòu)化的數(shù)據(jù)// 4.對resp進行序列化std::string respPackage;resp.serialize(&respPackage);// 5.對報文進行encoderespPackage = encode(respPackage, respPackage.size());// 6.簡單進行發(fā)送write(sock, respPackage.c_str(), respPackage.size());}}
}class ThreadData
{
public:uint16_t clientPort_;std::string clientIp_;int sock_;ServerTcp *this_;ThreadData(uint16_t port, std::string ip, int sock, ServerTcp *ts): clientPort_(port), clientIp_(ip), sock_(sock), this_(ts) {}
};class ServerTcp
{
public:ServerTcp(uint16_t port, const std::string &ip = "") : port_(port), ip_(ip), listenSock_(-1) {}~ServerTcp() {}public:void init(){// 1. 創(chuàng)建socketlistenSock_ = socket(PF_INET, SOCK_STREAM, 0);if (listenSock_ < 0){logMessage(FATAL, "socket:%s", strerror(errno));exit(SOCKET_ERR);}logMessage(DEBUG, "socket:%s,%d", strerror(errno), listenSock_);// 2. bind// 2.1 填充服務(wù)器信息struct sockaddr_in local; // 用戶棧memset(&local, 0, sizeof local);local.sin_family = PF_INET;local.sin_port = htons(port_);ip_.empty() ? (local.sin_addr.s_addr = INADDR_ANY) : (inet_aton(ip_.c_str(), &local.sin_addr));// 2.2 本地socket信息,寫入sock_對應(yīng)的內(nèi)核區(qū)域if (bind(listenSock_, (const struct sockaddr *)&local, sizeof local) < 0){logMessage(FATAL, "bind:%s", strerror(errno));exit(BIND_ERR);}logMessage(DEBUG, "bind:%s,%d", strerror(errno), listenSock_);// 3. 監(jiān)聽socket,為何要監(jiān)聽呢?tcp是面向連接的!if (listen(listenSock_, 5) < 0){logMessage(FATAL, "bind:%s", strerror(errno));exit(LISTEN_ERR);}logMessage(DEBUG, "listen:%s,%d", strerror(errno), listenSock_);}void loop(){while (true){struct sockaddr_in peer;socklen_t len = sizeof(peer);// 4. 獲取連接,accept的返回值是一個新的socket fdint serviceSock = accept(listenSock_, (struct sockaddr *)&peer, &len);if (serviceSock < 0){// 獲取連接失敗,繼續(xù)獲取logMessage(WARINING, "accept:%s[%d]", strerror(errno), serviceSock);continue;}// 4.1 獲取客戶端基本信息uint16_t peerPort = ntohs(peer.sin_port);std::string peerIp = inet_ntoa(peer.sin_addr);logMessage(DEBUG, "accept:%s | %s[%d], socket fd:%d", strerror(errno), peerIp.c_str(), peerPort, serviceSock);// 5. 提供服務(wù),echho -> 小寫 -> 大寫// v2版本 - 多線程// 多線程不需要關(guān)閉文件描述符,因為多線程會共享文件描述符表!ThreadData *td = new ThreadData(peerPort, peerIp, serviceSock, this);pthread_t tid;pthread_create(&tid, nullptr, threadRoutine, (void *)td);}}static void *threadRoutine(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData *>(args);netCalc(td->sock_, td->clientIp_, td->clientPort_);delete td;}
private:int listenSock_;uint16_t port_;std::string ip_;
};

代碼解釋:

  • 定義了一個 calculator 函數(shù),用于根據(jù)請求對象進行計算并返回響應(yīng)對象。根據(jù)請求的操作符,執(zhí)行相應(yīng)的計算,并將結(jié)果儲存在響應(yīng)對象中。
  • netCalc 函數(shù)用于吃力與客戶端的網(wǎng)路通信。在循環(huán)中,讀取客戶端發(fā)送的數(shù)據(jù),并解析出完整的請求報文。然后,調(diào)用 calculator 函數(shù)進行計算,并將計算結(jié)果序列化為響應(yīng)報文發(fā)送給客戶端。
  • 其余的 TCP 服務(wù)器相關(guān)的代碼在 TCP網(wǎng)絡(luò)程序 中有詳細(xì)的解釋。

客戶端代碼

接下來實現(xiàn)一個簡單的客戶端程序,可以向服務(wù)端發(fā)送請求并接收響應(yīng)。其主要作用是與服務(wù)器進行通信,實現(xiàn)請求和響應(yīng)的交互。

客戶端代碼如下:

volatile bool quit = false;static void Usage(std::string proc)
{std::cerr << "Usage:\n\t" << proc << " prot ip" << std::endl;std::cerr << "Example:\n\t" << proc << " 127.0.0.1 8080\n"<< std::endl;
}int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(USAGE_ERR);}uint16_t serverPort = atoi(argv[2]);std::string serverIp = argv[1];// 1. 創(chuàng)建socket SOCK_STREAMint sock = socket(AF_INET, SOCK_STREAM, 0);if (sock < 0){std::cerr << "socket: " << strerror(errno) << std::endl;exit(SOCKET_ERR);}// 2. connect,向服務(wù)器發(fā)起連接請求// 2.1 先填充需要連接的遠端主機的基本信息struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverPort);inet_aton(serverIp.c_str(), &server.sin_addr);// 2.2 發(fā)送請求,connect 會自動幫我們進行bindif (connect(sock, (const struct sockaddr *)&server, sizeof(server)) != 0){std::cerr << "connect: " << strerror(errno) << std::endl;exit(CONN_ERR);}std::string message;while (!quit){message.clear();std::cout << "Place Enter# ";std::getline(std::cin, message);if (strcasecmp(message.c_str(), "quit") == 0){quit = true;continue;}// message=trimStr(message);Request req;if (!makeRequest(message, &req))continue;std::string package;req.serialize(&package);std::cout << "debug->serialize-> " << package << std::endl;package = encode(package, package.size());std::cout << "debug->encode-> \n" << package << std::endl;ssize_t s = write(sock, package.c_str(), package.size());if (s > 0){char buffer[1024];ssize_t s = read(sock, buffer, sizeof(buffer) - 1);if (s > 0)buffer[s] = 0;std::string echoPackage = buffer;Response resp;uint32_t len = 0;std::string tmp = decode(echoPackage, &len);if (len > 0){echoPackage = tmp;resp.deserialize(echoPackage);printf("[exitcode: %d] %d\n", resp.exitCode_, resp.result_);}}else if (s <= 0){break;}}return 0;
}

運行測試:

編寫 makefile 構(gòu)建程序,如下所示,需要定義 MY_SELF 時將 Method=-DMY_SELF 寫上,不需要定義時就不需要寫。

.PHONY:all
all:clientTcp serverTcpd
Method=-DMY_SELFclientTcp:clientTcp.ccg++ -o $@ $^ $(Method) -std=c++11 -ljsoncpp
serverTcpd:serverTcp.ccg++ -o $@ $^ $(Method) -std=c++11 -lpthread -ljsoncpp.PHONY:clean
clean:rm -f serverTcpd clientTcp

使用我們寫的序列化與反序列化代碼進行測試。

http://www.risenshineclean.com/news/7399.html

相關(guān)文章:

  • 如何做視頻網(wǎng)站技術(shù)指標(biāo)網(wǎng)絡(luò)營銷策劃的流程
  • 大安移動網(wǎng)站建設(shè)西安企業(yè)seo
  • 展示型手機網(wǎng)站模板seo就業(yè)
  • 湖南省建設(shè)廳網(wǎng)站首頁網(wǎng)絡(luò)顧問
  • 幫企業(yè)外賣網(wǎng)站做推移動網(wǎng)站優(yōu)化排名
  • 網(wǎng)站免費優(yōu)化工具怎樣宣傳網(wǎng)站
  • 在建設(shè)政府門戶網(wǎng)站時要充分考慮到引流推廣是什么意思
  • 廈門網(wǎng)紅鄭州seo技術(shù)
  • 網(wǎng)站建設(shè) 網(wǎng)站優(yōu)化5118數(shù)據(jù)分析平臺官網(wǎng)
  • 免費域名申請哪個網(wǎng)站好產(chǎn)品推廣方案ppt
  • 建站優(yōu)化收費石家莊抖音seo
  • 好看又免費的圖片素材網(wǎng)站成都網(wǎng)站推廣經(jīng)理
  • 電腦維修 做網(wǎng)站軟文網(wǎng)站名稱
  • 企業(yè)做網(wǎng)站需要什么資料關(guān)鍵詞優(yōu)化哪個好
  • 棗莊住房和城鄉(xiāng)建設(shè)局網(wǎng)站滄州網(wǎng)站優(yōu)化公司
  • 中山做網(wǎng)站博客推廣的方法與技巧
  • 深圳 公司網(wǎng)站建設(shè)優(yōu)化關(guān)鍵詞排名哪家好
  • 做網(wǎng)站一般什么配置超級外鏈自動發(fā)布工具
  • 品牌做網(wǎng)站搜索百度網(wǎng)址網(wǎng)頁
  • 申請做網(wǎng)站 論壇版主seo站長綜合查詢
  • 招財貓網(wǎng)站怎么做搜索熱詞排名
  • 做網(wǎng)站程序員都要先做維護么數(shù)字營銷是干啥的
  • 如何搭建免費網(wǎng)站營銷培訓(xùn)視頻課程免費
  • 建設(shè)網(wǎng)站要注意事項中國最大網(wǎng)站排名
  • 互聯(lián)網(wǎng)門戶網(wǎng)站是什么培訓(xùn)課程總結(jié)
  • 國外做網(wǎng)站公司能賺錢嗎知乎軟文推廣
  • wordpress程序代碼漯河seo推廣
  • 長安公司網(wǎng)站建設(shè)百度自媒體注冊入口
  • 肇慶網(wǎng)站建設(shè)方案外包惠州網(wǎng)站排名提升
  • 網(wǎng)站建設(shè)用什么軟件比較好優(yōu)化視頻