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

乌兰县wap网站建设公司免费的招标网有哪些

乌兰县wap网站建设公司,免费的招标网有哪些,怀化seo公司,做网站含营销1、TCP没考虑粘包分包 TCP是面向连接的可靠协议#xff0c;TCP是流式协议#xff0c;创建TCP套接字的类型为SOCK_STREAM int sockfd socket(AF_INET, SOCK_STREAM, 0);很多同学面试时对书上的话背诵如流#xff0c;在实际TCP编程中却没有处理粘包和分包的代码#xff0c;以…1、TCP没考虑粘包分包 TCP是面向连接的可靠协议TCP是流式协议创建TCP套接字的类型为SOCK_STREAM int sockfd socket(AF_INET, SOCK_STREAM, 0);很多同学面试时对书上的话背诵如流在实际TCP编程中却没有处理粘包和分包的代码以为TCP也和UDP一样客户端每send一次服务端就会recv一次在本机上测试可能也没有出现问题一旦到了线上发生粘包和分包的情况就会导致逻辑出错甚至程序崩溃。 解决方案 1、使用流式解析器保存当前状态如http-parser就使用了流式解析 2、使用缓存push接收到的数据判断接收到完整的一帧数据再pop取出进行处理 Tips 在libhv中可通过hio_set_unpack设置拆包规则支持固定包长、分隔符、头部长度字段三种常见的拆包方式调用该接口设置拆包规则后内部会根据拆包规则处理粘包与分包保证回调上来的是完整的一包数据大大节省了上层处理粘包与分包的成本。 2、UDP没考虑丢包 在一些追求低延时的场景为了避免TCP三次握手我们会考虑使用UDP协议但是却忽略了系统对丢包的容忍度没考虑到某个关键包丢失带来的影响没有重传重组机制。 解决方案 结合FEC、KCP、UDT、QUIC等手段增强可靠性 Tips: libhv计划陆续集成FEC、KCP、UDT、QUIC等开源实现欢迎有志之士加入 3、长连接没考虑应用层心跳 TCP连接不是指真的有一条物理的连接而是通信双方靠状态来记录维持的从客户端发起SYN请求开始状态就开始有序转换了。如果不发包我们也就无法感知对方是否掉线虽然TCP协议本身有keepalive机制但是默认的间隔时间特别久也无法携带其它信息所以发送应用层心跳是非常有必要的能快速感知掉线以便做出通知和处理也能及时关闭fd释放相关资源以节省开销。 解决方案 使用定时器发送心跳包多长时间或者多少次没有收到回应便断开连接 Tips 在libhv中可通过hio_set_heartbeat设置心跳 4、大数据没考虑分片和流量控制 见过有人直接将几十M、上百M甚至几G的文件直接读到内存进行发送试问你家内存TB级别的吗经的起这么消耗另外不做发送速率控制和流量控制可能会导致网络拥塞。 解决方案 循环从磁盘读取少量数据到内存再发送并做好流量控制 Tips: libhv中大文件的发送示例可参考examples/httpd里的largeFileHandler 5、客户端没考虑断线重连 网络哪没有个掉线的时候如果没有断线重连机制将会严重影响用户体验试想你正在打游戏突然掉线了不给你自动重连必须重新启动应用程序是不是很影响心情。 Tips: 在libhv中可通过TcpClient::setReconnect设置重连延时策略 6、外网没考虑加密通信 在外网环境不使用SSL/TLS加密通信就犹如一个人在大街上裸奔没有丝毫隐私可言安全系数为0。 解决方案 1、集成openssl、gnutls、mbedtls等SSL/TLS加密通信库 2、在网关处使用SSL代理如使用nginx做反向代理服务 Tips: 在libhv中集成了openssl、gnutls、mbedtls等SSL/TLS加密通信库打开WITH_OPENSSL、WITH_GNUTLS、WITH_MBEDTLS选项编译通过hio_enable_ssl即可开启SSL/TLS加密通信。 7、没有处理SIGPIPE 当向已经收到RST的socket执行写操作时内核就会向进程发送一个SIGPIPE信号该信号的默认行为是终止进程。通常的做法是忽略该信号。 signal(SIGPIPE, SIG_IGN); 8、大小端字节序问题 计算机硬件有两种存储数据的方式大端字节序和小端字节序。网络通信中我们一般使用大端字节序如果我们不按照对应的字节序来编码解码就会得到错误的值。 9、多线程发送乱序问题 TCP虽然保证重传重组但是我们自己要保证发送数据的有序性特别是多线程发送时即使加锁我们也无法保证哪个线程先发送除非每个发送的包都是独立完整的一包不分先后顺序否则就可能引发乱序问题。 解决方案 通常不建议多线程发送而是由一个线程来负责发送。 Tips: libhv中的hio_write、hio_close是多线程安全的这可以让网络IO事件循环线程里接收数据、拆包组包、反序列化后放入队列消费者线程从队列里取出数据、处理后发送响应和关闭连接变得更加简单。 10、串包问题 串包即将本将发送给A的数据发送给了B。通常发生在服务器用fd1接受A的请求A掉线后B再上线了POSIX标准要求每次打开文件的时候必须是要当前最小可以的文件描述符于是又将fd1分配给了B如果你继续使用fd1给A发送数据就会发送到了B。 解决方案 该问题发生的根本原因是使用fd作为了设备的标示应该建立某种机制来确认socket句柄是否是你想发送的那一个例如设备连接后通过登录验证携带uuid来唯一标示该设备。 11、server端业务进程响应心跳超时被监控进程kill导致数据或者逻辑异常 我们的后台框架采用的是proxyworker模型proxy处理连接和会话worker处理业务proxy和worker之间通过共享内存队列进行通信并有监控进程扫描proxy和worker的运行情况。管理进程会定时向worker发起心跳查询防止业务进程挂起。业务worker的心跳默认是60s如果任务处理超过60s没有回复心跳该进程会被认为异常被监控进程kill后重启。review代码没有发现sleep或者耗时操作初步判定是网络连接异常导致的超时检查了客户端连接代码果然是没有使用带超时的连接导致超时被监控进程kill 分析到这里应该是找到原因了但是疑问来了默认连接超时是多久该怎样设置连接超时如果是非阻塞的socket该怎么做 先自行回顾下三次握手的过程。 connect函数的接口并没有设置超时时间那么默认的超时机制是什么情况 int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen); 侯捷说源码面前了无秘密还是看协议栈源码。 先从系统调用开始inet_stream_connect是connect调用的socket层实现 代码在内核文件 net/ipv4/af_inet.c int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags){ struct sock *sk sock-sk; int err; long timeo; ... timeo sock_sndtimeo(sk, flags O_NONBLOCK); ...} 从代码可以看到connect只是完成发送syn的过程后续的两次握手由协议栈完成。如果是非阻塞方式返回的错误码是EINPROGRESS超时时间在连接前设置设置连接超时和设置发送超时是一样的 。 timeo sock_sndtimeo(sk, flags O_NONBLOCK); 设置超时的实现​​​​​​​ static inline long sock_sndtimeo(const struct sock *sk, bool noblock){ return noblock ? 0 : sk-sk_sndtimeo;} 如果没有设置发送超时那么默认的机制是什么继续看代码传输层的具体实现是调用tcp_v4_connect()中间过程忽略最后调用构造syn并发送的接口是tcp_connect() 代码在文件 net/ipv4/tcp_output.c 从代码可以看到发送后会启用重传定时器直到应答或者超时,每次重传的超时时间采用指数退避的方式。具体实现是这两个函数​​​​​​​ tcp_write_timeout()retransmits_timed_out() 如果发送syn超时没有响应重传次数sysctl_tcp_sys_retries这个值是在tcp的系统参数设置使用 sysctl 查看默认设置为5​​​​​​​ sysctl net.ipv4.tcp_syn_retriesnet.ipv4.tcp_syn_retries 5 至此tcp的connect的机制已经很清楚了如果设置了超时当syn_retries重传syn次数的累计时间大于超时那么在超时后返回否则在syn_retries重传累计时间后返回。 为了验证以上逻辑使用telnet 和tcpdump进行验证​​​​​​​ telnet 192.168.128.254 10086 Trying 192.168.128.254... telnet: Unable to connect to remote host: Connection timed out 耗时63.128s tcpdump跟踪如下​​​​​​​ 00:37:25.986061 IP 192.168.128.131.46640 192.168.128.254.10086: Flags [S], seq 3312153031, win 1460000:37:26.983700 IP 192.168.128.131.46640 192.168.128.254.10086: Flags [S], seq 3312153031, win 1460000:37:28.987752 IP 192.168.128.131.46640 192.168.128.254.10086: Flags [S], seq 3312153031, win 1460000:37:32.995936 IP 192.168.128.131.46640 192.168.128.254.10086: Flags [S], seq 3312153031, win 1460000:37:41.012194 IP 192.168.128.131.46640 192.168.128.254.10086: Flags [S], seq 3312153031, win 1460000:37:57.059795 IP 192.168.128.131.46640 192.168.128.254.10086: Flags [S], seq 3312153031, win 14600 如果目标ip不可达在5次重试后返回总耗时63.128s从时间戳可以看到重传syn的时间是采用指数退避的方式分别为 1,2,4,8,16,32 如果目标ip可达只是没有对应监听端口在一次重试后对端机器直接发送了reset标志连接结束耗时只要1s多tcpdump跟踪如下​​​​​​ 00:52:21.776637 IP 192.168.128.131.58497 192.168.128.1.10086: Flags [S], seq 2415778508, win 1460000:52:22.775693 IP 192.168.128.131.58497 192.168.128.1.10086: Flags [S], seq 2415778508, win 1460000:52:22.799371 IP 192.168.128.1.10086 192.168.128.131.58497: Flags [R.], seq 19086327, ack 2415778509, win 64240 telnet: Unable to connect to remote host: Connection refused 因此要设置连接超时其实有两种方法如果是非阻塞方式按照Stevens的建议 1、设置socket为非阻塞 2、根据connect返回值检查连接是否建立 3、调用select 4、检查超时 5、检查socket状态是可读还是可写 如果是阻塞方式根据之前的源码分析只要在连接前设置socket的发送超时即可 int connect_with_timeout(){ ... struct timeval timeo {1, 0}; socklen_t len sizeof(timeo); fd socket(AF_INET, SOCK_STREAM, 0); setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, timeo, len); int retconnect(fd, (struct sockaddr*)addr, sizeof(addr)) ...} 12、在接入层调用后端多个逻辑服务时某一个后端的服务异常导致接入层不能处理新的请求 查看机器的log发现某个后端服务异常代码使用了短连接请求后端服务并在失败时自动重试创建socket时发生错误。查看机器的网络状态发现有大量的TIME_WAIT状态。 统计机器的TIME_WAIT状态数量有几个命令最简单的是 cat /proc/net/sockstat​​​​​​​ sockets: used 2861TCP: inuse 603 orphan 0 tw 19 alloc 795 mem 339UDP: inuse 985 mem 557 如果要查看更详细的状态统计可以使用netstat 或者ss 加 awk 来处理 netstat -ant |awk {if(NR1)s[$NF]} END {for(k in s) print k,s[k]} ss -ant |awk {if(NR1)s[$1]} END {for(k in s) print k,s[k]} 推荐使用ss命令当socket数量很大的时候ss会快很多。 因此原因很清楚了是短连接在后端服务异常时大量产生的TIME_WAIT状态导致创建文件描述符失败不能处理请求。 这种情况通常的处理建议是打开tcp_tw_recycle 或者tcp_tw_reuse 选项那么是否有效还会不会有什么坑 TIME_WAIT是在连接断开时产生先看下连接断开的过程 上面就是常说的连接断开四次挥手的过程TIME_WAIT出现在主动断开连接方那它存在的意义是什么呢 stevens在unix网络编程里边讲到有两点 1、保证TCP连接关闭的可靠性。如果最终发送的ACK丢失被动关闭的一端会重传最终的FIN包如果执行主动关闭的一端没有维护这个连接的状态信息会发送RST包响应导致连接不正常关闭。 2、允许老的重复分组在网络中消逝。假设在一个连接关闭后发起建立连接的一端客户端立即重用原来的端口、IP地址和服务端建立新的连接。老的连接上的分组可能在新的连接建立后到达服务端TCP必须防止来自某个连接的老的重复分组在连接终止后再现从而被误解为同一个连接的化身。要实现这种功能TCP不能给处于TIME_WAIT状态的连接启动新的连接。 TIME_WAIT的时长通常定义成2*MSLMSL表示报文在网络上存在的最长时间如果超过这个时间报文将被丢弃。linux下TIME_WAIT被定义在tcp.h中时间是60s除非重新编译内核否则不能修改。​​​​​​​ /* how long to wait to destroy TIME-WAIT state, about 60 seconds*/ #define TCP_TIMEWAIT_LEN (60*HZ) 如果每秒有1000个请求在60秒内产生的TIME_WAIT就有60000个要控制或者减少TIME_WAIT的数量协议栈提供了tcp_tw_recycle、tcp_tw_reuse、tcp_max_tw_buckets这几个选项下面逐一分析。 tcp_tw_recycle linux协议栈实现的时候提供了一种快速回收TIME_WAIT状态的机制不用等待2MSL的时间只要等待一个重传的时间即可回收在idc内部这个时间极短可能不到1ms。但是新建立的连接可能存在风险 1、如果之前的FIN延迟到达新连接会被reset 2、如果之前发出的包延时后到达对端会造成干扰 tcp协议栈设计的时候是如何处理这些风险呢代码如下​​​​​​​ int tcp_conn_request(struct request_sock_ops *rsk_ops, const struct tcp_request_sock_ops *af_ops, struct sock *sk, struct sk_buff *skb){ ... if (!want_cookie !isn) { if (tcp_death_row.sysctl_tw_recycle) { bool strict; dst af_ops-route_req(sk, fl, req, strict); if (dst strict !tcp_peer_is_proven(req, dst, true,tmp_opt.saw_tstamp)) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED); goto drop_and_release; } } } ...} 在tcp_tw_recycle模式下判断是无效连接的条件是 1、来自对端的tcp syn请求携带时间戳 2、本机在MSL时间内接收过来自同一台ip机器的tcp数据 3、新连接的时间戳小于上次tcp数据的时间戳 以上条件满足时连接请求会被拒绝使用netstat -s |grep timestamp 有如下记录 ……packets rejects in established connections because of timestamp 因此在启用了tcp_tw_recycle的情况下TIME_WAIT时间内60s同一源ip主机syn请求中的timestamp必须是递增的连接才能被接受。 这个看起来很完美同一个主机的timestamp的一定是递增的但是NAT环境就悲剧了NAT下多个主机映射到同一个或几个对外IPNAT设备只修改源地址和端口timestamp不做修改不能保证来自NAT机器多个主机间连接请求的timestamp是递增的时间戳小的请求都会被拒绝。 tcp_tw_reuse TIME_WAIT的重用只满足一定的条件下处于TIME_WAIT状态的socket连接可以被新请求的syn使用。条件如下 1、新请求的sequence要大于TIME_WAIT连接的最后的sequence 2、如果启用了tcp的timestamp选项syn请求的时间戳要大于TIME_WAIT连接最后接收数据的时间戳 这个选项没有太大的意义满足这个条件的情形并不多并不能减少TIME_WAIT的数量。 tcp_max_tw_buckets这个选项其实没有什么可说的就是设置系统允许的最大TIME_WAIT数量如果超过这个量就不再出现TIME_WAIT直接close​​​​​​​ struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, struct inet_timewait_death_row *dr, const int state){ struct inet_timewait_sock *tw; if (atomic_read(dr-tw_count) dr-sysctl_max_tw_buckets) return NULL; ...} 这种方式对TIME_WAIT数量控制简单粗暴但是效果也比较明显。但是问题和tcp_tw_recycle类似新连接也可能被对端重传的FIN reset。 总结控制TIME_WAIT的选项都存在一些问题最好慎用。最好的方式是维持正常的TIME_WAIT状态通过连接池的方式复用连接减少TIME_WAIT出现的数量。如果要使用tcp_tw_recycle一定要确保没有NAT设备接入如果是只有client场景的机器可以使用tcp_tw_reuse或增大net.ipv4.ip_local_port_range范围来发起更多的连接。 13、使用spp实现简单的web服务器压测时短连接功能正常但是采用keeplive模式大约有40ms的时延 抓包分析发现server端连续发送了两个小于mss的包第一个包发出后经过大约40ms才确认第二个包才发出。检查代码回包时先回了http的包头再回复http的body命中nagle算法和delayed ack的应用场景后一个包延时发出但是为什么短连接正常长连接有问题 wikipedia对nagle算法的描述 https://en.wikipedia.org/wiki/Nagle%27s_algorithm 算法实现​​​​​​​ if there is new data to send if the window size MSS and available data is MSS send complete MSS segment now else if there is unconfirmed data still in the pipe enqueue data in the buffer until an acknowledge is received else send data immediately end if end ifend if xshell或telnet这样的应用每次键盘输入发送包含一个字符的包却要耗费40字节的包头tcp头加ip头为了改进这种情况Nagle算法的做法是先把第一个小包发送出去后面的小包都缓存起来直到收到前一个数据段的ack或者缓存数据长度已经达到mss大小才发送代码文件在 net/ipv4/tcp_output.c​​​​​​​ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, int push_one, gfp_t gfp){ ... if (tso_segs 1) { if (unlikely(!tcp_nagle_test(tp, skb, mss_now,(tcp_skb_is_last(sk, skb) ?nonagle : TCP_NAGLE_PUSH)))) break; } else { if (!push_one tcp_tso_should_defer(sk, skb, is_cwnd_limited,max_segs)) break; } ...} 具体判断是否启用nagle算法的逻辑 /* Return false, if packet can be sent now without violation Nagles rules: * 1. It is full sized. (provided by caller in %partial bool) * 2. Or it contains FIN. (already checked by caller) * 3. Or TCP_CORK is not set, and TCP_NODELAY is set. * 4. Or TCP_CORK is not set, and all sent packets are ACKed. * With Minshalls modification: all sent small packets are ACKed. */static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp, int nonagle){ return partial ((nonagle TCP_NAGLE_CORK) || (!nonagle tp-packets_out tcp_minshall_check(tp)));} 根据上面代码nagle算法生效的条件是 1、当前发送的包小于mss 2、启用TCP_NAGLE_CORK 并禁用TCP_NODELAY或者启用TCP_NODELAY有需要发送的数据以及还未ack的数据包 明确nagle算法的条件后问题来了为什么上一个包的ack是经过了40ms才返回正常情况应该就是一个rtt的时间同一idc的rtt时延小于1ms怎么会有40ms延时呢 其实这个涉及到了tcp协议的另外一个机制延迟确认delayed ack tcp发送ack有两种方式quick ack 和 delayed ack quick ack收到数据包后立即发送ACK给对端。 delayed ack收到数据包后不会立即发送ACK而是启动延时确认定时器在此期间 1. 本端有数据包要发送给对端。就在发送数据包的时候捎带上此ACK。 2. 本端没有数据包要发送定时器超时后发送ACK给对端。 根据算法的描述可以看到nagle算法和delayed ack都是为了减少小数据包在网路中传输的数量优化网络性能。 delayed ack的具体实现在代码文件net/ipv4/tcp_input.c 检查是否需要发送ack static void __tcp_ack_snd_check(struct sock *sk, int ofo_possible){ struct tcp_sock *tp tcp_sk(sk); /* More than one full frame received... */ if (((tp-rcv_nxt - tp-rcv_wup) inet_csk(sk)-icsk_ack.rcv_mss /* ... and right edge of window advances far enough. * (tcp_recvmsg() will send ACK otherwise). Or... */ __tcp_select_window(sk) tp-rcv_wnd) || /* We ACK each frame or... */ tcp_in_quickack_mode(sk) || /* We have out of order data. */ (ofo_possible skb_peek(tp-out_of_order_queue))) { /* Then ack it now */ tcp_send_ack(sk); } else { /* Else, send delayed ack. */ tcp_send_delayed_ack(sk); }} 满足下列条件之一需要立即发送ack否则进入延迟确认模式 1、收到大于mss的包且有能力接收数据 2、满足快速确认模式 3、有乱序的数据需要对端重传 检查是否快速确认模式 /* Send ACKs quickly, if quick count is not exhausted * and the session is not interactive. */static inline int tcp_in_quickack_mode(const struct sock *sk){ const struct inet_connection_sock *icsk inet_csk(sk); return icsk-icsk_ack.quick !icsk-icsk_ack.pingpong;} 快速确认模式的初始化 static void tcp_incr_quickack(struct sock *sk){ struct inet_connection_sock *icsk inet_csk(sk); unsigned int quickacks tcp_sk(sk)-rcv_wnd / (2 * icsk-icsk_ack.rcv_mss); if (quickacks 0) quickacks 2; if (quickacks icsk-icsk_ack.quick) icsk-icsk_ack.quick min(quickacks, TCP_MAX_QUICKACKS);} static void tcp_enter_quickack_mode(struct sock *sk){ struct inet_connection_sock *icsk inet_csk(sk); tcp_incr_quickack(sk); icsk-icsk_ack.pingpong 0; icsk-icsk_ack.ato TCP_ATO_MIN;} socket有一个pingpong属性来表明当前会话是否交互模式如果是会使用延迟确认机制这个值是动态计算的。 有数据要发送时如果当前时间与最近接受数据包的时间间隔小于ato40msminrtt200ms则进入pingpong模式。因此一旦有数据交互后很快就切换到pingpong模式。 综上在长连接的模式下会话很快进入pingpong模式server端先回了一个http头的小包client收到数据准备回复ack时进入延时确认机制server端继续发送http body也是一个小包nagle算法生效需要等前一个包的ack到达或者发送的数据大于mss时数据才会发送等40ms后前一个包的延时ack到达http的body内容才发送出去。 至此问题已经真相大白但是有个疑问为什么短连接的时候server端也是连续写入了两个小包为什么没有触发nagle算法和delayed ack 呢玄机在这里 /* There is something which you must keep in mind when you analyze the * behavior of the tp-ato delayed ack timeout interval. When a * connection starts up, we want to ack as quickly as possible. The * problem is that good TCPs do slow start at the beginning of data * transmission. The means that until we send the first few ACKs the * sender will sit on his end and only queue most of his data, because * he can only send snd_cwnd unacked packets at any given time. For * each ACK we send, he increments snd_cwnd and transmits more of his * queue. -DaveM */static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb){ ...} tcp_event_data_recv函数的注释解释的很清楚 连接刚启动的时候拥塞算法使用的是慢启动必须尽快发送ack发送方才可能尽快增大发送窗口发送更多的数据所以在首次收包的时候启用了快速确认模式pingpong模式的值为0而短连接只有一次业务数据的收发后边连接就关闭了nagle算法和delayed ack并没有生效。 针对问题描述的情形解决方案是把http头和http body合并后发送就不会有问题了因为接收方收到完整数据后会重新发起新的请求这时候会把上一个包的ack附带发回发送方就不用等40ms的超时了。 如果发送方确实存在有多个小包要分别发送并使用长连接的情况最好是禁用nagle算法其实这也是主流的做法nginx在keeplive模式下就禁用了nagle算法。
http://www.tj-hxxt.cn/news/229923.html

相关文章:

  • 深圳做男装什么网站容易找工网络营销第二板斧是什么
  • 商城型网站建设多少钱渭南网站建设推广
  • 网站平台做捐助功能有风险吗什么是php网站
  • 黑龙江省建设教育信息网站广西桂林天气预报15天查询
  • 做的视频传到哪个网站好静态网站模板源码下载
  • 上海行业网站建设在线制作头像框
  • dremrever做网站流程网页生成pdf不显示
  • 高端建站是什么公司网站建设建设
  • 新网站 seo网站seo分析报告
  • 新建网站站点的一级a做爰片图片免费观看网站
  • 重庆高考征集志愿网站拍摄企业宣传片哪家好
  • 网站建设初期怎么添加内容天津专业网站设计
  • 广西网站设计运营公司如何推广网站运营
  • 在线原型设计网站wordpress 主页地址函数
  • 一站式外贸综合服务平台如何建设一个简单的公司网站
  • wordpress 火车头接口邯郸网站优化技巧
  • 合肥企业建站程序国家重大建设项目库网站电话
  • 网站移动化建设方案asp.net 网站建设方案
  • 网站建设厌倦网站 缓存方式
  • 万维网申请网站域名佛山国内快速建站
  • 东营网站建设教程简单建设网站首页
  • 建地方门户网站建筑设计方案汇报ppt
  • 结婚网站模版深州做网站公司
  • 合肥网站制作开发个人个案网站 类型
  • 支付宝 收费 网站开发建站工具论坛
  • 专业的盐城网站开发wordpress如和安装
  • 网站建设是什么语言齐齐哈尔网站开发
  • 中小型网站建设服务重庆建设工程交易网
  • 网站建设 预算江华网站建设
  • 培训网站设计简单的网页开发