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

网站策划网温州文成县高端网站设计

网站策划网,温州文成县高端网站设计,自己做书画交易网站,wordpress 发表时间由于本人使用的方式原因,只打算了解socket模式,其余两种方式。适合裸机的RAW模式和基础的CONNECT模式不适合面向对象的编程方式,不打算看。 Socket API 与实现关系 1) 总体映射关系 LwIP 的 BSD-like Socket API 是在 netconn API 之上实现的…

由于本人使用的方式原因,只打算了解socket模式,其余两种方式。适合裸机的RAW模式和基础的CONNECT模式不适合面向对象的编程方式,不打算看。

Socket API 与实现关系

1) 总体映射关系

  • LwIP 的 BSD-like Socket API 是在 netconn API 之上实现的:
    • socket() 会创建一个 netconn,并把 netconn 指针保存在全局 sockets[] 数组对应的 struct lwip_sock 中。
    • sockets 数字(文件描述符)就是数组索引 + 偏移(LWIP_SOCKET_OFFSET)。
  • 绝大多数 socket 操作(bind/connect/send/recv/setsockopt 等)最终交给 netconn 层处理;netconn 进一步调用 core 层(udp/tcp/ip)完成真正的网络 I/O。
  • sockets 提供了兼容 POSIX 的接口(lwip_* 或系统宏映射),但在实现细节上依赖 lwIP 的线程/消息模型(tcpip_thread / netconn mailbox)。

2) 重要结构:struct lwip_sock

  • 主要成员:
    • struct netconn *conn:对应的 netconn(实际 I/O 对象)。
    • union lwip_sock_lastdata lastdata:保存上次未完全消费的数据(TCP 用 pbuf,UDP/RAW 用 netbuf)。
    • rcvevent、sendevent、errevent:用于 select/poll 判断事件与计数。
    • select_waiting:有多少线程在 select/poll 上等待该 socket。
    • (可选)fd_used、fd_free_pending:用于 full-duplex 模式下防止并发释放。
    • 在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 基本类型与约定

    • u8_t/u16_t/s8_t/s32_t:lwIP 的固定宽度整型(通常映射到 uint8_t/uint16_t/int8_t/int32_t),用于二进制协议字段与位掩码,避免平台差异。
    • size_t:用于字节计数(拷贝/写入长度);注意与 pbuf->tot_len(u16_t 或 u32_t,取决实现)之间的转换/截断风险。
    • sys_mbox_t/sys_sem_t:OS 抽象类型,封装消息队列/信号量;不同端口具体类型不同,尽量通过 sys_* API 操作,不直接访问内部字段。
    • ip_addr_t/pbuf/netbuf/netconn:关键复合类型,含指针(需注意生命周期和所有权:谁负责 free)。
  • netconn 相关宏与标志(语义)

    • NETCONN_NOFLAG/COPY/MORE/DONTBLOCK:写入时的行为控制
      • MORE:表示后续还有更多分段(影响 TCP Nagle/PSH 组合)。
      • DONTBLOCK:告诉 netconn_write_partly 立即返回,不阻塞。
    • NETCONN_FLAG_*(conn->flags)
      • MBOXCLOSED:接收/accept 队列已关闭,后续阻塞操作应失败/立刻返回。
      • NON_BLOCKING:netconn_is_nonblocking() 读取此位决定 recv/send 是否可阻塞。
      • IN_NONBLOCKING_CONNECT:用于 connect 的状态机(非阻塞 connect 的后续处理)。
      • CHECK_WRITESPACE:若此前写被拒绝,需要 poll 再次检测写入可用性。
      • PKTINFO:启用 recv 时附带到达接口/目标地址信息(netbuf->toaddr/toport)。
    • NETCONNTYPE_GROUP / DATAGRAM / ISIPV6:用来按类型(TCP/UDP/RAW)做通用处理,避免显式枚举每一项。
  • 事件枚举(netconn_evt)语义要点

    • RCVPLUS / RCVMINUS:计数式事件,表示“可安全再做一次潜在阻塞的 recv/accept”。socket 层把这些累加到 sock->rcvevent,再由 select/poll 判定。
    • SENDPLUS / SENDMINUS:写可/不可用事件(通常用作标志,不计数)。
    • ERROR:异步错误通知(设置 conn->pending_err),应用应通过 netconn_err() 查询。
  • netbuf 结构与 API 细节

    • struct netbuf { pbuf *p, *ptr; ip_addr_t addr; u16_t port; flags; toport_chksum; toaddr; }
      • p: 链表头,ptr: 当前遍历指针(netbuf_next 使用),netbuf_len 返回 p->tot_len(注意 tot_len 是 pbuf 的累积长度)。
      • NETBUF_FLAG_DESTADDR:表示 netbuf 包含目标地址(用于 UDP send/forward 场景)。
      • NETBUF_FLAG_CHKSUM:当 CHECKSUM_ON_COPY 被启用时,toport_chksum 存储端口与校验相关信息。
    • 常用宏:
      • netbuf_copy_partial/netbuf_take:基于 pbuf API 做部分拷贝;对大数据需多次调用 netbuf_next。
      • netbuf_fromaddr / netbuf_fromport:接收方读取源地址/端口的便捷宏。
      • netbuf_destaddr / netbuf_destport:在带 recvinfo 时用于目的地址(例如 IP_PKTINFO 场景)。
  • 并发、生命周期与所有权规则

    • recvmbox/acceptmbox:netconn 将接收到的数据/连接放入 mbox,应用线程负责从 mbox 中取出并释放(netbuf_delete / pbuf_free)。
    • recv_avail(LWIP_SO_RCVBUF):用于实现 FIONREAD / 限制接收缓冲,注意仅对 UDP/RAW 有效(TCP 使用 TCP_WND)。
    • op_completed(若没有 per-thread sem):netconn 在 core 上运行时用于同步完成回调,注意在 netconn_thread_init/cleanup 的配置差异。
    • callback 与 callback_arg:conn->callback 在 tcpip core 上调用,传入的 callback_arg 用于 socket 层/应用关联;回调必须是非阻塞且尽量短(因为运行在核心线程)。
  • 常见易错点与调试策略(针对宏/类型)

    • 误用 NETCONN_FLAG_NON_BLOCKING:直接修改 flags 位能改变行为,但不要绕过 netconn_set_nonblocking 宏(便于日后调整)。
    • pbuf/tot_len 与 size_t 交互:当数据量大于 pbuf 的 u16_t 限制(某些平台),注意可能发生截断或需要分片读取。
    • RCVPLUS 计数语义:收到多包会产生多次 RCVPLUS,socket 层可能把它转成 rcvevent 计数;select/poll 唤醒逻辑依赖该计数,调试时打印 conn->flags 与 sock->rcvevent 帮助定位唤醒丢失。
    • NETBUF_FLAG_DESTADDR 与 NETCONN_FLAG_PKTINFO:若期望获取目的地址,需同时在 netconn/pcb 层启用 recvinfo 标志,否则 netbuf 的 toaddr 不会被填充。

3) recv/send 的实现要点

  • 接收:
    • lwip_recvfrom -> lwip_recvfrom_udp_raw(UDP/RAW) 或 lwip_recv_tcp(TCP)。
    • 如果 sock->lastdata 有残留数据,会直接从 lastdata 返回(支持 MSG_PEEK、分段拷贝、部分读取)。
    • 当没有残留数据时,lwip_recvfrom 会调用 netconn_recv_*(在 netconn 层)去取数据;netconn 层在 tcpip_thread 中等待或从 mailbox 返回 netbuf/pbuf。
    • netbuf/pbuf 中的数据被拷贝到用户缓冲区;如果不是 peek,netbuf/pbuf 将被释放,或保存到 sock->lastdata 以便下次继续读取。
  • 发送:
    • lwip_sendto -> 构造 netbuf(或 pbuf 链) -> netconn_send(sock->conn, &buf)
    • 对于 TCP,lwip_send 使用 netconn_write_partly(可返回写入字节数)。
    • 发送调用通常会在调用线程与 tcpip_thread 之间通过消息/回调协调(取决于 CORE_LOCKING 配置)。

4) 阻塞 / 非阻塞 与 fcntl/ioctl

  • 非阻塞 I/O:可以用 fcntl(F_SETFL, O_NONBLOCK) 或 ioctlsocket(FIONBIO)。实现上通过 netconn_set_nonblocking 将 netconn 标记为非阻塞。
  • recv/send 的 MSG_DONTWAIT 也会被转成 NETCONN_DONTBLOCK 传递给 netconn 层。
  • select/poll 可配合非阻塞使用,也可用于阻塞等待事件发生。

5) select / poll 机制(实现细节)

  • 等待者管理:
    • 全局链表 select_cb_list 保存所有正在等待的 select/poll 的控制块(struct lwip_select_cb)。
    • 每个等待者分配一个 semaphore(或使用线程本地 sem),并把控制块加入链表后进入等待。
  • 事件产生与通知路径(从网络到应用):
    1. 数据到达网卡 → lwIP core 线程(tcpip_thread)处理 → 协议层最终把数据入 netconn 的接收队列。
    2. netconn 在接收或缓冲数据时触发事件(NETCONN_EVT_RCVPLUS / SENDPLUS / ERROR 等)。
    3. event_callback(socket 模块注册为 netconn 回调)在 tcpip_thread 上被调用:
      • 根据 evt 更新对应 socket 的 sock->rcvevent / sendevent / errevent。
      • 若 sock->select_waiting>0 并且需要检查等待者,则调用 select_check_waiters。
    4. select_check_waiters 遍历 select_cb_list,比较每个 select 的 fdset/pollfds 和 sock 的事件:
      • 若命中,则对 select 对应的控制块 sem_signalled=1 并 sys_sem_signal() 唤醒等待线程。
    5. 等待线程醒来后重新调用 lwip_selscan/lwip_pollscan 来读取最终事件并返回给应用。
  • 关键变量/机制:
    • sock->rcvevent 用于计数“接收到数据”的次数(避免丢失连续到达的唤醒)。
    • select_waiting 防止当等待者数多时 socket 被误释放(并用于上/下调等待计数)。
    • select_scan(lwip_selscan)会结合 sock->lastdata(是否有残留数据)和 rcvevent 判断是否可读。

6) select 与 poll 的差异与实现共性

  • 共性:
    • 都使用同样的事件来源(event_callback)和唤醒链表(select_cb_list)。
    • 都在被唤醒后调用各自的 scan 函数(lwip_selscan / lwip_pollscan)重新判断实际就绪项。
  • 差异:
    • select 使用 fd_set 位集合,poll 使用 pollfd 数组(更适合动态 fd 数量)。
    • poll 的实现额外提供 revents 存储并对 POLLNVAL 处理;poll 在唤醒时避免多次 copy fd_set。
  • 注意:
    • FD 在 FD_SET 时要考虑 LWIP_SOCKET_OFFSET 偏移(实现宏 FD_SET/FD_ISSET 已封装),不要直接用小范围的 fd 操作系统宏。
    • select/poll 都受限于 MEMP_NUM_NETCONN(即 sockets 数量)。

7) 同步与线程上下文

  • 若配置 LWIP_TCPIP_CORE_LOCKING:部分 socket 操作直接在任意线程加 core lock 即可调用内部实现;否则某些操作会通过 tcpip_callback 交给 tcpip_thread,并用 semaphore 等待完成(见 lwip_setsockopt_impl/getsockopt_impl 的分支)。
  • select/poll 的唤醒必须在 tcpip core 锁或受保护的上下文中进行(源码中有若干断言与锁宏)。

8) 重要的 socket options 与行为提示

  • SO_BROADCAST:若要 sendto 广播 IP(255.255.255.255 或 子网广播),必须先 setsockopt(SOL_SOCKET, SO_BROADCAST)。
  • SO_NO_CHECK:可用于 UDP 跳过校验(udp_set_flags)。
  • IP_PKTINFO / NETBUF_FLAG_DESTADDR:可用于接收方获取到达接口信息(需打开 NETCONN_FLAG_PKTINFO)。
  • IP_ADD_MEMBERSHIP / IPV6_JOIN_GROUP:通过 lwip_setsockoptImpl 对 IGMP/MLD 做组管理,socket close 时会自动 drop(代码中有注册表管理)。

9) 调试建议(常见问题定位)

  • 捕获数据但 recv 未返回:
    • 检查 sock->lastdata(peek 情况)、sock->rcvevent、netconn_recv 是否成功取到 netbuf。
    • 在 tcpip_thread 中断点:netconn 层(netconn_recv_udp_raw_netbuf_flags)、event_callback、select_check_waiters。
  • select 未被唤醒:
    • 检查 event_callback 是否被调用(netconn->callback 是否存在)。
    • 检查 select_cb_list 是否正确加入,以及 select_waiting 是否被增减。
  • 内存/句柄泄漏:
    • 检查 alloc_socket / free_socket 路径,确认 done_socket 在每个返回点都被调用。
    • 检查 lastdata 是否在释放路径中被 free(free_socket_free_elements)。

10) Socket API 使用详解(阻塞/非阻塞、select/poll 实现)

基本 Socket API 使用流程
TCP 服务端示例
int server_fd = lwip_socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(8080);lwip_bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
lwip_listen(server_fd, 5);int client_fd = lwip_accept(server_fd, NULL, NULL);
char buffer[1024];
ssize_t len = lwip_recv(client_fd, buffer, sizeof(buffer), 0);
lwip_send(client_fd, "Hello", 5, 0);
lwip_close(client_fd);
lwip_close(server_fd);
UDP 客户端示例
int udp_fd = lwip_socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in server_addr = {0};
server_addr.sin_family = AF_INET;
inet_pton(AF_INET, "192.168.1.100", &server_addr.sin_addr);
server_addr.sin_port = htons(9999);char data[] = "UDP Message";
lwip_sendto(udp_fd, data, sizeof(data), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));char response[512];
struct sockaddr_in from_addr;
socklen_t from_len = sizeof(from_addr);
ssize_t recv_len = lwip_recvfrom(udp_fd, response, sizeof(response), 0,(struct sockaddr*)&from_addr, &from_len);
lwip_close(udp_fd);
阻塞与非阻塞模式实现
阻塞模式(默认)
  • 实现原理
    • recv/send 调用时,若无数据/缓冲区满,线程会在 netconn->recvmbox/sendbuffer 上等待
    • netconn 层通过 sys_arch_mbox_fetch() 无限期等待数据到达
    • 数据到达时,tcpip_thread 会将 netbuf/pbuf 放入 mbox 并唤醒等待线程
// 阻塞接收 - 等到有数据才返回
char buffer[1024];
ssize_t len = lwip_recv(fd, buffer, sizeof(buffer), 0);  // 会阻塞
if (len > 0) {// 处理接收到的数据
}
非阻塞模式设置与使用
// 方法1:使用 fcntl 设置
int flags = lwip_fcntl(fd, F_GETFL, 0);
lwip_fcntl(fd, F_SETFL, flags | O_NONBLOCK);// 方法2:使用 ioctl 设置
int nonblock = 1;
lwip_ioctl(fd, FIONBIO, &nonblock);// 方法3:在单次调用中指定
ssize_t len = lwip_recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT);
if (len < 0 && errno == EWOULDBLOCK) {// 当前无数据可读,稍后重试
}
  • 实现原理
    • 设置 NETCONN_FLAG_NON_BLOCKING 标志位
    • netconn 层调用 netconn_recv_* 时传入 NETCONN_DONTBLOCK
    • 若 mbox 为空,立即返回 ERR_WOULDBLOCK 而不等待
select 机制详解
基本 select 使用
fd_set readfds, writefds, exceptfds;
int max_fd = 0;FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);// 添加需要监听的 socket
FD_SET(sock1, &readfds);
FD_SET(sock2, &readfds);
FD_SET(sock3, &writefds);
max_fd = MAX(sock1, MAX(sock2, sock3));struct timeval timeout = {5, 0};  // 5秒超时
int ready = lwip_select(max_fd + 1, &readfds, &writefds, &exceptfds, &timeout);if (ready > 0) {if (FD_ISSET(sock1, &readfds)) {// sock1 可读handle_read(sock1);}if (FD_ISSET(sock3, &writefds)) {// sock3 可写handle_write(sock3);}
} else if (ready == 0) {// 超时
} else {// 错误处理
}
select 实现原理

数据结构与状态管理

// 每个 socket 的事件计数器
struct lwip_sock {s16_t rcvevent;     // 接收事件计数(累加)u16_t sendevent;    // 发送事件标志(0/1)u16_t errevent;     // 错误事件标志(0/1)SELWAIT_T select_waiting;  // 等待该socket的select数量
};// select 等待控制块
struct lwip_select_cb {fd_set *readset, *writeset, *exceptset;int sem_signalled;              // 是否已被唤醒sys_sem_t sem;                  // 等待信号量struct lwip_select_cb *next, *prev;  // 链表节点
};

select 执行流程

  1. 初始扫描lwip_selscan() 检查所有fd当前状态

    • 检查 sock->lastdata(是否有残留数据)
    • 检查 sock->rcvevent > 0(是否有接收事件)
    • 检查 sock->sendevent != 0(是否可写)
  2. 等待设置(若无就绪事件):

    • 创建 lwip_select_cb 并加入全局 select_cb_list
    • 对所有相关socket增加 select_waiting 计数
    • 线程在信号量上等待
  3. 事件触发

    • 网络数据到达 → event_callback() 更新 sock->rcvevent
    • 调用 select_check_waiters() 检查等待列表
    • 若匹配则设置 sem_signalled=1sys_sem_signal()
  4. 唤醒与清理

    • 等待线程被唤醒,重新调用 lwip_selscan() 获取最终结果
    • 减少 select_waiting 计数,从等待列表移除

关键时序(数据到达唤醒select)

NIC中断 → tcpip_thread处理 → udp_input/tcp_input → pcb->recv_callback 
→ netconn接收队列 → API_EVENT(NETCONN_EVT_RCVPLUS) → event_callback 
→ sock->rcvevent++ → select_check_waiters → sys_sem_signal → 等待线程唤醒
poll 机制详解
基本 poll 使用
struct pollfd fds[3];
memset(fds, 0, sizeof(fds));// 设置要监听的事件
fds[0].fd = sock1;
fds[0].events = POLLIN;           // 监听可读fds[1].fd = sock2;
fds[1].events = POLLIN | POLLOUT; // 监听可读可写fds[2].fd = sock3;
fds[2].events = POLLOUT;          // 监听可写int timeout_ms = 3000;  // 3秒超时
int ready = lwip_poll(fds, 3, timeout_ms);if (ready > 0) {for (int i = 0; i < 3; i++) {if (fds[i].revents & POLLIN) {// fds[i].fd 可读handle_read(fds[i].fd);}if (fds[i].revents & POLLOUT) {// fds[i].fd 可写handle_write(fds[i].fd);}if (fds[i].revents & POLLERR) {// fds[i].fd 发生错误handle_error(fds[i].fd);}}
}
poll vs select 差异
特性selectpoll
fd集合表示fd_set 位图(固定大小)pollfd 数组(动态大小)
fd数量限制FD_SETSIZE(通常1024)仅受内存限制
事件表示分离的读/写/异常集合每个fd的events/revents字段
超时精度struct timeval(微秒)int(毫秒)

poll实现要点

  • 使用 lwip_pollscan() 扫描和更新 revents
  • 同样使用 select_cb_list 和事件回调机制
  • lwip_poll_should_wake() 检查特定fd的特定事件
超时机制实现
socket 级别超时
// 设置接收超时
struct timeval tv = {5, 0};  // 5秒
lwip_setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));// 设置发送超时
tv.tv_sec = 3;
lwip_setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));// 后续recv/send会在指定时间后超时返回
select/poll 超时
  • selectstruct timeval *timeout,NULL表示无限等待
  • pollint timeout,-1表示无限等待,0表示立即返回

超时实现

  • 通过 sys_arch_sem_wait(sem, timeout_ms) 实现
  • 超时返回 SYS_ARCH_TIMEOUT,正常返回时间差
高级使用技巧
1. 水平触发 vs 边缘触发

LwIP的select/poll是水平触发

  • 只要条件满足(有数据可读/可写),每次select/poll都会返回
  • 需要在处理完数据后再次调用select/poll
2. 组合使用示例
// 非阻塞socket + select的典型模式
int setup_nonblocking_server(int port) {int server_fd = lwip_socket(AF_INET, SOCK_STREAM, 0);// 设置非阻塞int flags = lwip_fcntl(server_fd, F_GETFL, 0);lwip_fcntl(server_fd, F_SETFL, flags | O_NONBLOCK);// 绑定监听struct sockaddr_in addr = {0};addr.sin_family = AF_INET;addr.sin_addr.s_addr = INADDR_ANY;addr.sin_port = htons(port);lwip_bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));lwip_listen(server_fd, 10);return server_fd;
}void event_loop(int server_fd) {fd_set master_set, read_set;int max_fd = server_fd;FD_ZERO(&master_set);FD_SET(server_fd, &master_set);while (1) {read_set = master_set;int ready = lwip_select(max_fd + 1, &read_set, NULL, NULL, NULL);for (int fd = 0; fd <= max_fd && ready > 0; fd++) {if (FD_ISSET(fd, &read_set)) {ready--;if (fd == server_fd) {// 新连接到达int client_fd = lwip_accept(server_fd, NULL, NULL);if (client_fd >= 0) {// 设置客户端为非阻塞int flags = lwip_fcntl(client_fd, F_GETFL, 0);lwip_fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);FD_SET(client_fd, &master_set);if (client_fd > max_fd) max_fd = client_fd;}} else {// 客户端数据到达char buffer[1024];ssize_t len = lwip_recv(fd, buffer, sizeof(buffer), 0);if (len <= 0) {// 连接关闭或错误lwip_close(fd);FD_CLR(fd, &master_set);} else {// 处理数据handle_client_data(fd, buffer, len);}}}}}
}
3. 错误处理要点
// 完整的错误处理示例
ssize_t safe_recv(int fd, void *buf, size_t len) {ssize_t result = lwip_recv(fd, buf, len, 0);if (result < 0) {switch (errno) {case EWOULDBLOCK:case EAGAIN:// 非阻塞模式下无数据,稍后重试return 0;case ECONNRESET:// 连接被对端重置printf("Connection reset by peer\n");return -1;case EINTR:// 被信号中断,可重试return safe_recv(fd, buf, len);default:printf("recv error: %s\n", strerror(errno));return -1;}}return result;
}
http://www.dtcms.com/a/405511.html

相关文章:

  • 免费域名申请网站空间网站做虚假宣传有没有做处罚
  • 做传销网站php企业网站管理系统
  • 做网站哪些公司好云起时网站建设
  • 网络营销与管理专业是干什么的旺道seo推广效果怎么样
  • 新彊生产建设兵团网站网站icp备案查询官网
  • 徐州市政建设集团公司网站西安推广公司无网不胜
  • 上海网站建设公司介绍网站建设投
  • 网站建设与运营市场风险邯郸信息港招聘信息
  • 西安公司注册网站网站建设 教案
  • 广州网站推广html网页设计作业成品代码免费下载
  • 建购物网站要多少钱重庆seo推广外包
  • 购物网站建设存在的问题哈尔滨建设集团
  • 兼职做海报网站wordpress mysql扩展
  • 公司网站建设的会计分录有效的网站推广方案
  • 手机商城网站建设江西网站开发科技公司
  • 上海的广告公司网站建设建设一个网站需要做哪些工作
  • 正版seo搜索引擎泉州seo用户体验
  • 来宾网站建设石家庄网站建设是什么意思
  • 织梦网站地图调用全站文章影城网站设计
  • 做影视免费网站违法吗设计师互动平台
  • 南京模板建站哪家好河北公司网站建设效果
  • 网页粒子效果网站.帮别人做网站
  • 杭州网站推广技巧网络优化有哪些主要流程
  • 网站空间格式asp网站建设设计解决方案
  • 公司网站建设考核东莞公司注册地址可以是住宅吗
  • 建设网站书怎么免费建设个人网站
  • 做暧暧免费网站网站备案名称重复
  • 简述网站的建设步骤电脑网站打不开怎么解决
  • 沈阳做网站 熊掌号景安网站备案幕布
  • 织梦网站建设培训怎么制作网站编辑页面