<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    周游世界

    喂馬, 劈柴, 周游世界

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      28 隨筆 :: 0 文章 :: 4 評論 :: 0 Trackbacks
    8.3 基于Socket(套接字)的低層次Java網絡編程

      8.3.1 Socket通訊

      網絡上的兩個程序通過一個雙向的通訊連接實現數據的交換,這個雙向鏈路的一端稱為一個Socket。Socket通常用來實現客戶方和服務方的連接。Socket是TCP/IP協議的一個十分流行的編程界面,一個Socket由一個IP地址和一個端口號唯一確定。

      在傳統的UNIX環境下可以操作TCP/IP協議的接口不止Socket一個,Socket所支持的協議種類也不光TCP/IP一種,因此兩者之間是沒有必然聯系的。在Java環境下,Socket編程主要是指基于TCP/IP協議的網絡編程。

      說Socket編程是低層次網絡編程并不等于它功能不強大,恰恰相反,正因為層次低,Socket編程比基于URL的網絡編程提供了更強大的功能和更靈活的控制,但是卻要更復雜一些。由于Java本身的特殊性,Socket編程在Java中可能已經是層次最低的網絡編程接口,在Java中要直接操作協議中更低的層次,需要使用Java的本地方法調用(JNI),在這里就不予討論了。

      8.3.2 Socket通訊的一般過

      前面已經提到Socket通常用來實現C/S結構。

      使用Socket進行Client/Server程序設計的一般連接過程是這樣的:Server端Listen(監聽)某個端口是否有連接請求, Client端向Server端發出Connect(連接)請求,Server端向Client端發回Accept(接受)消息。一個連接就建立起來了。 Server端和Client端都可以通過Send,Write等方法與對方通信。

      對于一個功能齊全的Socket,都要包含以下基本結構,其工作過程包含以下四個基本的步驟:
      (1) 創建Socket;
      (2) 打開連接到Socket的輸入/出流;
      (3) 按照一定的協議對Socket進行讀/寫操作;
      (4) 關閉Socket.

      第三步是程序員用來調用Socket和實現程序功能的關鍵步驟,其他三步在各種程序中基本相同。

      以上4個步驟是針對TCP傳輸而言的,使用UDP進行傳輸時略有不同,在后面會有具體講解。

      8.3.3 創建Socket

      java在包java.net中提供了兩個類Socket和ServerSocket,分別用來表示雙向連接的客戶端和服務端。這是兩個封裝得非常好的類,使用很方便。其構造方法如下:
      Socket(InetAddress address, int port);
      Socket(InetAddress address, int port, boolean stream);
      Socket(String host, int prot);
      Socket(String host, int prot, boolean stream);
      Socket(SocketImpl impl)
      Socket(String host, int port, InetAddress localAddr, int localPort)
      Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
      ServerSocket(int port);
      ServerSocket(int port, int backlog);
      ServerSocket(int port, int backlog, InetAddress bindAddr)

      其中address、host和port分別是雙向連接中另一方的IP地址、主機名和端口號,stream指明socket是流socket還是數據報 socket,localPort表示本地主機的端口號,localAddr和bindAddr是本地機器的地址(ServerSocket的主機地址),impl是socket的父類,既可以用來創建serverSocket又可以用來創建Socket。count則表示服務端所能支持的最大連接數。例如:
      Socket client = new Socket("127.0.01.", 80);
      ServerSocket server = new ServerSocket(80);

      注意,在選擇端口時,必須小心。每一個端口提供一種特定的服務,只有給出正確的端口,才能獲得相應的服務。0~1023的端口號為系統所保留,例如 http服務的端口號為80,telnet服務的端口號為21,ftp服務的端口號為23, 所以我們在選擇端口號時,最好選擇一個大于1023的數以防止發生沖突。

      在創建socket時如果發生錯誤,將產生IOException,在程序中必須對之作出處理。所以在創建Socket或ServerSocket是必須捕獲或拋出例外。

      8.3.4 客戶端的Socket

      下面是一個典型的創建客戶端Socket的過程。
       try{
         Socket socket=new Socket("127.0.0.1",4700);
         //127.0.0.1是TCP/IP協議中默認的本機地址
       }catch(IOException e){
         System.out.println("Error:"+e);
       }

      這是最簡單的在客戶端創建一個Socket的一個小程序段,也是使用Socket進行網絡通訊的第一步,程序相當簡單,在這里不作過多解釋了。在后面的程序中會用到該小程序段。


      8.3.5 服務器端的ServerSocket

      下面是一個典型的創建Server端ServerSocket的過程。
      ServerSocket server=null;
      try {
         server=new ServerSocket(4700);
         //創建一個ServerSocket在端口4700監聽客戶請求
      }catch(IOException e){
         System.out.println("can not listen to :"+e);
      }
      Socket socket=null;
      try {
        socket=server.accept();
        //accept()是一個阻塞的方法,一旦有客戶請求,它就會返回一個Socket對象用于同客戶進行交互
      }catch(IOException e){
        System.out.println("Error:"+e);
      }

      以上的程序是Server的典型工作模式,只不過在這里Server只能接收一個請求,接受完后Server就退出了。實際的應用中總是讓它不停的循環接收,一旦有客戶請求,Server總是會創建一個服務線程來服務新來的客戶,而自己繼續監聽。程序中accept()是一個阻塞函數,所謂阻塞性方法就是說該方法被調用后,將等待客戶的請求,直到有一個客戶啟動并請求連接到相同的端口,然后accept()返回一個對應于客戶的socket。這時,客戶方和服務方都建立了用于通信的socket,接下來就是由各個socket分別打開各自的輸入/輸出流。

      8.3.6 打開輸入/出流

      類Socket提供了方法getInputStream ()和getOutStream()來得到對應的輸入/輸出流以進行讀/寫操作,這兩個方法分別返回InputStream和OutputSteam類對象。為了便于讀/寫數據,我們可以在返回的輸入/輸出流對象上建立過濾流,如DataInputStream、DataOutputStream或 PrintStream類對象,對于文本方式流對象,可以采用InputStreamReader和OutputStreamWriter、 PrintWirter等處理。

      例如:
      PrintStream os=new PrintStream(new BufferedOutputStreem(socket.getOutputStream()));
      DataInputStream is=new DataInputStream(socket.getInputStream());
      PrintWriter out=new PrintWriter(socket.getOutStream(),true);
      BufferedReader in=new ButfferedReader(new InputSteramReader(Socket.getInputStream()));

      輸入輸出流是網絡編程的實質性部分,具體如何構造所需要的過濾流,要根據需要而定,能否運用自如主要看讀者對Java中輸入輸出部分掌握如何。

      8.3.7 關閉Socket

      每一個Socket存在時,都將占用一定的資源,在Socket對象使用完畢時,要其關閉。關閉Socket可以調用Socket的Close()方法。在關閉Socket之前,應將與Socket相關的所有的輸入/輸出流全部關閉,以釋放所有的資源。而且要注意關閉的順序,與Socket相關的所有的輸入/輸出該首先關閉,然后再關閉Socket。
      os.close();
      is.close();
      socket.close();

      盡管Java有自動回收機制,網絡資源最終是會被釋放的。但是為了有效的利用資源,建議讀者按照合理的順序主動釋放資源。

      8.3.8 簡單的Client/Server程序設計

      下面我們給出一個用Socket實現的客戶和服務器交互的典型的C/S結構的演示程序,讀者通過仔細閱讀該程序,會對前面所討論的各個概念有更深刻的認識。程序的意義請參考注釋。

      1. 客戶端程序

      import java.io.*;
      import java.net.*;
      public class TalkClient {
        public static void main(String args[]) {
          try{
            Socket socket=new Socket("127.0.0.1",4700);
            //向本機的4700端口發出客戶請求
            BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
            //由系統標準輸入設備構造BufferedReader對象
            PrintWriter os=new PrintWriter(socket.getOutputStream());
            //由Socket對象得到輸出流,并構造PrintWriter對象
            BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //由Socket對象得到輸入流,并構造相應的BufferedReader對象
            String readline;
            readline=sin.readLine(); //從系統標準輸入讀入一字符串
            while(!readline.equals("bye")){
            //若從標準輸入讀入的字符串為 "bye"則停止循環
              os.println(readline);
              //將從系統標準輸入讀入的字符串輸出到Server
              os.flush();
              //刷新輸出流,使Server馬上收到該字符串
              System.out.println("Client:"+readline);
              //在系統標準輸出上打印讀入的字符串
              System.out.println("Server:"+is.readLine());
              //從Server讀入一字符串,并打印到標準輸出上
              readline=sin.readLine(); //從系統標準輸入讀入一字符串
            } //繼續循環
            os.close(); //關閉Socket輸出流
            is.close(); //關閉Socket輸入流
            socket.close(); //關閉Socket
          }catch(Exception e) {
            System.out.println("Error"+e); //出錯,則打印出錯信息
          }
      }
      } 2. 服務器端程序

      import java.io.*;
      import java.net.*;
      import java.applet.Applet;
      public class TalkServer{
        public static void main(String args[]) {
          try{
            ServerSocket server=null;
            try{
              server=new ServerSocket(4700);
            //創建一個ServerSocket在端口4700監聽客戶請求
            }catch(Exception e) {
              System.out.println("can not listen to:"+e);
            //出錯,打印出錯信息
            }

            Socket socket=null;
            try{
              socket=server.accept();
              //使用accept()阻塞等待客戶請求,有客戶
              //請求到來則產生一個Socket對象,并繼續執行
            }catch(Exception e) {
              System.out.println("Error."+e);
              //出錯,打印出錯信息
            }
            String line;
            BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
             //由Socket對象得到輸入流,并構造相應的BufferedReader對象
            PrintWriter os=newPrintWriter(socket.getOutputStream());
             //由Socket對象得到輸出流,并構造PrintWriter對象
            BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
             //由系統標準輸入設備構造BufferedReader對象

            System.out.println("Client:"+is.readLine());
            //在標準輸出上打印從客戶端讀入的字符串
            line=sin.readLine();
            //從標準輸入讀入一字符串
            while(!line.equals("bye")){
            //如果該字符串為 "bye",則停止循環
              os.println(line);
              //向客戶端輸出該字符串
              os.flush();
              //刷新輸出流,使Client馬上收到該字符串
              System.out.println("Server:"+line);
              //在系統標準輸出上打印讀入的字符串
              System.out.println("Client:"+is.readLine());
              //從Client讀入一字符串,并打印到標準輸出上
              line=sin.readLine();
              //從系統標準輸入讀入一字符串
            }  //繼續循環
            os.close(); //關閉Socket輸出流
            is.close(); //關閉Socket輸入流
            socket.close(); //關閉Socket
            server.close(); //關閉ServerSocket
          }catch(Exception e){
            System.out.println("Error:"+e);
            //出錯,打印出錯信息
          }
        }
      }

      從上面的兩個程序中我們可以看到,socket四個步驟的使用過程。讀者可以分別將Socket使用的四個步驟的對應程序段選擇出來,這樣便于讀者對socket的使用有進一步的了解。

      讀者可以在單機上試驗該程序,最好是能在真正的網絡環境下試驗該程序,這樣更容易分辨輸出的內容和客戶機,服務器的對應關系。同時也可以修改該程序,提供更為強大的功能,或更加滿足讀者的意圖。

      

      8.3.9 支持多客戶的client/server程序設計

      前面提供的Client/Server程序只能實現Server和一個客戶的對話。在實際應用中,往往是在服務器上運行一個永久的程序,它可以接收來自其他多個客戶端的請求,提供相應的服務。為了實現在服務器方給多個客戶提供服務的功能,需要對上面的程序進行改造,利用多線程實現多客戶機制。服務器總是在指定的端口上監聽是否有客戶請求,一旦監聽到客戶請求,服務器就會啟動一個專門的服務線程來響應該客戶的請求,而服務器本身在啟動完線程之后馬上又進入監聽狀態,等待下一個客戶的到來。

      客戶端的程序和上面程序是完全一樣的,讀者如果仔細閱讀過上面的程序,可以跳過不讀,把主要精力集中在Server端的程序上。

      2. 服務器端程序: MultiTalkServer.java

      import java.io.*;
      import java.net.*;
      import ServerThread;
      public class MultiTalkServer{
       static int clientnum=0; //靜態成員變量,記錄當前客戶的個數
       public static void main(String args[]) throws IOException {
        ServerSocket serverSocket=null;
        boolean listening=true;
        try{
          serverSocket=new ServerSocket(4700);
          //創建一個ServerSocket在端口4700監聽客戶請求
        }catch(IOException e) {
          System.out.println("Could not listen on port:4700.");
          //出錯,打印出錯信息
          System.exit(-1); //退出
        }
        while(listening){ //永遠循環監聽
          new ServerThread(serverSocket.accept(),clientnum).start();
          //監聽到客戶請求,根據得到的Socket對象和
           客戶計數創建服務線程,并啟動之
          clientnum++; //增加客戶計數
        }
        serverSocket.close(); //關閉ServerSocket
      }
      }

      3. 程序ServerThread.java

      import java.io.*;
      import java.net.*;
      public class ServerThread extends Thread{
       Socket socket=null; //保存與本線程相關的Socket對象
       int clientnum; //保存本進程的客戶計數
       public ServerThread(Socket socket,int num) { //構造函數
        this.socket=socket; //初始化socket變量
        clientnum=num+1; //初始化clientnum變量
       }
       public void run() { //線程主體
        try{
          String line;
          BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
      //由Socket對象得到輸入流,并構造相應的BufferedReader對象
          PrintWriter os=newPrintWriter(socket.getOutputStream());
          //由Socket對象得到輸出流,并構造PrintWriter對象
          BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
          //由系統標準輸入設備構造BufferedReader對象
          System.out.println("Client:"+ clientnum +is.readLine());
          //在標準輸出上打印從客戶端讀入的字符串
          line=sin.readLine();
          //從標準輸入讀入一字符串
          while(!line.equals("bye")){
          //如果該字符串為 "bye",則停止循環
            os.println(line);
            //向客戶端輸出該字符串
            os.flush();
            //刷新輸出流,使Client馬上收到該字符串
            System.out.println("Server:"+line);
            //在系統標準輸出上打印該字符串
            System.out.println("Client:"+ clientnum +is.readLine());
            //從Client讀入一字符串,并打印到標準輸出上
            line=sin.readLine();
            //從系統標準輸入讀入一字符串
          } //繼續循環
          os.close(); //關閉Socket輸出流
          is.close(); //關閉Socket輸入流
          socket.close(); //關閉Socket
          server.close(); //關閉ServerSocket
         }catch(Exception e){
          System.out.println("Error:"+e);
          //出錯,打印出錯信息
         }
       }
      }

      通過以上的學習,讀者應該對Java的面向流的網絡編程有了一個比較全面的認識,這些都是基于TCP的應用,后面我們將介紹基于UDP的Socket編程
    posted on 2006-11-05 11:42 周游世界 閱讀(599) 評論(0)  編輯  收藏 所屬分類: 網絡協議
    主站蜘蛛池模板: 久久夜色精品国产亚洲AV动态图 | 伊人久久综在合线亚洲2019| 337P日本欧洲亚洲大胆精品| 黄色永久免费网站| 亚洲精品在线播放| 亚洲视频免费在线看| 亚洲精品亚洲人成在线麻豆| 无码精品人妻一区二区三区免费看| 亚洲AV无码一区东京热| 久久黄色免费网站| 亚洲美女大bbbbbbbbb| 最近免费最新高清中文字幕韩国| 精品亚洲麻豆1区2区3区| 最近中文字幕mv免费高清在线| 亚洲精品国产福利片| 丁香花免费完整高清观看| 亚洲熟女综合色一区二区三区| 免费鲁丝片一级在线观看| 国产精品亚洲专区在线播放 | 免费大片黄在线观看yw| 亚洲黄页网在线观看| 国产免费爽爽视频免费可以看| 麻豆69堂免费视频| 亚洲中文字幕无码永久在线| 久久精品视频免费播放| 亚洲国色天香视频| 在线观看免费国产视频| 国产免费人成视频在线播放播| 日韩va亚洲va欧洲va国产| 最近2019中文字幕免费大全5| 亚洲精品不卡视频| 在线观看免费国产视频| 国产精品免费大片一区二区| 亚洲网址在线观看你懂的| 噼里啪啦电影在线观看免费高清 | 国产一级一片免费播放| aaa毛片免费观看| 亚洲制服在线观看| 亚洲A∨精品一区二区三区| 久久免费观看国产精品| 亚洲国产美女精品久久久|