kingpub

          海內(nèi)存知己,博客若比鄰

           

          Java Socket編程

          Java Socket編程

          1.Socket傳輸模式


          Sockets有兩種主要的操作方式:面向連接的和無連接的.面向連接的sockets操作就像一部電話,他們必須建立一個連接和一人呼叫.所有的事情在到達(dá)時的順序與它們出發(fā)時的順序時一樣.無連接的sockets操作就像是一個郵件投遞,,沒有什么保證,多個郵件可能在到達(dá)時的順序與出發(fā)時的順序不一樣.

          到底用哪種模式是郵應(yīng)用程序的需要決定的.如果可靠性更重要的話,用面向連接的操作會好一些.比如文件服務(wù)器需要他們的數(shù)據(jù)的正確性和有序性.如果一些數(shù)據(jù)丟失了,系統(tǒng)的有效性將會失去.一些服務(wù)器,比如間歇性地發(fā)送一些數(shù)據(jù)塊.如果數(shù)據(jù)丟了的話,服務(wù)器并不想要再重新發(fā)過一次.因為當(dāng)數(shù)據(jù)到達(dá)的時候,它可能已經(jīng)過時了.確保數(shù)據(jù)的有序性和正確性需要額外的操作的內(nèi)存消耗,額外的費用將會降低系統(tǒng)的回應(yīng)速率.

          無連接的操作使用數(shù)據(jù)報協(xié)議.一個數(shù)據(jù)報是一個獨立的單元,它包含了所有的這次投遞的信息.把它想象成一個信封吧,它有目的地址和要發(fā)送的內(nèi)容.這個模式下的socket不需要連接一個目的的socket,它只是簡單地投出數(shù)據(jù)報.無連接的操作是快速的和高效的,但是數(shù)據(jù)安全性不佳.

          面向連接的操作使用TCP協(xié)議.一個這個模式下的socket必須在發(fā)送數(shù)據(jù)之前與目的地的socket取得一個連接.一旦連接建立了,sockets就可以使用一個流接口:打開-讀-寫-關(guān)閉.所有的發(fā)送的信息都會在另一端以同樣的順序被接收.面向連接的操作比無連接的操作效率更低,但是數(shù)據(jù)的安全性更高.

          SUN一直是網(wǎng)絡(luò)建設(shè)的支持者,所以在Java中支持sockets就不足為奇了.實際上,Java降低了建立一個sockets程序的難度.每一個傳輸模式都被封裝到了不同的類中.面向連接的類將會首先被我們討論.

          Sockets有兩種主要的操作方式:面向連接的和無連接的.面向連接的sockets操作就像一部電話,他們必須建立一個連接和一人呼叫.所有的事情在到達(dá)時的順序與它們出發(fā)時的順序時一樣.無連接的sockets操作就像是一個郵件投遞,,沒有什么保證,多個郵件可能在到達(dá)時的順序與出發(fā)時的順序不一樣.

          到底用哪種模式是郵應(yīng)用程序的需要決定的.如果可靠性更重要的話,用面向連接的操作會好一些.比如文件服務(wù)器需要他們的數(shù)據(jù)的正確性和有序性.如果一些數(shù)據(jù)丟失了,系統(tǒng)的有效性將會失去.一些服務(wù)器,比如間歇性地發(fā)送一些數(shù)據(jù)塊.如果數(shù)據(jù)丟了的話,服務(wù)器并不想要再重新發(fā)過一次.因為當(dāng)數(shù)據(jù)到達(dá)的時候,它可能已經(jīng)過時了.確保數(shù)據(jù)的有序性和正確性需要額外的操作的內(nèi)存消耗,額外的費用將會降低系統(tǒng)的回應(yīng)速率.

          無連接的操作使用數(shù)據(jù)報協(xié)議.一個數(shù)據(jù)報是一個獨立的單元,它包含了所有的這次投遞的信息.把它想象成一個信封吧,它有目的地址和要發(fā)送的內(nèi)容.這個模式下的socket不需要連接一個目的的socket,它只是簡單地投出數(shù)據(jù)報.無連接的操作是快速的和高效的,但是數(shù)據(jù)安全性不佳.

          面向連接的操作使用TCP協(xié)議.一個這個模式下的socket必須在發(fā)送數(shù)據(jù)之前與目的地的socket取得一個連接.一旦連接建立了,sockets就可以使用一個流接口:打開-讀-寫-關(guān)閉.所有的發(fā)送的信息都會在另一端以同樣的順序被接收.面向連接的操作比無連接的操作效率更低,但是數(shù)據(jù)的安全性更高.

          SUN一直是網(wǎng)絡(luò)建設(shè)的支持者,所以在Java中支持sockets就不足為奇了.實際上,Java降低了建立一個sockets程序的難度.每一個傳輸模式都被封裝到了不同的類中.面向連接的類將會首先被我們討論

          2.Java面向連接的類

          Sockets有兩種主要的操作方式:面向連接的和無連接的.面向連接的sockets操作就像一部電話,他們必須建立一個連接和一人呼叫.所有的事情在到達(dá)時的順序與它們出發(fā)時的順序時一樣.無連接的sockets操作就像是一個郵件投遞,,沒有什么保證,多個郵件可能在到達(dá)時的順序與出發(fā)時的順序不一樣.

          到底用哪種模式是郵應(yīng)用程序的需要決定的.如果可靠性更重要的話,用面向連接的操作會好一些.比如文件服務(wù)器需要他們的數(shù)據(jù)的正確性和有序性.如果一些數(shù)據(jù)丟失了,系統(tǒng)的有效性將會失去.一些服務(wù)器,比如間歇性地發(fā)送一些數(shù)據(jù)塊.如果數(shù)據(jù)丟了的話,服務(wù)器并不想要再重新發(fā)過一次.因為當(dāng)數(shù)據(jù)到達(dá)的時候,它可能已經(jīng)過時了.確保數(shù)據(jù)的有序性和正確性需要額外的操作的內(nèi)存消耗,額外的費用將會降低系統(tǒng)的回應(yīng)速率.

          無連接的操作使用數(shù)據(jù)報協(xié)議.一個數(shù)據(jù)報是一個獨立的單元,它包含了所有的這次投遞的信息.把它想象成一個信封吧,它有目的地址和要發(fā)送的內(nèi)容.這個模式下的socket不需要連接一個目的的socket,它只是簡單地投出數(shù)據(jù)報.無連接的操作是快速的和高效的,但是數(shù)據(jù)安全性不佳.

          面向連接的操作使用TCP協(xié)議.一個這個模式下的socket必須在發(fā)送數(shù)據(jù)之前與目的地的socket取得一個連接.一旦連接建立了,sockets就可以使用一個流接口:打開-讀-寫-關(guān)閉.所有的發(fā)送的信息都會在另一端以同樣的順序被接收.面向連接的操作比無連接的操作效率更低,但是數(shù)據(jù)的安全性更高.

          SUN一直是網(wǎng)絡(luò)建設(shè)的支持者,所以在Java中支持sockets就不足為奇了.實際上,Java降低了建立一個sockets程序的難度.每一個傳輸模式都被封裝到了不同的類中.面向連接的類將會首先被我們討論

          在Java中面向連接的類有兩種形式,它們分別是客戶端和服務(wù)器端.客戶端這一部分是最簡單的,所以我們先討論它.

          列表9.1列出了一個簡單的客戶端的程序.它向一個服務(wù)器發(fā)出一個請求,取回一個HTML文檔,并把它顯示在控制臺上.

          9.1一個簡單的socket客戶端

          import java.io.*;
          import java.net.*;
          /**
          * 一個簡單的從服務(wù)器取回一個HTML頁面的程序
          * 注意:merlin是本地機器的名字
          */
          public class SimpleWebClient {
          public static void main(String args[])
          {
          try
          {
          // 打開一個客戶端socket連接
          Socket clientSocket1 = new Socket("merlin", 80);
          System.out.println("Client1: " + clientSocket1);
          // 取得一個網(wǎng)頁
          getPage(clientSocket1);
          }
          catch (UnknownHostException uhe)
          {
          System.out.println("UnknownHostException: " + uhe);
          }
          catch (IOException ioe)
          {
          System.err.println("IOException: " + ioe);
          }
          }
          /**
          *通過建立的連接請求一個頁面,顯示回應(yīng)然后關(guān)閉socket
          */
          public static void getPage(Socket clientSocket)
          {
          try
          {
          // 需要輸入和輸出流
          DataOutputStream outbound = new DataOutputStream(
          clientSocket.getOutputStream() );
          DataInputStream inbound = new DataInputStream(
          clientSocket.getInputStream() );
          // 向服務(wù)器發(fā)出HTTP請求
          outbound.writeBytes("GET / HTTP/1.0 ");
          // 讀出回應(yīng)
          String responseLine;
          while ((responseLine = inbound.readLine()) != null)
          {
          // 把每一行顯示出來
          System.out.println(responseLine);
          if ( responseLine.indexOf("") != -1 )
          break;
          }
          // 清除
          outbound.close();
          inbound.close();
          clientSocket.close();
          }
          catch (IOException ioe)
          {
          System.out.println("IOException: " + ioe);
          }
          }
          }

          Java面向連接的類

          回憶一個,一個客戶端向一個正在監(jiān)聽的服務(wù)器socket發(fā)出一個連接.客戶端的sockets是用Socket類建立的.下面的程序建立了一個客戶端的socket并且連接到了一個主機:

          Socket clientSocket = new Socket("merlin", 80);

          第一個參數(shù)是你想要連接的主機的名稱,第二個參數(shù)是端口號.一個主機名稱指定了目的的名稱.端口號指定了由哪個應(yīng)用程序來接收.在我們的情況下,必須指定80,因為它是默認(rèn)的HTTP協(xié)議的端口.另外的知名的端口列在表9.1中,看:

          知名的端品:

          echo 7

          daytime 13

          daytime 13

          ftp 21

          telnet 23

          smtp 25

          finger 79

          http 80

          pop3 110

          因為Socket類是面向連接的,它提供了一個可供讀寫的流接口.java.io包中的類可以用來訪問一個已連接的socket:

          DataOutputStream outbound = new DataOutputStream(
          clientSocket.getOutputStream() );
          DataInputStream inbound = new DataInputStream( clientSocket.getInputStream()
          );

          一旦流建立了,一般的流操作就可以做了:

          outbound.writeBytes("GET / HTTP/1.0 );
          String responseLine;
          while ( (responseLine = inbound.readLine()) != null)
          {
          System.out.println(responseLine);
          }

          以上的小程序請求了一個WEB頁面并且把它顯示出來.當(dāng)程序完成之后,連接必須關(guān)閉.

          outbound.close();
          inbound.close();
          clientSocket.close();

          注意socket流必須首先關(guān)閉.所有的的socket流必須在socket關(guān)閉之前關(guān)閉.這個小程序非常地簡單,但是所有的客戶端程序都必須遵首下面的基本的步驟:

          1.建立客戶端socket連接.

          2.得到socket的讀和寫的流.

          3.利用流.

          4.關(guān)閉流.

          5.關(guān)閉socket.

          使用一個服務(wù)器端的socket只是有一點復(fù)雜,它將在下面講到.

          服務(wù)器Sockets

          列表9.2是一個服務(wù)器應(yīng)用程序的一部分.

          列表9.2 一個簡單的服務(wù)器程序

          /**
          * 一個監(jiān)聽端口并提供HTML文檔的程序.
          */
          class SimpleWebServer {
          public static void main(String args[])
          {
          ServerSocket serverSocket = null;
          Socket clientSocket = null;
          int connects = 0;
          try
          {
          {
          // 建立一個服務(wù)器socket
          serverSocket = new ServerSocket(80, 5);
          while (connects < 5)
          {
          // 等待連接
          clientSocket = serverSocket.accept();
          //服務(wù)連接
          ServiceClient(clientSocket);
          connects++;
          }
          serverSocket.close();
          }
          catch (IOException ioe)
          {
          System.out.println("Error in SimpleWebServer: " + ioe);
          }
          }
          public static void ServiceClient(Socket client)
          throws IOException
          {
          DataInputStream inbound = null;
          DataOutputStream outbound = null;
          try
          {
          // 得到IO流
          inbound = new DataInputStream( client.getInputStream());
          outbound = new DataOutputStream( client.getOutputStream());
          //格式化輸出(回應(yīng)頭和很少的HTML文檔)
          StringBuffer buffer = PrepareOutput();
          String inputLine;
          while ((inputLine = inbound.readLine()) != null)
          {
          //如果到了HTTP請求的尾部,就發(fā)送回應(yīng)
          if ( inputLine.equals("") )
          {
          outbound.writeBytes(buffer.toString());
          break;
          }
          }
          }
          finally
          {
          // 清除
          System.out.println("Cleaning up connection: " + client);
          tln("Cleaning up connection: " + client);
          outbound.close();
          inbound.close();
          client.close();
          client.close();
          }
          }

          服務(wù)器Sockets

          服務(wù)器并不是主動地建立連接.相反地,他們是被動地監(jiān)聽一個客戶端的連接請示然后給他們服務(wù).服務(wù)器是由類ServerSocket來建立的.下面的程序建立了一個服務(wù)器端socket并把它綁定到80端口:

          ServerSocket serverSocket = new ServerSocket(80, 5);

          第一個參數(shù)是服務(wù)器要監(jiān)聽的端口.第二個參數(shù)是可選的.API文檔中說明了這是一個監(jiān)聽時間,但是在傳統(tǒng)的socket程序中第二個參數(shù)是監(jiān)聽深度.一個服務(wù)器可以同時接收多個連接請求,但是每次只能處理一個.監(jiān)聽堆是一個無回答的連接請求隊列.上面的請求建立一個連接來處理最后五個請求.如果省略了后面的一個參數(shù),則默認(rèn)值是50.

          ServerSocket serverSocket = new ServerSocket(80, 5);

          一旦socket建立了并開始監(jiān)聽連接,進(jìn)來的連接將會建立并放在監(jiān)聽堆.accetp()方法把在堆中的連接取出來.

          Socket clientSocket = serverSocket.accept();

          這個方法返回一個用來與來訪者對話的客戶端連接.服務(wù)器本身不可能建立對話,相反地,服務(wù)器socket會使用accept()方法來產(chǎn)生一個新的socket.服務(wù)器socket依舊打開并排列新的連接請求.

          與客戶端socket一樣,下面的一步建立輸入和輸出流:

          DataInputStream inbound = new DataInputStream( clientSocket.getInputStream() ); DataOutputStream outbound = new DataOutputStream( clientSocket.getOutputStream() );

          一般的I/O操作可以在新建的流中運用.在服務(wù)器回應(yīng)前它等待客戶端發(fā)送一個空白的行.當(dāng)會話結(jié)束時,服務(wù)器關(guān)閉流和客戶端socket.如果在隊列中沒有請示將會出現(xiàn)什么情況呢?那個方法將會等待一個的到來.這個行為叫阻塞.accept()方法將會阻塞服務(wù)器線程直到一個呼叫到來.當(dāng)5個連接處理完閉之后,服務(wù)器退出.任何的在隊列中的呼叫將會被取消.

          所有的服務(wù)器都要有以下的基本的步驟:

          1.建立一個服務(wù)器socket并開始監(jiān)聽.

          2.使用accept()方法取得新的連接.

          3.建立輸入和輸出流.

          4.在已有的協(xié)議上產(chǎn)生會話.

          5.關(guān)閉客戶端流和socket.

          6.回到第二步或者到第七步.

          7.關(guān)閉服務(wù)器socket.

          重復(fù)和并發(fā)服務(wù)器

          這個應(yīng)用程序被當(dāng)作一個重復(fù)的服務(wù)器.因為它只有在處理完一個進(jìn)程以后才會接受另一個連接.更多的復(fù)雜服務(wù)器是并發(fā)的.它為每一個請求分配一個線程,而不是來一個處理一個.所以看起來它在同時處理多人請求.所有的商業(yè)的服務(wù)器都是并發(fā)的服務(wù)器.

          Java數(shù)據(jù)報類

          不像面向連接的類,數(shù)據(jù)報的客戶端和服務(wù)器端的類在表面上是一樣的.下面的程序建立了一個客戶和服務(wù)器商的數(shù)據(jù)報sockets:

          DatagramSocket serverSocket = new DatagramSocket( 4545 );
          DatagramSocket clientSocket = new DatagramSocket();

          服務(wù)器用參數(shù)4545來指定端口號,由于客戶端將要呼叫服務(wù)器,客戶端可以利用可利用的端口.如果省略第二個參數(shù),程序會讓操作系統(tǒng)分配一個可用的端口.客戶端可以請求一個指定的端口,但是如果其它的應(yīng)用程序已經(jīng)綁定到這個端口之上,請求將會失敗.如果你的意圖不是作為一個服務(wù)器,最好不要指定端口.

          由于流不能由交談得到,那么我么如何與一個數(shù)據(jù)報Socket進(jìn)行對話.答案在于數(shù)據(jù)報類.

          接收數(shù)據(jù)報

          DatagramPacket類是用來通過DatagramSocket類接收和發(fā)送數(shù)據(jù)的類.packet類包括了連接信息和數(shù)據(jù).就如前面所說的一樣,數(shù)據(jù)報是自身獨立的傳輸單元.DatagramPacket類壓縮了這些單元.下面的程序表示了用一個數(shù)據(jù)報socket來接收數(shù)據(jù):

          DatagramPacket packet = new DatagramPacket(new byte[512], 512); clientSocket.receive(packet);
          clientSocket.receive(packet);

          packet的構(gòu)建器需要知道將得到的數(shù)據(jù)放在哪兒.一個512字節(jié)的緩存被建立并且作為構(gòu)建器的第二個參數(shù).每二個構(gòu)建器參數(shù)是緩存的大小.就像ServerSocket類的accept()方法一樣,receive()方法在數(shù)據(jù)可用之前將會阻塞.

          發(fā)送數(shù)據(jù)報

          發(fā)送數(shù)據(jù)報是非常地簡單地,所有需要的只是一個地址.地址是由InetAddress類來建立的.這個類沒有公共的構(gòu)建器,但是它有幾個static的方法,可以用來建立這個類的實例.下面的列表列出了建立InetAddress類的實例的方法:

          Public InetAddress Creation Methods

          InetAddress getByName(String host);
          InetAddress[] getAllByName(String host);
          InetAddress getLocalHost();

          得到本地主機的地址是非常地有用的,只有前面兩個方法是用來發(fā)送數(shù)據(jù)包的.getByName()和getAllByName()需要目的主機的地址.第一個方法僅僅只是返回第一個符合條件的東西.第二個方法是必須的,因為一臺計算機可能有多個地址.在這種情況下,這臺計算機被稱為multi-homed.

          所有的建立的方法都被標(biāo)記為static.它們必須像下面這樣得到調(diào)用:

          InetAddress addr1 = InetAddress.getByName("merlin");
          InetAddress addr2[] = InetAddress.getAllByName("merlin");
          InetAddress addr3 = InetAddress.getLocalHost();

          所有的這些調(diào)用都可以擲出一個UnknownHostException違例.如果一臺計算機沒有連接上DNS服務(wù)器,或者主機的確沒有找到,這個違例就會被擲出.如果一臺計算機沒有一個激活的TCP/IP配置,getLocalHost()也為失敗并擲出一個違例.

          一旦一個地址被確定了,數(shù)據(jù)報就可以被送出了.下面的程序傳輸了一個字符串給目的socket:

          String toSend = "This is the data to send!");
          byte[] sendbuf = new byte[ toSend.length() ];
          toSend.getBytes( 0, toSend.length(), sendbuf, 0 );
          DatagramPacket sendPacket = new DatagramPacket( sendbuf, sendbuf.length,
          addr, port);
          clientSocket.send( sendPacket );

          首先,字符串必須被轉(zhuǎn)換成一個字節(jié)數(shù)組.然后,一個新的DatagramPacket實例必須被建立.注意構(gòu)建器的最后兩個參數(shù).因為要發(fā)送一個包,所以地址和端口必須被給定.一個applet可能可以知道它的服務(wù)器的地址,但是服務(wù)器如何知道它的客戶機的地址呢.當(dāng)任何一個包被收到后,返回的地址和端口會被解壓出來,并通過getAddress()和getPort()方法得到.這就是一個服務(wù)器如何回應(yīng)一個客戶端的包:

          DatagramPacket sendPacket = new DatagramPacket( sendbuf, sendbuf.length,
          recvPacket.getAddress(), recvPacket.getPort() );
          serverSocket.send( sendPacket );

          不像面向連接的操作,數(shù)據(jù)報服務(wù)器服務(wù)器其實比數(shù)據(jù)報客戶端更簡單:

          數(shù)據(jù)報服務(wù)器

          一個數(shù)據(jù)報服務(wù)器的基本步驟:

          1.在一個指定的端口上建立一個數(shù)據(jù)報socket.

          2.用receive方法等待進(jìn)來的包.

          3.用特定的協(xié)議來回應(yīng)收到的包.

          4.回到第二步或繼續(xù)第二步.

          5.關(guān)閉數(shù)據(jù)報socket.

          列表9.3演示了一人簡單的數(shù)據(jù)報回應(yīng)服務(wù)器.它將回應(yīng)它收到的包.

          列表9.3.一個簡單的數(shù)據(jù)報回應(yīng)服務(wù)器

          import java.io.*;
          import java.net.*;
          public class SimpleDatagramServer
          {
          public static void main(String[] args)
          {
          DatagramSocket socket = null;
          DatagramPacket recvPacket, sendPacket;
          try
          {
          socket = new DatagramSocket(4545);
          while (socket != null)
          {
          recvPacket= new DatagramPacket(new byte[512], 512);
          socket.receive(recvPacket);
          sendPacket = new DatagramPacket(
          recvPacket.getData(), recvPacket.getLength(),
          recvPacket.getAddress(), recvPacket.getPort() );
          socket.send( sendPacket );
          }
          }
          catch (SocketException se)
          {
          System.out.println("Error in SimpleDatagramServer: " + se);
          }
          catch (IOException ioe)
          {
          System.out.println("Error in SimpleDatagramServer: " + ioe);


          簡單的WEB服務(wù)器

          一個簡單的WEB服務(wù)器將由列表9.2這樣構(gòu)建.當(dāng)然,還必須要對方法和回應(yīng)事件進(jìn)行改進(jìn).簡單的服務(wù)器不會分析和存儲請求頭.新的WEB服務(wù)器將分析和存儲請求,為以后的處理作準(zhǔn)備.為了達(dá)到這個目的,你必須有一個包含HTTP請求的類.

          HTTPrequest類

          列表9.5列出了一個完整的HTTPrequest類.這個類必須包括一個請求頭所需的所有信息.

          列表9.5.HTTPrequest類.

          import java.io.*;
          import java.util.*;
          import java.net.*;
          import NameValue;
          /**
          * 這個類有一個HTTP請求的所有信息
          */
          public class HTTPrequest
          {
          public String version;
          public String method;
          public String file;
          public Socket clientSocket;
          public DataInputStream inbound;
          public NameValue headerpairs[];
          /**
          * 建立一個這個類的實例
          */
          public HTTPrequest()
          {
          version = null;
          method = null;
          file = null;
          clientSocket = null;
          inbound = null;
          inbound = null;
          headerpairs = new NameValue[0];
          }
          /**
          * 加入一個名稱/值對到核心數(shù)組
          */
          public void addNameValue(String name, String value)
          {
          try
          {
          NameValue temp[] = new NameValue[ headerpairs.length + 1 ];
          System.arraycopy(headerpairs, 0, temp, 0, headerpairs.length);
          temp[ headerpairs.length ] = new NameValue(name, value);
          headerpairs = temp;
          }
          catch (NullPointerException npe)
          {
          System.out.println("NullPointerException while adding name-value:
          " + npe);
          }
          }
          /**
          * 以字符串的形式歸還這個類
          */
          public String toString()
          {
          String s = method + " " + file + " " + version + " ";
          for (int x = 0; x < headerpairs.length; x++ )
          s += headerpairs[x] + " ";
          return s;
          }
          }

          NameValue類簡單地存儲了兩個字符串:name 和 value.當(dāng)一個新的對要被加入時,一個新的數(shù)組將被分配.新的數(shù)組接受了舊的數(shù)組和新的成員.舊的數(shù)組然后被一個新建的對象覆蓋了

          posted on 2006-06-08 15:34 xiaofeng 閱讀(177) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿(2)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          收藏夾

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 香河县| 江城| 松桃| 朝阳县| 滨州市| 太仆寺旗| 安阳县| 湟中县| 玉环县| 台山市| 宁武县| 沈丘县| 德州市| 义马市| 南丰县| 胶州市| 嫩江县| 土默特右旗| 咸宁市| 谷城县| 响水县| 沙河市| 峨边| 沈丘县| 明水县| 安乡县| 保德县| 清远市| 公主岭市| 丹棱县| 民和| 台前县| 汉川市| 西安市| 长子县| 望奎县| 芜湖市| 靖江市| 双城市| 抚松县| 沁水县|