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

一个人如何注册公司优化网站加载速度

一个人如何注册公司,优化网站加载速度,医院做网站运维,泉州网站建设泉州文章目录 1. udp_echo_server是个什么东西?2. 初步文件构建.3. nocopy服务器防止被拷贝4. 创建套接字5. sockfd bind ip & port6. start 开始服务7. 编码客户端8. 服务端ip应该绑0号pi.9. 加入日志10. 封装ip port类11. reference codeUdpServer.hppUdpServerMain.ccUdpC…

文章目录

    • 1. udp_echo_server是个什么东西?
    • 2. 初步文件构建.
    • 3. `nocopy`服务器防止被拷贝
    • 4. 创建套接字
    • 5. sockfd bind ip & port
    • 6. start 开始服务
    • 7. 编码客户端
    • 8. 服务端ip应该绑0号pi.
    • 9. 加入日志
    • 10. 封装ip + port类
    • 11. reference code
      • UdpServer.hpp
      • UdpServerMain.cc
      • UdpClientMain.cc
      • nocopy.hpp
      • Log.hpp
      • LockGuard.hpp
      • InetAddr.hpp
      • makefile

好的, 今天我们开始写一个简单的网络代码. 整体代码难度不大, 不过我打算其中穿插着说一点基础知识, 整体可能就比较多了.

1. udp_echo_server是个什么东西?

  首先, 我们先来说一下udp_echo_server这个程序要做什么? 这个是我们开始学习网络的第一个编码实践, 所以说是很简单, 整体的功能是有两个端, 一端是客户端, 一端是服务端, 客户端每次向服务端发送数据, 服务端接受到后吧这个数据又发回客户端, 实现一个客户端数据回显的效果.
  然后这个程序使用的网络协议是UDP协议, 整体很简单.

2. 初步文件构建.

我们先来简单预测一下有哪些文件?
我打算:

  • UdpServer.hpp 服务端头文件
  • UdpServerMain.cc 服务端主函数
  • UdpClientMain.cc 客户端主函数
    在这里插入图片描述
      不过因为我们可能中间需要多次编译, 所以我们先写一个makefile脚本文件吧:
    在这里插入图片描述
      哎, 那有人可能会有疑问, 你这个用户端你咋不编译啊? 这是因为我们一上来先写服务端, 用户端等写完了我们再用make编译, 因为现在还没写嘛…

好的, 下面我们开始编码吧.

3. nocopy服务器防止被拷贝

  一般来说, 服务器程序是不会用到拷贝的, 因此为了防止其他人误写拷贝或者错误使用服务端的类, 我们写一个nocopy, 我们的服务端类可以继承这个类, 从而间接实现服务端不会被拷贝的一个效果.

  我花两分钟就编完了, 我把这个类的拷贝构造赋值运算符重载函数直接delete掉了:
在这里插入图片描述
那下面我们让UdpServer.hpp中的服务端继承一下:
在这里插入图片描述
好的, 这样的话我们的UdpServer类就不能拷贝了…

  之后, 我们来构思一下我们的服务端主函数要实现什么功能?
  作为一个服务器, 应该先构建出一个服务类对象, 然后这个类对象需要先初始化, 然后启动起来进行服务. 在这里插入图片描述
  好的, 我们围绕这个实现功能, 我们来写一写服务端.

4. 创建套接字

  一个程序要跨网络通信, 需要套接字, 即socket, 那我们来给我们的服务端写一个套接字. 套接字socket = ip + port, 实际上套接字就是一个整数, 一个文件描述符, 我们在网络编程的时候万物皆文件, 然后把这个文件和ip以及端口号绑定起来, 表示一个网络当中具有唯一性的进程, 这叫做套接字的作用.
在这里插入图片描述

  这样就完成了吗? 显然没有啊兄弟, 这样你只是在栈上定义出来了一个类型, 一个空间而已, 系统中没有真正的文件的兄弟, 想要有真正的文件, 我们需要调用接口socket. 来, 让我们看看对应的接口介绍:
在这里插入图片描述

int socket(int domain, int type, int protocol);

  • domain: 域, 用来指定该socket是网络通信还是本地通信.
    • AF_UNIX Local communication unix(7)
    • AF_INET IPv4 Internet protocols ip(7)
  • type: 套接字类型
    • SOCK_DGRAM Supports datagrams (connectionless, unreliable messages of a fixed maximum length).
  • protocol 协议类型
    • 因为我们前面指明了AF_INETSOCK_DGRAM, 因此可以写0为默认.
  • 返回值:
    • success 返回文件描述符
    • 失败, -1, 错误码被设置

好的, 所以我们写这样:
在这里插入图片描述
好的, 显然我们的exit是红色的, 所以我们把头文件给他包上.
在这里插入图片描述
  好的, 我们再来考虑一下如果socket创建失败了? 那我们是不是需要终止这个进程呀? 因为文件命名符都要不过来了, 后面肯定是没办法继续的嘛. 如果socket创建成功了, 那么咱们就打印一个std::cout << "DEBUG :socket create success, _sockfd: " << _sockfd << std::endl;用来当作debug信息. 我们预期这个sockfd是3, 为什么? 因为0是标准输入, 1是标准输出, 2是标准错误, 所以下一个系统肯定给咱们的程序分配一个3给到sockfd.
在这里插入图片描述

基于这个预测, 我们来测试一下.
在这里插入图片描述
  看来是符合我们预期的, 那我们继续? 我们创建好了一个sockfd文件描述符, 但是我们的套接字已经弄好了吗?
  显然没有啊兄弟, 现在只是创建了这么一个文件和文件描述符而已, 咱们现在需要把这个sockfd关联上ip地址和prot.
所以, 下一步是bind操作.

5. sockfd bind ip & port

接下来我们继续写我们的bind操作哈. 我们先来看一下bind接口所需要的参数是啥?
在这里插入图片描述


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

  • sockfd: 套接字描述符
  • sockaddr: 绑定的ip + port信息
  • addrlen: 绑定的ip + port结构体的大小.
  • 返回值是如果失败返回-1, 错误码被设置, 成功返回0.

  所以, 我们可以get到, 我们绑定ip + port, 就需要用一个struct sockaddr_in类型的结构体给他套进去.
  请注意, 这个地方用的是struct sockaddr_in而非struct sockaddr, 这是一个类似于多态的接口, struct sockaddr*是一个通用指针, 然后struct sockaddr_in是专门用来处理网络通信的ip & port等信息的结构体.

我们来认识一下struct sockaddr_in这个结构体:
在这里插入图片描述在这里插入图片描述

  不过好像有个小问题啊? 咋有个"不允许使用不完整的类型"的报错呢? 这是因为没有包头文件, 我们查找一下对应的头文件.
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

  上面这个地方需要额外注意, 就是我们在填充sockadd_in类型的时候, 我们这个类型中的数据将来是要发到网络当中的, 所以需要把主机字节序转换成网络字节序. 说白了就是大小端问题.
  其中, 对于ip地址, 需要把ip地址从字符串形式转换成4字节的形式, 然后再转成网络字节序, 对于port, 需要转网络字节序.
自己做这个工作很麻烦, 还需要判断大小端, 然后再进行转换, 我们有对应的库函数去做这个事情, 转ip: inet_addr()以及 转port: htons().
下面是两个接口的截图:
在这里插入图片描述
在这里插入图片描述
  但是代码中local.sin_family = AF_INET;是啥玩意呢? 是一个标志位, 用来标识struct sockaddr_in是将来用来网络通信的还是本地进程通信的. AF_INET标识是网络通信.

  那到这里是绑定成功了吗? 没有啊兄弟, 我们只是填充好了一个在用户栈上的一个变量而已, 下面开始绑定, 把sockfd 与 对应的ip和port绑定起来.
在这里插入图片描述
在这里插入图片描述
  咱们来解释一下为啥用ip -> "127.0.0.1"来进行测试, 这个是因为这个ip比较特殊, 是一个本地回环, 就是在本主机内部通讯的ip地址, 用这个ip如果通过了, 则证明我们的软件内部是没问题的, 再上线网络, 如果这个都过不了, 则问题一定在软件内部.
然后, 我们运行测试一下:
在这里插入图片描述
  看来没啥大问题哈, 我们看来是绑定成功了.
  所以呢, 我们创建套接字然后绑定完了ip和port, 这才算是为网络通信做了铺垫, 下面我们得开始写我们的服务逻辑了.

6. start 开始服务

  首先搞明白一个问题, 就是服务器是个啥玩意? 这个东西是一个24小时不断运行的死循环代码. 所以我们的进程一旦开始, 就不会主动退出.
所以我们的Start()逻辑应该是这样的:
在这里插入图片描述

  写到这里, 我们需要想我们的服务器要干嘛的? 接受数据包, 然后把这个数据包扔回客户端的!
  要接受数据包, 需要用接口 -> recvfrom(), 这个地方咋不是read()呢? 因为UDP协议是面向数据报的, 而非字节流的.
在这里插入图片描述
我们来简单介绍一下这个recvfrom()接口.

  • sockfd: sockfd.
  • buf: 缓冲区
  • len: 缓冲区大小
  • flags: 接受标识符, (0是阻塞接受)
  • src_addr: 输入输出参数, 用来保存接收到的数据报的客户端信息.
  • addrlen: 输入输出参数, 用来保存接收到的数据报的客户端信息的大小.

在这里插入图片描述
  我们拿到了数据呢? 之后我们是不是要在服务器端打印一下, 看看是不是真的拿到了, 然后再构造一个返回给客户端的数据? 如果n <= 0, 咱们直接std::cout << "recvfrom , error" << std::endl; // debug 信息.即可.
在这里插入图片描述
好的, 我们大体逻辑写好了吧? 然后把析构写一下: 要记得关掉文件描述符.
在这里插入图片描述
  我们现在可以测试吗? 不能啊兄弟, 没有客户端怎么测试呢? 我们先快速写一个客户端代码出来:

7. 编码客户端

我们的客户端也需要跨网络发数据吧? 所以, 也是第一步弄一个sockfd.
在这里插入图片描述
然后呢, 我们要发给服务器, 所以我们需要提前"内置"好服务器的套接字吧?
在这里插入图片描述
  这个地方需要注意, 我们的客户端是不需要绑定端口号的ip地址的, 为啥呢? 为啥服务端需要绑定端口号和ip地址, 而我们的客户端不需要绑定端口号和ip地址?
  兄弟, 你想想看啊, 服务器在公司手里, 服务端是运行在服务器上的, 一般一个主机上是只运行那个服务的, 所以端口号基本不会改变嘛, 但是客户端呢? 运行在用户的手机上吧? 用户开多少个应用你知道吗? 你不知道, 那有没有各种应用的端口号如果让程序员指定会不会冲突呢? 肯定会啊, 所以说客户端让用户的OS分配就好了, 程序员是不需要明确写的.
  那, 有人可能会有疑问, 那么服务器怎么找到用户的用户端呢? 首先, 是客户端先给服务器发消息, 第二就是服务器拿到消息之后, 就会拿到这次客户端的具体ip地址和port.

在这里插入图片描述

  所以, 我们的客户端应该不断发消息, 所以写一个循环嘛, 然后发成功了, 就阻塞等待接受消息, 如果收到了, 咱们就打印一遍(回显), 如果接受失败, 就break出去. 如果一上来就没发成功, 我们也是break, 然后打个提示信息, 好找错误.

我们测试一下吧?
  不过, 我们先把makefile在写一下, 因为现在是一个客户端和一个服务端同时编译了.
在这里插入图片描述
输入: netstat -aupn
在这里插入图片描述
在这里插入图片描述
看来好像是能跑了~

但是, 还是稍微存在一点细节的, 下面来进行细说:

8. 服务端ip应该绑0号pi.

  实际上, 我们的服务端是由很多ip地址的, 有内网, 有公网, 我们尝试用客户端以内网的形式发送信息给服务端.

我们看一下这台机器的内网ip:
在linux机器上输入: ifconfig
在这里插入图片描述
在这里插入图片描述
我们启动一下看一下结果:
在这里插入图片描述
  这是为啥呢? 因为我们的服务端绑定的不是内网啊, 服务端有很多个ip, 但是只能通过绑定的ip给他发信息, 是不太好的, 我们希望服务端的任何ip的数据包都可以被拿到. 所以:
  我们给服务端绑定ip地址为0号ip(0.0.0.0). 看来我们需要修改一下代码:
在这里插入图片描述

在这里插入图片描述

9. 加入日志

  为了输入一些debug和error信息比较方便, 我们可以写一个日志加进去, 之前是写好的了, 所以我们直接CV到代码里就好了.
在这里插入图片描述
在这里插入图片描述
我们再来看一下效果:
在这里插入图片描述

  然后我们还可以把那个ip + port封装起来, 可以让他自动转为主机序列, 而不用在外面转换.

10. 封装ip + port类

// InetAddr.hpp

#pragma once#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>class InetAddr
{
private:void ToHost(const struct sockaddr_in &addr){_port = ntohs(addr.sin_port);_ip = inet_ntoa(addr.sin_addr);}public:InetAddr(const struct sockaddr_in &addr):_addr(addr){ToHost(addr);}std::string Ip(){return _ip;}uint16_t Port(){return _port;}~InetAddr(){}private:std::string _ip;uint16_t _port;struct sockaddr_in _addr;
};

我们把封装好的这个ip + port弄到服务端代码中去:
在这里插入图片描述
在这里插入图片描述
  好的, 终于差不多了, 当然也有一些细节是没有处理好的, 或者说想要处理好细节, 还需要很多代码进行补充, 这次只是一个练习, 也就不求甚解了~

11. reference code

UdpServer.hpp

#pragma once
#include "nocopy.hpp"
#include <sys/socket.h> // 主要 socket 函数和类型
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <netinet/in.h>
#include <string>
#include <arpa/inet.h>
#include <unistd.h>
#include "LockGuard.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
using namespace log_ns;static const int gsockfd = -1;
static const int glocalport = 8899;
enum
{SOCKET_ERROR = 1,BIND_ERROR
};class UdpServer : public nocopy
{
public:UdpServer(std::string localip) : _sockfd(gsockfd), _localport(glocalport), /*_localip(localip),*/ _isrunning(false){}void InitServer(){// 创建sockfd文件_sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){// std::cout << "FATAL: socket error" << std::endl;LOG(FATAL, "socket error\n");exit(SOCKET_ERROR);}// std::cout << "DEBUG :socket create success, _sockfd: " << _sockfd << std::endl; // 3"LOG(DEBUG, "socket create success, _sockfd: %d\n", _sockfd); // 3// bind ip & portstruct sockaddr_in local;memset(&local, 0, sizeof(sockaddr_in));local.sin_family = AF_INET;/*local.sin_addr.s_addr = inet_addr(_localip.c_str());*/local.sin_addr.s_addr = INADDR_ANY; // 服务器端,进行任意IP地址绑定, INADDR_ANY -> 0, 表示绑定任意端口号local.sin_port = htons(_localport);int n = bind(_sockfd, (sockaddr *)&local, sizeof(local));if (n < 0){// std::cout << "FATAL, bind error" << std::endl;LOG(FATAL, "bind error\n");exit(BIND_ERROR);}// std::cout << "DEBUG, socket bind success" << std::endl;LOG(DEBUG, "socket bind success\n");}void Start(){_isrunning = true;char inbuffer[1024];struct sockaddr_in peer;socklen_t len = sizeof(peer);while (_isrunning){int n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer), 0, (sockaddr *)&peer, &len);if (n > 0){// 转换成主机序列InetAddr addr(peer);// uint16_t _port = ntohs(peer.sin_port);// std::string _ip = inet_ntoa(peer.sin_addr);inbuffer[n] = 0;// std::cout << "[" << _ip << ":" << _port << "]# " << inbuffer << std::endl; // 打印一下消息std::cout << "[" << addr.Ip() << ":" << addr.Port() << "]# " << inbuffer << std::endl; // 打印一下消息// 制造要发给客户端的数据std::string echo_string = "[udp_server echo] #";echo_string += inbuffer;sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr *)&peer, len);}else{std::cout << "recvfrom ,  error" << std::endl; // debug 信息.}}}~UdpServer(){if (_sockfd > gsockfd)::close(_sockfd);}private:int _sockfd;uint16_t _localport;// std::string _localip;bool _isrunning;
};

UdpServerMain.cc

#include"UdpServer.hpp"
#include<memory>int main()
{std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>("127.0.0.1"); //C++14的标准usvr->InitServer();usvr->Start();return 0;
}

UdpClientMain.cc

#include <stdlib.h>
#include <sys/socket.h>
#include <iostream>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>int main()
{int sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){std::cerr << "create socket error" << std::endl;exit(1);}// 客户端的socket文件会自动绑定ip地址和端口号, 我们无需关心// 你要发给谁?struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(8899);server.sin_addr.s_addr = inet_addr("10.0.16.4");while(1){// 让用户输入信息std::string line;std::cout << "Please Enter# ";std:getline(std::cin, line);// 发消息int n = sendto(sockfd, line.c_str(), line.size(), 0, (struct sockaddr*)&server, sizeof(server));if(n > 0) {// 收消息struct sockaddr_in temp;socklen_t len = sizeof(temp);char buffer[1024]; // 缓冲区int m = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&temp, &len);if(m > 0){buffer[m] = 0; // 写好字符串截至的位置std::cout << buffer << std::endl;}else{std::cout << "recvfrom error" << std::endl; // debugbreak;}}else // 没有发送出去{std::cout << "sendto error" << std::endl;break;}}::close(sockfd);return 0;
}

nocopy.hpp

#pragma onceclass nocopy
{
public:nocopy(){}~nocopy(){}nocopy(const nocopy&) = delete;const nocopy& operator=(const nocopy&) = delete;
};

Log.hpp

#pragma once#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <ctime>
#include <cstdarg>
#include <fstream>
#include <cstring>
#include <pthread.h>
#include "LockGuard.hpp"namespace log_ns
{enum{DEBUG = 1,INFO,WARNING,ERROR,FATAL};std::string LevelToString(int level){switch (level){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARNING:return "WARNING";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return "UNKNOWN";}}std::string GetCurrTime(){time_t now = time(nullptr);struct tm *curr_time = localtime(&now);char buffer[128];snprintf(buffer, sizeof(buffer), "%d-%02d-%02d %02d:%02d:%02d",curr_time->tm_year + 1900,curr_time->tm_mon + 1,curr_time->tm_mday,curr_time->tm_hour,curr_time->tm_min,curr_time->tm_sec);return buffer;}class logmessage{public:std::string _level;pid_t _id;std::string _filename;int _filenumber;std::string _curr_time;std::string _message_info;};#define SCREEN_TYPE 1
#define FILE_TYPE 2const std::string glogfile = "./log.txt";pthread_mutex_t glock = PTHREAD_MUTEX_INITIALIZER;// log.logMessage("", 12, INFO, "this is a %d message ,%f, %s hellwrodl", x, , , );class Log{public:Log(const std::string &logfile = glogfile) : _logfile(logfile), _type(SCREEN_TYPE){}void Enable(int type){_type = type;}void FlushLogToScreen(const logmessage &lg){printf("[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curr_time.c_str(),lg._message_info.c_str());}void FlushLogToFile(const logmessage &lg){std::ofstream out(_logfile, std::ios::app);if (!out.is_open())return;char logtxt[2048];snprintf(logtxt, sizeof(logtxt), "[%s][%d][%s][%d][%s] %s",lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curr_time.c_str(),lg._message_info.c_str());out.write(logtxt, strlen(logtxt));out.close();}void FlushLog(const logmessage &lg){// 加过滤逻辑 --- TODOLockGuard lockguard(&glock);switch (_type){case SCREEN_TYPE:FlushLogToScreen(lg);break;case FILE_TYPE:FlushLogToFile(lg);break;}}void logMessage(std::string filename, int filenumber, int level, const char *format, ...){logmessage lg;lg._level = LevelToString(level);lg._id = getpid();lg._filename = filename;lg._filenumber = filenumber;lg._curr_time = GetCurrTime();va_list ap;va_start(ap, format);char log_info[1024];vsnprintf(log_info, sizeof(log_info), format, ap);va_end(ap);lg._message_info = log_info;// 打印出来日志FlushLog(lg);}~Log(){}private:int _type;std::string _logfile;};Log lg;#define LOG(Level, Format, ...)                                        \do                                                                 \{                                                                  \lg.logMessage(__FILE__, __LINE__, Level, Format, ##__VA_ARGS__); \} while (0)
#define EnableScreen()          \do                          \{                           \lg.Enable(SCREEN_TYPE); \} while (0)
#define EnableFILE()          \do                        \{                         \lg.Enable(FILE_TYPE); \} while (0)
};

LockGuard.hpp

#pragma once#include <pthread.h>class LockGuard
{
public:LockGuard(pthread_mutex_t *mutex):_mutex(mutex){pthread_mutex_lock(_mutex);}~LockGuard(){pthread_mutex_unlock(_mutex);}
private:pthread_mutex_t *_mutex;
};

InetAddr.hpp

#pragma once#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>class InetAddr
{
private:void ToHost(const struct sockaddr_in &addr){_port = ntohs(addr.sin_port);_ip = inet_ntoa(addr.sin_addr);}public:InetAddr(const struct sockaddr_in &addr):_addr(addr){ToHost(addr);}std::string Ip(){return _ip;}uint16_t Port(){return _port;}~InetAddr(){}private:std::string _ip;uint16_t _port;struct sockaddr_in _addr;
};

makefile

.PHONT: all
all: udpserver udpclientudpserver: UdpServerMain.ccg++ -o $@ $^ -std=c++14
udpclient: UdpClientMain.ccg++ -o $@ $^ -std=c++14
.PHONY:clean
clean:rm -rf udpserver udpclient

文章转载自:

http://aBLcT83V.pmwhj.cn
http://W6VvBzJd.pmwhj.cn
http://tkPHlYLU.pmwhj.cn
http://E2rr2xbl.pmwhj.cn
http://QgSDmny7.pmwhj.cn
http://duYpnFv5.pmwhj.cn
http://8cRZhfua.pmwhj.cn
http://KmxHDIbK.pmwhj.cn
http://iEP0THc7.pmwhj.cn
http://V8tJHVj0.pmwhj.cn
http://jGRB7c6C.pmwhj.cn
http://g9mZLBsN.pmwhj.cn
http://5cTCmy83.pmwhj.cn
http://FzplJm7i.pmwhj.cn
http://e3JDN8Mr.pmwhj.cn
http://DVImmvDS.pmwhj.cn
http://GIwVURPG.pmwhj.cn
http://FeeRl7Os.pmwhj.cn
http://cEX1ooqw.pmwhj.cn
http://Otdy48rJ.pmwhj.cn
http://k77LGpQb.pmwhj.cn
http://ygNkKire.pmwhj.cn
http://PCENp14i.pmwhj.cn
http://gToMjMxB.pmwhj.cn
http://p4U4fUg7.pmwhj.cn
http://AoEyj0sD.pmwhj.cn
http://tcg1xSxM.pmwhj.cn
http://uh2Dme9y.pmwhj.cn
http://yVYF80ds.pmwhj.cn
http://jLbgIxKO.pmwhj.cn
http://www.dtcms.com/wzjs/705420.html

相关文章:

  • 网站开发留言板网站主题颜色
  • 网站添加js广告位网页特效代码下载
  • 1 建设网站目的微信扫码登记小程序
  • 石家庄制作网站的公司哪家好wordpress 引用图片
  • 昌平电子网站建设wordpress分享此文章
  • 买个网站域名要多少钱做美团旅游网站多少钱
  • 计算机毕设做网站wordpress 点评主题
  • 钛钢饰品移动网站建设网站中捕获鼠标位置
  • 南昌网站建设优化公司排名建筑公司网站设计思路
  • 公司做网站app入什么科目怎么查看自己的网站是否被百度收录
  • 在线教育网站模板wordpress批量插件
  • 超酷网站欣赏定制开发一个网站多少钱
  • php网站留言板漏洞千川广告投放平台
  • 能打开任何网站的浏览器小米手机如何做游戏视频网站
  • 牡丹江建设行业协会网站电商资源网站
  • 怎么把自己做的网站让别人收到网易博客搬家wordpress
  • 网站须知弹幕播放器 wordpress
  • 网站做推广的团队互联网小项目
  • win10怎么做网站移动建站是什么意思
  • 个人博客网站设计重庆给商家企业做网站
  • 大型网站的建设做app简单还是网站
  • wordpress网站加密码破解wordpress主题子主题运行速度
  • 上海建设网站费用客户案例 网站建设
  • 济源市建设管理处网站重新安装wordpress
  • 公司网站在国外打开很慢使用cdn好还是国外租用服务器好网页和网站的不同
  • 保定做网站苏州工业园区属于哪个区
  • 工程机械网站模板推销网站
  • 包头建设网站网站建设基本流程信息技术
  • 如何用万网建设网站企业做网站需要什么条件
  • yii2 网站开发微信如何创建自己的公众号