最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java?IO多路復(fù)用之select、poll、epoll示例詳解

 更新時間:2026年05月11日 09:34:22   作者:程序員懶洋洋  
IO多路復(fù)用是解決高并發(fā)IO的核心技術(shù),select、poll、epoll是Linux系統(tǒng)下三種主流的多路復(fù)用機制,這篇文章主要介紹了Java IO多路復(fù)用之select、poll、epoll的相關(guān)資料,文中介紹的非常詳細(xì),需要的朋友可以參考下

一、整體知識體系:用戶態(tài)、內(nèi)核態(tài)、IO 多路復(fù)用

在理解 select、poll、epoll 之前,先建立一條主線:

用戶態(tài):我們寫的 Java 代碼運行的地方
內(nèi)核態(tài):操作系統(tǒng)內(nèi)核運行的地方
IO 多路復(fù)用:用戶態(tài)線程通過系統(tǒng)調(diào)用,讓內(nèi)核幫忙監(jiān)聽多個 socket 的 IO 事件

在 Java 后端里,我們寫的代碼,比如 NIO、Netty、業(yè)務(wù)邏輯,都是運行在用戶態(tài)。

但是網(wǎng)絡(luò) IO 的底層,比如 TCP 協(xié)議棧、socket 緩沖區(qū)、網(wǎng)卡數(shù)據(jù)接收,這些都由操作系統(tǒng)內(nèi)核管理,也就是在內(nèi)核態(tài)完成。

所以用戶程序不能直接判斷某個 socket 有沒有數(shù)據(jù),它需要通過系統(tǒng)調(diào)用進(jìn)入內(nèi)核態(tài),讓內(nèi)核幫它檢查。

一句話總結(jié):

IO 多路復(fù)用就是一個用戶線程通過 select、poll、epoll 這樣的系統(tǒng)調(diào)用,讓內(nèi)核同時監(jiān)聽多個 fd,哪個 fd 就緒了,用戶線程就處理哪個 fd。

二、fd 是什么?

fd 全稱是:

File Descriptor
文件描述符

它是操作系統(tǒng)給當(dāng)前進(jìn)程打開的 IO 資源分配的一個編號。

在 Linux 里,很多東西都被抽象成文件,比如:

普通文件
socket 連接
管道
標(biāo)準(zhǔn)輸入
標(biāo)準(zhǔn)輸出
標(biāo)準(zhǔn)錯誤

所以這些資源都會有 fd。

例如:

標(biāo)準(zhǔn)輸入 stdin   -> fd = 0
標(biāo)準(zhǔn)輸出 stdout  -> fd = 1
標(biāo)準(zhǔn)錯誤 stderr  -> fd = 2
某個 socket 連接 -> fd = 3
另一個 socket 連接 -> fd = 4
某個日志文件     -> fd = 5

所以要注意:

fd 不是連接數(shù)量本身,而是操作系統(tǒng)給某個 IO 資源分配的編號。

但是在網(wǎng)絡(luò)編程里,一個 socket 通常對應(yīng)一個 fd,所以很多時候我們說“監(jiān)聽多個 fd”,本質(zhì)上就是監(jiān)聽多個 socket 連接。

三、fd 和 socket 連接的關(guān)系

在服務(wù)端網(wǎng)絡(luò)編程里,一般有兩類 socket。

1. 監(jiān)聽 socket

服務(wù)端啟動時,會創(chuàng)建一個監(jiān)聽端口的 socket。

比如服務(wù)端監(jiān)聽 8080 端口:

ServerSocketChannel 監(jiān)聽 8080
listenFd = 3

這個 listenFd 是監(jiān)聽 socket 的 fd。

它的作用是接收新的客戶端連接,不代表某一個具體客戶端連接。

2. 已連接 socket

當(dāng)客戶端連接進(jìn)來之后,服務(wù)端通過 accept() 得到一個新的 socket。

例如:

客戶端 A 連接進(jìn)來 -> connFd = 4
客戶端 B 連接進(jìn)來 -> connFd = 5
客戶端 C 連接進(jìn)來 -> connFd = 6

這些 fd 才分別對應(yīng)一個個客戶端連接。

所以服務(wù)端中可能是這樣的:

fd = 3:監(jiān)聽 socket,負(fù)責(zé)接收新連接
fd = 4:客戶端 A 的連接 socket
fd = 5:客戶端 B 的連接 socket
fd = 6:客戶端 C 的連接 socket

面試時可以這樣說:

fd 是操作系統(tǒng)層面對 IO 資源的編號。一個 socket 通常對應(yīng)一個 fd;在服務(wù)端中,監(jiān)聽 socket 有一個監(jiān)聽 fd,每 accept 一個客戶端連接,內(nèi)核會為這個已連接 socket 分配一個新的 fd。

四、IO 多路復(fù)用監(jiān)聽的到底是什么?

嚴(yán)格來說,IO 多路復(fù)用監(jiān)聽的是:

多個 fd 上的 IO 就緒事件

它不是直接監(jiān)聽“連接個數(shù)”。

但是在網(wǎng)絡(luò)編程場景中,大多數(shù) fd 都是 socket fd,所以可以近似理解為:

監(jiān)聽多個 fd
≈ 監(jiān)聽多個 socket
≈ 監(jiān)聽多個客戶端連接

更嚴(yán)謹(jǐn)?shù)谋磉_(dá)是:

IO 多路復(fù)用監(jiān)聽的是多個文件描述符 fd 的 IO 就緒事件。在網(wǎng)絡(luò)編程里,一個已連接 socket 通常對應(yīng)一個 fd,所以監(jiān)聽多個 fd 就相當(dāng)于監(jiān)聽多個 socket 連接。

五、BIO 和 IO 多路復(fù)用的區(qū)別

假設(shè)服務(wù)端有三個客戶端連接:

客戶端 A -> fd = 4
客戶端 B -> fd = 5
客戶端 C -> fd = 6

1. BIO 的處理方式

BIO 通常是:

一個連接對應(yīng)一個線程

例如:

線程 1 阻塞等待 fd=4 的數(shù)據(jù)
線程 2 阻塞等待 fd=5 的數(shù)據(jù)
線程 3 阻塞等待 fd=6 的數(shù)據(jù)

如果連接數(shù)很多,線程數(shù)量就會爆炸。

這會帶來幾個問題:

線程占用內(nèi)存
線程上下文切換成本高
大量線程阻塞在 IO 上
CPU 利用率不高
并發(fā)擴(kuò)展能力差

2. IO 多路復(fù)用的處理方式

IO 多路復(fù)用是一個線程調(diào)用 selectpoll 或 epoll,把多個 fd 交給內(nèi)核監(jiān)聽:

一個線程監(jiān)聽 fd=4、fd=5、fd=6

內(nèi)核負(fù)責(zé)檢查:

fd=4 有沒有數(shù)據(jù)?
fd=5 有沒有數(shù)據(jù)?
fd=6 有沒有數(shù)據(jù)?

如果某一刻:

fd=5 有數(shù)據(jù)可讀
fd=6 有數(shù)據(jù)可讀

內(nèi)核就通知用戶態(tài)線程:

fd=5 就緒了
fd=6 就緒了

然后用戶態(tài)線程再去處理對應(yīng)連接的數(shù)據(jù)。

核心區(qū)別是:

BIO 是一個線程阻塞在一個 socket 上;IO 多路復(fù)用是一個線程阻塞在多路復(fù)用器上,讓內(nèi)核幫它監(jiān)聽多個 socket。

六、select 的工作原理

select 是比較早期的 IO 多路復(fù)用機制。

它的核心流程是:

1. 用戶態(tài)應(yīng)用程序準(zhǔn)備一批 fd 集合
2. 通過 select 系統(tǒng)調(diào)用把 fd 集合傳給內(nèi)核
3. 內(nèi)核遍歷 fd 集合,檢查哪些 fd 就緒
4. 內(nèi)核把結(jié)果寫回用戶傳入的數(shù)據(jù)結(jié)構(gòu)
5. select 返回用戶態(tài)
6. 用戶態(tài)程序再次遍歷 fd 集合,找出真正就緒的 fd
7. 處理對應(yīng)連接

例如用戶態(tài)有這些 fd:

fd = 3
fd = 4
fd = 5
fd = 6
fd = 7

應(yīng)用程序調(diào)用 select 后,內(nèi)核遍歷這些 fd,發(fā)現(xiàn):

fd = 4 可讀
fd = 7 可讀

select 返回后,用戶態(tài)程序還要自己再遍歷一遍 fd 集合,才能知道到底是 fd=4 和 fd=7 就緒了。

select 的缺點

1. fd 數(shù)量有限制

select 使用 fd_set 保存 fd 集合,常見限制是 1024。

也就是說,一個 select 能監(jiān)聽的 fd 數(shù)量通常有限。

2. 每次調(diào)用都要重復(fù)傳遞 fd 集合

每次調(diào)用 select,都要把 fd 集合從用戶態(tài)拷貝到內(nèi)核態(tài)。

連接數(shù)量越多,這個拷貝成本越高。

3. 內(nèi)核和用戶態(tài)都要遍歷所有 fd

即使只有一個 fd 就緒,內(nèi)核也要遍歷所有 fd。

返回用戶態(tài)后,用戶程序還要再遍歷所有 fd。

所以時間復(fù)雜度是:

O(n)

一句話總結(jié):

select 有 fd 數(shù)量限制,每次調(diào)用都要傳入全部 fd,內(nèi)核和用戶態(tài)都要遍歷全部 fd。

七、poll 的工作原理

poll 的整體思想和 select 很像,也是:

用戶態(tài)把一批 fd 交給內(nèi)核
內(nèi)核檢查哪些 fd 就緒
返回后用戶態(tài)再遍歷

不同點是:

poll 不再使用固定大小的 fd_set,而是使用 pollfd 數(shù)組,因此解決了 select 的 fd 數(shù)量限制問題。

pollfd 可以簡單理解為:

fd:要監(jiān)聽哪個連接
events:關(guān)心什么事件,比如可讀、可寫
revents:內(nèi)核返回實際發(fā)生了什么事件

poll 的流程是:

1. 用戶態(tài)準(zhǔn)備 pollfd 數(shù)組
2. 調(diào)用 poll 進(jìn)入內(nèi)核態(tài)
3. 內(nèi)核遍歷 pollfd 數(shù)組,檢查每個 fd 是否就緒
4. 內(nèi)核把結(jié)果寫到 revents 字段
5. poll 返回用戶態(tài)
6. 用戶態(tài)遍歷 pollfd 數(shù)組,找出 revents 有事件的 fd

poll 的優(yōu)點

相比 select,poll 的主要優(yōu)點是:

解決了 fd 數(shù)量限制問題

poll 的缺點

但是 poll 沒有解決核心性能問題:

每次調(diào)用仍然要傳遞全部 fd
內(nèi)核仍然要遍歷全部 fd
返回用戶態(tài)后仍然要遍歷全部 fd

所以 poll 的時間復(fù)雜度仍然是:

O(n)

一句話總結(jié):

poll 解決了 select 的 fd 數(shù)量限制,但沒有解決全量傳遞和全量遍歷的問題。

八、epoll 的工作原理

epoll 是 Linux 對 IO 多路復(fù)用的優(yōu)化,更適合高并發(fā)場景。

它和 selectpoll 最大的區(qū)別是:

select/poll 是每次調(diào)用都把全部 fd 傳給內(nèi)核;epoll 是先把 fd 注冊到內(nèi)核,后續(xù)只等待就緒事件。

epoll 的三個核心系統(tǒng)調(diào)用

epoll_create:創(chuàng)建 epoll 實例
epoll_ctl:添加、修改、刪除要監(jiān)聽的 fd
epoll_wait:等待就緒事件

epoll 的流程

1. 用戶態(tài)調(diào)用 epoll_create 創(chuàng)建 epoll 對象
2. 用戶態(tài)調(diào)用 epoll_ctl 把 socket fd 注冊到內(nèi)核
3. 內(nèi)核維護(hù)這些 fd 的監(jiān)聽關(guān)系
4. 當(dāng)某個 fd 就緒時,內(nèi)核把它放入就緒隊列
5. 用戶態(tài)調(diào)用 epoll_wait 等待事件
6. epoll_wait 返回的就是已經(jīng)就緒的 fd
7. 用戶態(tài)程序直接處理這些就緒 fd

假設(shè)服務(wù)端維護(hù) 10000 個連接,但只有 10 個連接有數(shù)據(jù)。

select/poll 的做法是:

遍歷 10000 個 fd,找到 10 個就緒 fd

epoll 的做法是:

內(nèi)核把 10 個就緒 fd 放入就緒隊列
epoll_wait 直接返回這 10 個 fd

所以 epoll 更適合:

大量連接
少量活躍
高并發(fā)網(wǎng)絡(luò)服務(wù)

九、epoll 為什么性能更好?

epoll 性能更好的原因主要有三個。

1. 不需要每次重復(fù)傳遞全部 fd

select/poll 每次調(diào)用都要把 fd 集合從用戶態(tài)傳給內(nèi)核。

epoll 是先通過 epoll_ctl 注冊 fd,之后由內(nèi)核自己維護(hù)這些 fd。

所以后續(xù) epoll_wait 不需要每次重復(fù)傳遞全部 fd。

2. 不需要遍歷全部 fd 找就緒事件

select/poll 返回后,用戶態(tài)程序還要遍歷所有 fd,找出真正就緒的連接。

epoll 中,內(nèi)核會把已經(jīng)就緒的 fd 放到就緒隊列里。

所以 epoll_wait 返回的就是就緒 fd。

3. 監(jiān)聽集合和就緒集合分離

epoll 內(nèi)部可以簡單理解為:

紅黑樹:管理所有注冊的 fd
就緒鏈表:保存已經(jīng)就緒的 fd

紅黑樹用于管理監(jiān)聽集合。

就緒鏈表用于保存已經(jīng)發(fā)生事件的 fd。

這就是 epoll 的核心優(yōu)勢:

監(jiān)聽集合和就緒集合分離。

十、select、poll、epoll 核心區(qū)別對比

對比項selectpollepoll
監(jiān)聽 fd 數(shù)量有限制,常見 1024理論上無固定限制理論上無固定限制
數(shù)據(jù)結(jié)構(gòu)fd_set / bitmappollfd 數(shù)組紅黑樹 + 就緒鏈表
是否每次傳全部 fd否,先注冊后等待
內(nèi)核是否遍歷全部 fd不需要全量遍歷就緒結(jié)果
用戶態(tài)是否遍歷全部 fd只遍歷就緒事件
時間復(fù)雜度O(n)O(n)和就緒 fd 數(shù)量更相關(guān)
適合場景少量連接中等連接高并發(fā)、大量連接、少量活躍

最關(guān)鍵的區(qū)別:

select 有 fd 數(shù)量限制,并且每次都要全量傳遞、全量遍歷;poll 解決了 fd 數(shù)量限制,但仍然要全量傳遞、全量遍歷;epoll 通過事件驅(qū)動和就緒隊列,只返回真正就緒的 fd,更適合高并發(fā)場景。

十一、epoll 的 LT 和 ET 模式

epoll 有兩種觸發(fā)模式:

LT:Level Trigger,水平觸發(fā)
ET:Edge Trigger,邊緣觸發(fā)

1. LT 水平觸發(fā)

LT 是默認(rèn)模式。

特點是:

只要 fd 中還有數(shù)據(jù)沒讀完,epoll_wait 就會一直通知你。

例如 socket 緩沖區(qū)里有 100 字節(jié)數(shù)據(jù),這次只讀了 50 字節(jié),那么下次 epoll_wait 還會繼續(xù)通知這個 fd 可讀。

優(yōu)點:

編程簡單
不容易漏事件

缺點:

可能重復(fù)通知
效率相對低一些

2. ET 邊緣觸發(fā)

ET 模式是:

只有 fd 狀態(tài)發(fā)生變化時才通知一次。

比如從“沒有數(shù)據(jù)”變成“有數(shù)據(jù)”時通知一次。

如果這次沒有把數(shù)據(jù)讀完,后續(xù)不一定繼續(xù)通知。

所以 ET 模式通常要求:

必須配合非阻塞 IO,并且一次性循環(huán)讀到 EAGAIN 為止。

優(yōu)點:

通知次數(shù)少
效率更高

缺點:

編程復(fù)雜
容易漏讀數(shù)據(jù)

面試表達(dá):

LT 模式下,只要數(shù)據(jù)沒讀完,epoll 會反復(fù)通知;ET 模式下,只在狀態(tài)變化時通知一次,所以必須循環(huán)讀到 EAGAIN,否則可能漏事件。ET 性能更高,但編碼要求也更高。

十二、用戶態(tài)和內(nèi)核態(tài)在 select/poll/epoll 中如何配合

select/poll 的用戶態(tài)和內(nèi)核態(tài)流程

用戶態(tài)應(yīng)用程序準(zhǔn)備 fd 集合
        ↓
通過 select/poll 系統(tǒng)調(diào)用進(jìn)入內(nèi)核態(tài)
        ↓
內(nèi)核遍歷全部 fd,檢查哪些 fd 就緒
        ↓
內(nèi)核把結(jié)果寫回用戶傳入的數(shù)據(jù)結(jié)構(gòu)
        ↓
系統(tǒng)調(diào)用返回用戶態(tài)
        ↓
用戶態(tài)程序再次遍歷全部 fd,找出真正就緒的 fd

所以 select/poll 的問題是:

內(nèi)核遍歷一遍,用戶態(tài)返回后還要再遍歷一遍。

epoll 的用戶態(tài)和內(nèi)核態(tài)流程

用戶態(tài)通過 epoll_create 創(chuàng)建 epoll 實例
        ↓
用戶態(tài)通過 epoll_ctl 把 fd 注冊到內(nèi)核
        ↓
內(nèi)核維護(hù)監(jiān)聽集合和就緒隊列
        ↓
用戶態(tài)調(diào)用 epoll_wait 等待事件
        ↓
某些 fd 就緒后,內(nèi)核把它們放入就緒隊列
        ↓
epoll_wait 返回用戶態(tài)
        ↓
用戶態(tài)只處理返回的就緒 fd

所以 epoll 的優(yōu)勢是:

fd 只需要注冊一次,后續(xù) epoll_wait 只返回就緒 fd,減少了重復(fù)拷貝和無意義遍歷。

十三、和 Java NIO 的關(guān)系

Java NIO 中的 Selector 本質(zhì)上是對操作系統(tǒng) IO 多路復(fù)用能力的封裝。

Java 代碼里可能這樣寫:

Selector selector = Selector.open();

while (true) {
    selector.select();

    Set<SelectionKey> keys = selector.selectedKeys();

    for (SelectionKey key : keys) {
        if (key.isReadable()) {
            // 處理讀事件
        }
    }
}

這里的:

selector.select();

底層會通過 JVM native 方法調(diào)用操作系統(tǒng)的多路復(fù)用機制。

在 Linux 下,通常底層就是 epoll。

對應(yīng)關(guān)系可以這樣理解:

Java 層:SocketChannel / ServerSocketChannel
        ↓
JVM native 層:調(diào)用操作系統(tǒng) socket API
        ↓
操作系統(tǒng)層:socket fd
        ↓
Selector 底層:select / poll / epoll

所以:

SocketChannel 是 Java 對 socket fd 的封裝;Selector 是 Java 層的多路復(fù)用器;epoll/select/poll 是操作系統(tǒng)層的多路復(fù)用機制。

十四、完整理解鏈路

可以把 IO 多路復(fù)用理解成下面這條鏈路:

客戶端連接
   ↓
服務(wù)端 accept 得到 socket
   ↓
操作系統(tǒng)給這個 socket 分配 fd
   ↓
Java 的 SocketChannel 封裝這個 fd
   ↓
把 Channel 注冊到 Selector
   ↓
Selector 底層把 fd 注冊給 epoll/select/poll
   ↓
內(nèi)核監(jiān)聽這些 fd 的 IO 狀態(tài)
   ↓
fd 可讀/可寫時,內(nèi)核通知用戶態(tài)
   ↓
Java 線程處理對應(yīng) Channel 的讀寫事件

關(guān)鍵對應(yīng)關(guān)系:

fd:操作系統(tǒng)層面的資源編號
Channel:Java 層面對 fd 的封裝
Selector:Java 層的多路復(fù)用器
epoll/select/poll:操作系統(tǒng)層的多路復(fù)用機制

總結(jié)

到此這篇關(guān)于Java IO多路復(fù)用之select、poll、epoll的文章就介紹到這了,更多相關(guān)IO多路復(fù)用select、poll、epoll內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java并發(fā)編程實例分析

    java并發(fā)編程實例分析

    在本文里我們給大家分享了關(guān)于java并發(fā)編程實例分析以及相關(guān)知識點,需要的朋友們學(xué)習(xí)下。
    2019-03-03
  • Spring Boot 集成 MyBatis 全攻略(最新整理)

    Spring Boot 集成 MyBatis 全攻略(最新整理)

    本文詳解SpringBoot集成MyBatis的流程,涵蓋依賴配置、數(shù)據(jù)源設(shè)置、實體類與Mapper編寫(注解/XML)、Service/Controller開發(fā)及分頁、緩存、動態(tài)SQL等優(yōu)化,助力建設(shè)高效全棧應(yīng)用,感興趣的朋友跟隨小編一起看看吧
    2025-08-08
  • 解決Eclipse/STS中出現(xiàn)Resource is out of sync with the file system的異常問題

    解決Eclipse/STS中出現(xiàn)Resource is out of sync with the file system

    今天小編就為大家分享一篇關(guān)于解決Eclipse/STS中出現(xiàn)Resource is out of sync with the file system的異常問題,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • java 取模與取余的區(qū)別說明

    java 取模與取余的區(qū)別說明

    這篇文章主要介紹了java 取模與取余的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • Kotlin教程之函數(shù)和包的定義和流程控制

    Kotlin教程之函數(shù)和包的定義和流程控制

    這篇文章主要介紹了Kotlin教程之函數(shù)和包的定義和流程控制的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Java爬蟲爬取漫畫示例

    Java爬蟲爬取漫畫示例

    這篇文章主要介紹了Java爬蟲爬取漫畫示例,大部分的爬蟲入門教學(xué)都是爬取圖片的,本文就來測試一下爬取網(wǎng)站的漫畫,需要的朋友可以參考下
    2023-04-04
  • Java8中Stream流求最大值最小值的實現(xiàn)示例

    Java8中Stream流求最大值最小值的實現(xiàn)示例

    本文主要介紹了Java8中Stream流求最大值最小值的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • IDEA的run maven方式啟動步驟詳解

    IDEA的run maven方式啟動步驟詳解

    這篇文章主要介紹了IDEA的run maven方式啟動步驟,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • mybatis水平分表實現(xiàn)動態(tài)表名的項目實例

    mybatis水平分表實現(xiàn)動態(tài)表名的項目實例

    本文主要介紹了mybatis水平分表實現(xiàn)動態(tài)表名的項目實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Java中的接口多繼承機制

    Java中的接口多繼承機制

    大家好,本篇文章主要講的是Java中的接口多繼承機制,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-02-02

最新評論

华池县| 深圳市| 东方市| 图们市| 井研县| 屏东市| 扎兰屯市| 北辰区| 肥西县| 抚州市| 五原县| 白银市| 怀集县| 巨野县| 和林格尔县| 明星| 武穴市| 沁水县| 正安县| 城市| 北辰区| 廊坊市| 祁阳县| 苍溪县| 新宾| 大连市| 建湖县| 林州市| 前郭尔| 勐海县| 九龙县| 天水市| 兴文县| 泰和县| 曲沃县| 阳高县| 九龙城区| 宁乡县| 张家口市| 十堰市| 明星|