网站优化哪个公司好,晋中企业网站建设,写一篇软文1000字,太原做网站公司哪家好#x1f525;个人主页#x1f525;#xff1a;孤寂大仙V #x1f308;收录专栏#x1f308;#xff1a;计算机网络 #x1f339;往期回顾#x1f339;#xff1a; #x1f516;流水不争#xff0c;争的是滔滔不息 一、epoll实现多路转接
epoll是Linux内核提供的一种… 个人主页孤寂大仙V 收录专栏计算机网络 往期回顾 流水不争争的是滔滔不息 一、epoll实现多路转接
epoll是Linux内核提供的一种高性能I/O事件通知机制用于处理大量文件描述符的I/O事件。它是对传统select/poll模型的改进避免了线性扫描文件描述符的开销适合高并发场景。
epoll的参数
epoll_create #include sys/epoll.hint epoll_create(int size);用于创建 epoll 实例返回一个 epoll 文件描述符fd用于后续的事件管理。 size一个提示参数建议内核预期的文件描述符数量。作用告诉内核大致需要管理的 fd 数量帮助内核分配初始内存。 返回值 成功返回 epoll 文件描述符 0如你的 _epfd。 失败返回 -1并设置 errno。 epoll_ctl int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);epoll_ctl 用于管理 epoll 实例中的文件描述符添加、修改、删除。 epfd epoll 实例的文件描述符由 epoll_create 或 epoll_create1 返回。 op 操作类型控制对 fd 的操作 EPOLL_CTL_ADD将 fd 添加到 epoll 实例的红黑树注册关注的事件。 EPOLL_CTL_MOD修改 fd 的事件或用户数据。 EPOLL_CTL_DEL从红黑树中移除 fd。 fd 要操作的文件描述符。 event 指向 struct epoll_event 的指针定义 fd 的事件和用户数据。 结构定义
struct epoll_event {uint32_t events; // 事件类型epoll_data_t data; // 用户数据
};
union epoll_data_t {void *ptr; // 指针int fd; // 文件描述符uint32_t u32; // 32 位整数uint64_t u64; // 64 位整数
};events 可选值可组合 EPOLLINfd 可读例如 socket 有数据或新连接。 EPOLLOUTfd 可写。 EPOLLERRfd 发生错误。 EPOLLHUPfd 被挂起例如 socket 关闭。 EPOLLET启用边沿触发ET模式默认是水平触发 LT。 EPOLLONESHOT事件只触发一次需重新注册。返回值 成功返回 0。 失败返回 -1并设置 errno epoll_wait int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);epoll_wait 等待 epoll 实例中的就绪事件并将就绪事件复制到用户空间。 epfd epoll 实例的文件描述符。 events 指向 struct epoll_event 数组的指针用于存储内核返回的就绪事件。 内核将就绪队列中的事件复制到这个数组每个元素包含 events触发的事件类型如 EPOLLIN、EPOLLERR。 data用户数据如 data.fd。 maxevents 数组 events 能容纳的最大事件数必须大于 0。 timeout 等待的超时时间毫秒 -1无限等待阻塞直到有事件。 0立即返回非阻塞。 0等待指定毫秒数。 返回值 成功返回就绪事件数量0 表示超时 0 表示有事件。 失败返回 -1并设置 errno
epoll的原理
epoll_create 会创建红黑树和就绪队列。 红黑树用于存储所有通过 epoll_ctl 注册的 fd 及其事件。 就绪队列用于存储已经就绪的事件供 epoll_wait 获取。 红黑树
作用红黑树是一个高效的平衡二叉搜索树用于存储和管理所有被监控的 fd 及其关联的事件。初始化在调用 epoll_create 时内核会为 epoll 实例分配一个红黑树初始为空。用途通过 epoll_ctl(EPOLL_CTL_ADD) 添加 fd 时fd 和事件信息struct epoll_event被插入红黑树内核通过红黑树高效管理插入、删除、查找复杂度为 O(log n)。
红黑树的本质是用户告诉内核你要帮我关心哪个一个fd哪一个事件。 就绪队列
作用就绪队列是一个双向链表用于存储已经触发的事件即 fd 上有用户关注的事件如新连接或数据可读。初始化epoll_create 初始化一个空的就绪队列等待事件触发时由内核填充。用途当 fd 上的事件触发例如 socket 收到数据内核通过回调函数将该 fd 的事件加入就绪队列epoll_wait 直接从这个队列获取就绪事件。
就绪队列的本质是内核告诉用户哪一个fd上面的哪些事件已经就绪了。 在这个就绪队列中检测是否有fd就绪事件复杂度是O(1)获取就绪事件的事件复杂度是O(n)。 红黑树中添加fd和事件时内核会为该 fd 注册一个回调函数但是此时不会将fd加入就绪队列。当事件触发(listen 套接字上有新连接到来)时回调函数才会将fd事件加入就绪队列。
[epoll_ctl 添加 fd 和事件]↓[红黑树维护 fd 状态]↓
[事件真正发生 → 回调触发 → 加入就绪队列]↓[epoll_wait 获取就绪事件处理]这时候我们发现epoll_create的返回值怎么是一个fd它不应该是一个红黑树吗为什么叫fd
这其实是对内核中 epoll 实现机制的一种描述方式不是说你创建的红黑树叫“fd”而是epoll_ctl调用时内核会把你要监控的 fd 加入到一棵红黑树结构中维护。这棵红黑树的作用是快速查找/修改/删除 被监控的 fd 对应的事件信息。 系统就会把 fd 作为一个 key加进 epoll 实例内部维护的这棵红黑树中。它不是真正“叫 fd 的红黑树”而是以 fd 为索引的红黑树用于管理大量的监听对象。
就绪队列就像是基于事件就绪的生产者消费者模型epoll接口是线程安全的。
epoll的优点
接口使用方便: 虽然拆分成了三个函数, 但是反而使用起来更方便高效.不需要每次循环都设置关注的文件描述符, 也做到了输入输出参数分离开。数据拷贝轻量: 只在合适的时候调用 EPOLL_CTL_ADD 将文件描述符结构拷贝到内核中, 这个操作并不频繁(而 select/poll 都是每次循环都要进行拷贝)。事件回调机制: 避免使用遍历, 而是使用回调函数的方式, 将就绪的文件描述符。结构加入到就绪队列中, epoll_wait 返回直接访问就绪队列就知道哪些文件描述符就绪. 这个操作时间复杂度 O(1). 即使文件描述符数目很多, 效率也不会受到影响。没有数量限制: 文件描述符数目无上限。
epoll的工作方式LT(水平触发)与ET(边缘触发)
LT水平触发 只要文件描述符fd处于就绪状态如可读或可写就会持续通知用户程序直到事件被处理。 ET边缘触发 其核心特点是仅在状态变化如从无数据到有数据时触发一次事件通知后续状态不变则不再重复通知。
水平触发(LT)只要条件满足(如缓冲区有数据)持续触发事件。边缘触发(ET)仅在条件变化(如空缓冲区变为非空)时触发一次需一次性处理完所有数据。
LT默认、友好、容错
只要内核检测到 fd 是就绪的就会反复通知哪怕你没一次性把数据读完。哪怕你处理得不彻底它下次还会提醒你“喂还有数据没处理呢” 假设你一次 recv() 只读了一半数据LT 会继续告诉你这个 fd 可读你下次还可以继续读。
ET极致、狠、效率高
只有状态从“无”到“有”的那一刻才会通知你。-如果你没一次性处理干净下次就永远不会再通知你你就错过了数据程序直接出 BUG 内核有新数据时调用一次 recv() 读了一点但还有数据没读完。如果你这次没把数据都读光下次 ET 不会再通知你这个 fd 可读了
ET 模式本质上就是一种“倒逼程序员认真写代码、处理彻底”的机制。 让你不能偷懒你必须写出健壮、完整、循环处理的逻辑否则你就会错过数据、错过连接、错过人生不是。勇敢面对ET的“倒逼”写出不漏不炸的代码。
那为啥要用 ET 呢 因为它效率高通知次数少系统开销更低适合高并发场景。常见于epoll 非阻塞IO ET 模式配合使用。
LT 更像是老妈提醒你“吃饭了”没吃她还会再喊ET 更像是高铁广播错过了就完蛋。 用ET模式要把文件描述符设置为非阻塞读数据要一直读写数据要一直写。
为什么ET模式文件描述符要设置为非阻塞
在ET模式下epoll通知一次后程序必须尽可能读取或写入所有可用数据直到无数据可读或无空间可写为止。如果文件描述符是阻塞的第一次读/写操作可能会因为缓冲区满或空而阻塞导致程序无法继续处理其他事件降低效率。 非阻塞模式允许程序在读/写操作返回EAGAIN或EWOULDBLOCK表示暂时无数据或无法写入时立即返回从而继续处理其他事件或逻辑。 如果不设置为非阻塞程序可能卡在某个文件描述符的读/写操作上无法及时响应其他文件描述符的事件这与ET模式高效、快速处理的设计初衷相悖。 示例场景 假设一个socket上有100字节数据到达ET模式只会通知一次。如果文件描述符是阻塞的程序可能只读取部分数据比如50字节后阻塞无法处理其他事件。而非阻塞模式下程序可以反复读取直到返回EAGAIN确保处理完所有数据。
为什么读数据要一直读写数据要一直写 在ET模式下epoll只在事件状态发生变化时触发一次通知因此程序必须在这一次通知中处理完所有可用的数据或完成所有可能的写操作否则可能错过后续数据或导致数据堆积。
读取数据时的while循环 当epoll通知可读事件时程序需要反复调用read或recv函数直到读取到EAGAIN或EWOULDBLOCK表示当前没有更多数据可读。如果不使用while循环只读取一次可能只读到部分数据例如缓冲区一次只能读1024字节但实际有4096字节数据。后续数据不会再次触发epoll事件导致数据丢失或处理不完整。
while (1) {ssize_t count read(fd, buffer, sizeof(buffer));if (count -1) {if (errno EAGAIN || errno EWOULDBLOCK) {// 缓冲区已读完break;}// 其他错误处理perror(read);break;} else if (count 0) {// 对端关闭连接break;}// 处理读取到的count字节数据
}写入数据时的while循环 类似地当epoll通知可写事件时程序需要反复调用write或send函数直到所有数据写入完成或返回EAGAIN表示发送缓冲区已满。 如果不使用while循环只写入一次可能只写出部分数据例如发送缓冲区一次只接受1024字节但实际要发送4096字节。后续写入可能不会再次触发epoll事件导致数据发送不完整。
while (data_to_write 0) {ssize_t count write(fd, buffer, data_to_write);if (count -1) {if (errno EAGAIN || errno EWOULDBLOCK) {// 缓冲区满等待下次可写事件break;}// 其他错误处理perror(write);break;}data_to_write - count;buffer count;
}ET模式的特性要求 ET模式假定程序在收到事件通知后会“榨干”文件描述符的处理能力读完所有可读数据或写完所有可写数据。如果不通过while循环处理可能会导致数据丢失或写操作不完整因为后续事件可能不会再次触发。相比之下LT模式会持续触发事件直到条件不再满足例如缓冲区无数据或可写因此LT模式对while循环的依赖较低但效率也低于ET模式。
二、代码简单实现epoll高并发服务器
#pragma once
#include Common.hpp
#include Socket.hpp
#include Log.hpp
#include sys/epoll.husing namespace std;
using namespace LogModule;
using namespace SocketModule;class epollserver
{
const static int size 4096;
const static int defaulted -1;
public:epollserver(int port):_listensockfd(make_uniqueTcpSocket()),_isrunning(false),_epfd(defaulted){_listensockfd-BuildTcpSocketServer(port); //构造TCP服务器_epfdepoll_create(256); //创建epoll模型if(_epfd0){LOG(LogLevel::FATAL)epoll_create error;exit(EPOLL_CREATE_ERR);}LOG(LogLevel::INFO)epoll_create success;epoll_event ev; //创建对象ev.eventsEPOLLIN;//关心读事件ev.data.fd_listensockfd-FD();//listen套接字int nepoll_ctl(_epfd,EPOLL_CTL_ADD,_listensockfd-FD(),ev); //设置进内核关系读事件if(n0){LOG(LogLevel::FATAL)epoll_ctl error;exit(EPOLL_CTL_ERR);}LOG(LogLevel::INFO)epoll_ctl listen_sockfd success;}void Start() //启动服务器{int timeout-1;_isrunningtrue;while(_isrunning){int nepoll_wait(_epfd,_revs,size,timeout); //n就是等待队列里有几个fd就绪了switch (n){case -1:LOG(LogLevel::FATAL)epoll error;break;case 0:LOG(LogLevel::DEBUG)epoll timeout;break;default:LOG(LogLevel::INFO)读事件就绪;Distribute(n);//事件派发break;}}}void Distribute(int rnum){for(int i0;irnum;i){if(_revs[i].events EPOLLIN) //读事件就绪{if(_revs[i].data.fd_listensockfd-FD()){//listen 套接字Accept();}else{//普通 套接字Recv(i);}}}}void Accept(){InetAddr client;int sockfd_listensockfd-AcceptOrDie(client);if(sockfd0){LOG(LogLevel::DEBUG)收到一个客户端连接client.StringAddr();epoll_event ev;ev.eventsEPOLLIN;ev.data.fdsockfd;int nepoll_ctl(_epfd,EPOLL_CTL_ADD,sockfd,ev);//把accept的套接字设置进内核if(n0){LOG(LogLevel::FATAL)epoll_ctl error;exit(EPOLL_CTL_ERR);}LOG(LogLevel::INFO)epoll_ctl accept_sockfd success;}} void Recv(int i){char buffer[1024];int nrecv(_revs[i].data.fd,buffer,sizeof(buffer)-1,0);if(n0){buffer[n]0;coutclient say-buffer;}else if(n0) //退出{LOG(LogLevel::WARNING)client quit;int mepoll_ctl(_epfd,EPOLL_CTL_DEL,_revs[i].data.fd,nullptr);if(m0){LOG(LogLevel::INFO)epoll_ctl remove sockfd success:_revs[i].data.fd;}close(_revs[i].data.fd);}else //异常{LOG(LogLevel::FATAL)recv error;int mepoll_ctl(_epfd,EPOLL_CTL_DEL,_revs[i].data.fd,nullptr);if(m0){LOG(LogLevel::INFO)epoll_ctl remove sockfd success:_revs[i].data.fd;}close(_revs[i].data.fd);}}~epollserver(){ _listensockfd-Close();if (_epfd 0)close(_epfd);}
private:unique_ptrSocket _listensockfd;struct epoll_event _revs[size];bool _isrunning;int _epfd;
};构造函数: 创建 socket、绑定监听、创建 epoll 实例、注册 listen 套接字读事件 Start(): 进入事件循环使用 epoll_wait 阻塞等待事件 Distribute(): 对每个就绪事件分类处理新连接 or 客户端发消息 Accept(): 接收新连接并将其注册到 epoll Recv(): 接收数据并输出 / 清理关闭连接 析构函数清理监听套接字 epoll fd 构造函数 注册事件
//私有成员变量
private:unique_ptrSocket _listensockfd;struct epoll_event _revs[size];bool _isrunning;int _epfd;
//构造epollserver(int port):_listensockfd(make_uniqueTcpSocket()),_isrunning(false),_epfd(defaulted){_listensockfd-BuildTcpSocketServer(port); //构造TCP服务器_epfdepoll_create(256); //创建epoll模型if(_epfd0){LOG(LogLevel::FATAL)epoll_create error;exit(EPOLL_CREATE_ERR);}LOG(LogLevel::INFO)epoll_create success;epoll_event ev; //创建对象ev.eventsEPOLLIN;//关心读事件ev.data.fd_listensockfd-FD();//listen套接字int nepoll_ctl(_epfd,EPOLL_CTL_ADD,_listensockfd-FD(),ev); //设置进内核关系读事件if(n0){LOG(LogLevel::FATAL)epoll_ctl error;exit(EPOLL_CTL_ERR);}LOG(LogLevel::INFO)epoll_ctl listen_sockfd success;}通过之前文章的模板方法模式的Tcp服务器父类托管子类构造TCP服务器。通过epoll_create创建epoll模型(红黑树和就绪队列)返回这个epoll_create的套接字_epfd。接下来就用epoll_ctl把关系的fd和关心的事件设置进内核epoll_ctl的参数第一个参数是epoll模型的fd、第二个参数EPOLL_CTL_ADD把 fd 添加到 epoll 实例的红黑树注册关注的事件、第三个参数是把要关注的fd(要监听哪个fd)添加进去、第四个参数设置关心的事件(是读事件还是写事件)和当这个事件被触发的时候返回的epoll_event中你能知道“那个fd触发了”是内核返回给用户的。 启动服务阻塞等待事件
void Start() //启动服务器{int timeout-1;_isrunningtrue;while(_isrunning){int nepoll_wait(_epfd,_revs,size,timeout); //n就是等待队列里有几个fd就绪了switch (n){case -1:LOG(LogLevel::FATAL)epoll error;break;case 0:LOG(LogLevel::DEBUG)epoll timeout;break;default:LOG(LogLevel::INFO)读事件就绪;Distribute(n);//事件派发break;}}}epoll_wait方法 就是看就绪队列中有没有就绪事件。epoll_wait的返回值就是有多少个事件就绪。对返回值进行判断n0是读事件就绪进行事件派发。 事件派发(对不同就绪事件分类处理新连接 or 客户端发消息
void Distribute(int rnum){for(int i0;irnum;i){if(_revs[i].events EPOLLIN) //读事件就绪{if(_revs[i].data.fd_listensockfd-FD()){//listen 套接字Accept();}else{//普通 套接字Recv(i);}}}}传过来的参数是epoll_wait的返回值表示有几个事件就绪epoll 的就绪队列中的就绪 fd 是顺序排列的。遍历struct epoll_event _revs[size];(是就绪队列)判断是否是读事件就绪然后判断是监听套接字(新连接)还是accept套接字(普通套接字)。 接收新连接并将其注册到 epoll
void Accept(){InetAddr client;int sockfd_listensockfd-AcceptOrDie(client);if(sockfd0){LOG(LogLevel::DEBUG)收到一个客户端连接client.StringAddr();epoll_event ev;ev.eventsEPOLLIN;ev.data.fdsockfd;int nepoll_ctl(_epfd,EPOLL_CTL_ADD,sockfd,ev);//把accept的套接字设置进内核if(n0){LOG(LogLevel::FATAL)epoll_ctl error;exit(EPOLL_CTL_ERR);}LOG(LogLevel::INFO)epoll_ctl accept_sockfd success;}} 接收客户端连接接着用epoll_ctl把这个新的套接字设置进内核(epoll)。 接收数据并输出
void Recv(int i){char buffer[1024];int nrecv(_revs[i].data.fd,buffer,sizeof(buffer)-1,0);if(n0){buffer[n]0;coutclient say-buffer;}else if(n0) //退出{LOG(LogLevel::WARNING)client quit;int mepoll_ctl(_epfd,EPOLL_CTL_DEL,_revs[i].data.fd,nullptr);if(m0){LOG(LogLevel::INFO)epoll_ctl remove sockfd success:_revs[i].data.fd;}close(_revs[i].data.fd);}else //异常{LOG(LogLevel::FATAL)recv error;int mepoll_ctl(_epfd,EPOLL_CTL_DEL,_revs[i].data.fd,nullptr);if(m0){LOG(LogLevel::INFO)epoll_ctl remove sockfd success:_revs[i].data.fd;}close(_revs[i].data.fd);}}接收数据recv返回值0是客户端退出返回值0是出现异常。要对注册到epoll模型中的fd进行清理然后关闭文件描述符。
三、源码epoll实现非阻塞服务器
文章转载自: http://www.morning.nwrzf.cn.gov.cn.nwrzf.cn http://www.morning.rbtny.cn.gov.cn.rbtny.cn http://www.morning.mlyq.cn.gov.cn.mlyq.cn http://www.morning.qxjck.cn.gov.cn.qxjck.cn http://www.morning.dhtdl.cn.gov.cn.dhtdl.cn http://www.morning.lztrt.cn.gov.cn.lztrt.cn http://www.morning.qbgff.cn.gov.cn.qbgff.cn http://www.morning.jcfdk.cn.gov.cn.jcfdk.cn http://www.morning.knqck.cn.gov.cn.knqck.cn http://www.morning.rcjwl.cn.gov.cn.rcjwl.cn http://www.morning.pwmm.cn.gov.cn.pwmm.cn http://www.morning.lhxdq.cn.gov.cn.lhxdq.cn http://www.morning.jqbmj.cn.gov.cn.jqbmj.cn http://www.morning.mzwfw.cn.gov.cn.mzwfw.cn http://www.morning.nqyfm.cn.gov.cn.nqyfm.cn http://www.morning.hxbjt.cn.gov.cn.hxbjt.cn http://www.morning.wngpq.cn.gov.cn.wngpq.cn http://www.morning.webpapua.com.gov.cn.webpapua.com http://www.morning.gtqws.cn.gov.cn.gtqws.cn http://www.morning.spfq.cn.gov.cn.spfq.cn http://www.morning.rxpp.cn.gov.cn.rxpp.cn http://www.morning.hqllj.cn.gov.cn.hqllj.cn http://www.morning.inheatherskitchen.com.gov.cn.inheatherskitchen.com http://www.morning.pgfkl.cn.gov.cn.pgfkl.cn http://www.morning.lyldhg.cn.gov.cn.lyldhg.cn http://www.morning.knscf.cn.gov.cn.knscf.cn http://www.morning.mswkd.cn.gov.cn.mswkd.cn http://www.morning.pcbfl.cn.gov.cn.pcbfl.cn http://www.morning.qmwzr.cn.gov.cn.qmwzr.cn http://www.morning.ndynz.cn.gov.cn.ndynz.cn http://www.morning.etsaf.com.gov.cn.etsaf.com http://www.morning.gwtbn.cn.gov.cn.gwtbn.cn http://www.morning.daxifa.com.gov.cn.daxifa.com http://www.morning.ztfzm.cn.gov.cn.ztfzm.cn http://www.morning.fdrwk.cn.gov.cn.fdrwk.cn http://www.morning.rkhhl.cn.gov.cn.rkhhl.cn http://www.morning.brtxg.cn.gov.cn.brtxg.cn http://www.morning.qqrlz.cn.gov.cn.qqrlz.cn http://www.morning.hqpyt.cn.gov.cn.hqpyt.cn http://www.morning.fwkjp.cn.gov.cn.fwkjp.cn http://www.morning.mdfxn.cn.gov.cn.mdfxn.cn http://www.morning.xsqbx.cn.gov.cn.xsqbx.cn http://www.morning.mjqms.cn.gov.cn.mjqms.cn http://www.morning.rdwm.cn.gov.cn.rdwm.cn http://www.morning.ktrzt.cn.gov.cn.ktrzt.cn http://www.morning.pngdc.cn.gov.cn.pngdc.cn http://www.morning.nzqqd.cn.gov.cn.nzqqd.cn http://www.morning.osshjj.cn.gov.cn.osshjj.cn http://www.morning.crfjj.cn.gov.cn.crfjj.cn http://www.morning.ljllt.cn.gov.cn.ljllt.cn http://www.morning.ybqlb.cn.gov.cn.ybqlb.cn http://www.morning.rlhjg.cn.gov.cn.rlhjg.cn http://www.morning.hsrch.cn.gov.cn.hsrch.cn http://www.morning.znkls.cn.gov.cn.znkls.cn http://www.morning.hcwjls.com.gov.cn.hcwjls.com http://www.morning.mrnnb.cn.gov.cn.mrnnb.cn http://www.morning.nykzl.cn.gov.cn.nykzl.cn http://www.morning.rjrz.cn.gov.cn.rjrz.cn http://www.morning.whnps.cn.gov.cn.whnps.cn http://www.morning.mydgr.cn.gov.cn.mydgr.cn http://www.morning.ndfwh.cn.gov.cn.ndfwh.cn http://www.morning.jxltk.cn.gov.cn.jxltk.cn http://www.morning.knpbr.cn.gov.cn.knpbr.cn http://www.morning.mkygc.cn.gov.cn.mkygc.cn http://www.morning.qynnw.cn.gov.cn.qynnw.cn http://www.morning.bkpbm.cn.gov.cn.bkpbm.cn http://www.morning.bpmnx.cn.gov.cn.bpmnx.cn http://www.morning.dqpd.cn.gov.cn.dqpd.cn http://www.morning.ykxnp.cn.gov.cn.ykxnp.cn http://www.morning.nwjzc.cn.gov.cn.nwjzc.cn http://www.morning.mrnnb.cn.gov.cn.mrnnb.cn http://www.morning.yrskc.cn.gov.cn.yrskc.cn http://www.morning.llllcc.com.gov.cn.llllcc.com http://www.morning.yrjym.cn.gov.cn.yrjym.cn http://www.morning.bxrqf.cn.gov.cn.bxrqf.cn http://www.morning.xwlhc.cn.gov.cn.xwlhc.cn http://www.morning.dqrpz.cn.gov.cn.dqrpz.cn http://www.morning.tqpr.cn.gov.cn.tqpr.cn http://www.morning.tjwfk.cn.gov.cn.tjwfk.cn http://www.morning.dnycx.cn.gov.cn.dnycx.cn