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

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

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

    隨筆 - 312, 文章 - 14, 評論 - 1393, 引用 - 0
    數據加載中……

    Java網絡編程從入門到精通(13):使用Socket類接收和發送數據

    本文為原創,如需轉載,請注明作者和出處,謝謝!

    上一篇:Java網絡編程從入門到精通(12):使用isReachable方法探測主機是否可以連通

        網絡應用分為客戶端和服務端兩部分,而Socket類是負責處理客戶端通信的Java類。通過這個類可以連接到指定IP或域名的服務器上,并且可以和服務器互相發送和接受數據。在本文及后面的數篇文章中將詳細討論Socket類的使用,內容包括Socket類基礎、各式各樣的連接方式、getset方法、連接過程中的超時以及關閉網絡連接等。

    在本文中,我們將討論使用Socket類的基本步驟和方法。一般網絡客戶端程序在連接服務程序時要進行以下三步操作。

    1.         連接服務器

    2.         發送和接收數據

    3.         關閉網絡連接

    一、連接服務器

    在客戶端可以通過兩種方式來連接服務器,一種是通過IP的方式來連接服務器,而另外一種是通過域名方式來連接服務器。

    其實這兩種方式從本質上來看是一種方式。在底層客戶端都是通過IP來連接服務器的,但這兩種方式有一定的差異,如果通過IP方式來連接服務端程序,客戶端只簡單地根據IP進行連接,如果通過域名來連接服務器,客戶端必須通過DNS將域名解析成IP,然后再根據這個IP來進行連接。

    在很多程序設計語言或開發工具中(如C/C++Delphi)使用域名方式連接服務器時必須自己先將域名解析成IP,然后再通過IP進行連接,而在Java中已經將域名解析功能包含在了Socket類中,因此,我們只需象使用IP一樣使用域名即可。

    通過Socket類連接服務器程序最常用的方法就是通過Socket類的構造函數將IP或域名以及端口號作為參數傳入Socket類中。Socket類的構造函數有很多重載形式,在這一節只討論其中最常用的一種形式:public Socket(String host, int port)。從這個構造函數的定義來看,只需要將IP或域名以及端口號直接傳入構造函數即可。下面的代碼是一個連接服務端程序的例子程序:

    package mysocket;

    import java.net.*;

    public class MyConnection
    {
        
    public static void main(String[] args)
        {
            
    try
            {
                
    if (args.length > 0)
                {
                    Socket socket 
    = new Socket(args[0], 80);
                    System.out.println(args[
    0+ "已連接成功!");
                }
                
    else
                    System.out.println(
    "請指定IP或域名!");
            }
            
    catch (Exception e)
            {
                System.err.println(
    "錯誤信息:" + e.getMessage());
            }
        }
    }

    在上面的中,通過命令行參數將IP或域名傳入程序,然后通過Socket socket = new Socket(args[0], 80)連接通過命令行參數所指定的IP或域名的80端口。由于Socket類的構造函數在定義時使用了throws,因此,在調用Socket類的構造函數時,必須使用try…catch語句來捕捉錯誤,或者對main函數使用throws語句來拋出錯誤。

    測試正確的IP

    java mysocket.MyConnection 127.0.0.1

    輸出結果:127.0.0.1已經連接成功!

    測試錯誤的IP

    java mysocket.MyConnection 10.10.10.10

    輸出結果:錯誤信息:Connection timed out: connect

    注:10.10.10.10是一個并不存在的IP,如果這個IP在你的網絡中存在,請使用其它的不存在的IP

    測試正確的域名

    java mysocket.MyConnection www.ptpress.com.cn

    輸出結果:www.ptpress.com.cn已經連接成功!

    測試錯誤的域名

    java mysocket.MyConnection www.ptpress1.com.cn

    輸出結果:錯誤信息:www.ptpress1.com.cn

    使用Socket類連接服務器可以判斷一臺主機有哪些端口被打開。下面的代碼是一個掃描本機有哪些端口被打開的程序。

    package mysocket;

    import java.net.*;

    public class MyConnection1 extends Thread
    {
        
    private int minPort, maxPort;

        
    public MyConnection1(int minPort, int maxPort)
        {
            
    this.minPort = minPort;
            
    this.maxPort = maxPort;
        }

        
    public void run()
        {
            
    for (int i = minPort; i <= maxPort; i++)
            {
                
    try
                {
                    Socket socket 
    = new Socket("127.0.0.1", i);
                    System.out.println(String.valueOf(i) 
    + ":ok");
                    socket.close();
                }
                
    catch (Exception e)
                {
                }
            }
        }
        
    public static void main(String[] args)
        {
            
    int minPort = Integer.parseInt(args[0]), maxPort = Integer
                    .parseInt(args[
    1]);
            
    int threadCount = Integer.parseInt(args[2]);
            
    int portIncrement = ((maxPort - minPort + 1/ threadCount)
                    
    + (((maxPort - minPort + 1% threadCount) == 0 ? 0 : 1);
           
    MyConnection1[] instances = new MyConnection1[threadCount];
            
    for (int i = 0; i < threadCount; i++)
            {
                instances[i] 
    = new MyConnection1(minPort + portIncrement * i, minPort
                        
    + portIncrement - 1 + portIncrement * i);
                instances[i].start();
            }
        }
    }

    上面代碼通過一個指定的端口范圍(如11000),并且利用多線程將這個端口范圍分成不同的段進行掃描,這樣可以大大提高掃描的效率。

    可通過如下命令行去運行例程4-2

    java mysocket.MyConnection1 1000 3000 20

    二、發送和接收數據

    Socket類中最重要的兩個方法就是getInputStreamgetOutputStream。這兩個方法分別用來得到用于讀取和寫入數據的InputStreamOutputStream對象。在這里的InputStream讀取的是服務器程序向客戶端發送過來的數據,而OutputStream是客戶端要向服務端程序發送的數據。

    在編寫實際的網絡客戶端程序時,是使用getInputStream,還是使用getOutputStream,以及先使用誰后使用誰由具體的應用決定。如通過連接郵電出版社網站(www.ptpress.com.cn)80端口(一般為HTTP協議所使用的默認端口),并且發送一個字符串,最后再讀取從www.ptpress.com.cn返回的信息。

    package mysocket;

    import java.net.*;
    import java.io.*;

    public class MyConnection2
    {
        
    public static void main(String[] args) throws Exception
        {
            Socket socket 
    = new Socket("www.ptpress.com.cn"80);
            
    // 向服務端程序發送數據
            OutputStream ops  = socket.getOutputStream();        
            OutputStreamWriter opsw 
    = new OutputStreamWriter(ops);
            BufferedWriter bw 
    = new BufferedWriter(opsw);
            
            bw.write(
    "hello world\r\n\r\n");
            bw.flush();
            
            
    // 從服務端程序接收數據
            InputStream ips = socket.getInputStream();
            InputStreamReader ipsr 
    = new InputStreamReader(ips);
            BufferedReader br 
    = new BufferedReader(ipsr);
            String s 
    = "";        
            
    while((s = br.readLine()) != null)
                System.out.println(s);        
            socket.close();
        }
    }

    在編寫上面代碼時要注意如下兩點:

    1. 為了提高數據傳輸的效率,Socket類并沒有在每次調用write方法后都進行數據傳輸,而是將這些要傳輸的數據寫到一個緩沖區里(默認是8192個字節),然后通過flush方法將這個緩沖區里的數據一起發送出去,因此,bw.flush();是必須的。

    2. 在發送字符串時之所以在Hello World后加上 “\r\n\r\n”,這是因為HTTP協議頭是以“\r\n\r\n”作為結束標志(HTTP協議的詳細內容將在以后講解),因此,通過在發送字符串后加入“\r\n\r\n”,可以使服務端程序認為HTTP頭已經結束,可以處理了。如果不加“\r\n\r\n”,那么服務端程序將一直等待HTTP頭的結束,也就是“\r\n\r\n”。如果是這樣,服務端程序就不會向客戶端發送響應信息,而br.readLine()將因無法讀以響應信息面被阻塞,直到連接超時。

    三、關閉網絡連接

    到現在為止,我們對Socket類的基本使用方法已經有了初步的了解,但在Socket類處理完數據后,最合理的收尾方法是使用Socket類的close方法關閉網絡連接。雖然在中已經使用了close方法,但使網絡連接關閉的方法不僅僅只有close方法,下面就讓我們看看Java在什么情況下可以使網絡連接關閉。

    可以引起網絡連接關閉的情況有以下4種:

    1.  直接調用Socket類的close方法。

    2.  只要Socket類的InputStreamOutputStream有一個關閉,網絡連接自動關閉(必須通過調用InputStreamOutputStreamclose方法關閉流,才能使網絡可愛接自動關閉)。

    3.  在程序退出時網絡連接自動關閉。

    4.  將Socket對象設為null或未關閉最使用new Socket(…)建立新對象后,由JVM的垃圾回收器回收為Socket對象分配的內存空間后自動關閉網絡連接。   

    雖然這4種方法都可以達到同樣的目的,但一個健壯的網絡程序最好使用第1種或第2種方法關閉網絡連接。這是因為第3種和第4種方法一般并不會馬上關閉網絡連接,如果是這樣的話,對于某些應用程序,將會遺留大量無用的網絡連接,這些網絡連接會占用大量的系統資源。

    Socket對象被關閉后,我們可以通過isClosed方法來判斷某個Socket對象是否處于關閉狀態。然而使用isClosed方法所返回的只是Socket對象的當前狀態,也就是說,不管Socket對象是否曾經連接成功過,只要處于關閉狀態,isClosed就返回true。如果只是建立一個未連接的Socket對象,isClose則會返回false。如下面的代碼將輸出false

    Socket socket = new Socket();
    System.out.println(socket.isClosed());

    除了isClose方法,Socket類還有一個isConnected方法來判斷Socket對象是否連接成功。看到這個名字,也許讀者會產生誤解。其實isConnected方法所判斷的并不是Socket對象的當前連接狀態,而是Socket對象是否曾經連接成功過,如果成功連接過,即使現在isClose返回trueisConnected仍然返回true。因此,要判斷當前的Socket對象是否處于連接狀態,必須同時使用isCloseisConnected方法,即只有當isClose返回falseisConnected返回true的時候Socket對象才處于連接狀態。下面的代碼演示了上述Socket對象的各種狀態的產生過程。

    package mysocket;

    import java.net.*;

    public class MyCloseConnection
    {
        
    public static void printState(Socket socket, String name)
        {
            System.out.println(name 
    + ".isClosed():" + socket.isClosed());
            System.out.println(name 
    + ".isConnected():" + socket.isConnected());
            
    if (socket.isClosed() == false && socket.isConnected() == true)
                System.out.println(name 
    + "處于連接狀態!");
            
    else
                System.out.println(name 
    + "處于非連接狀態!");
            System.out.println();
        }

        
    public static void main(String[] args) throws Exception
        {
            Socket socket1 
    = null, socket2 = null;

            socket1 
    = new Socket("www.ptpress.com.cn"80);
            printState(socket1, 
    "socket1");

            socket1.getOutputStream().close();
            printState(socket1, 
    "socket1");

            socket2 
    = new Socket();
            printState(socket2, 
    "socket2");

            socket2.close();
            printState(socket2, 
    "socket2");
        }
    }

    運行上面的代碼后,將有如下的輸出結果:

        socket1.isClosed():false

    socket1.isConnected():true

    socket1處于連接狀態!

    socket1.isClosed():true

    socket1.isConnected():true

    socket1處于非連接狀態!

    socket2.isClosed():false

    socket2.isConnected():false

    socket2處于非連接狀態!

    socket2.isClosed():true

    socket2.isConnected():false

    socket2處于非連接狀態!

    從輸出結果可以看出,在socket1OutputStream關閉后,socket1也自動關閉了。而在上面的代碼我們可以看出,對于一個并未連接到服務端的Socket對象socket2,它的isClosed方法為false,而要想讓socket2isClosed方法返回true,必須使用socket2.close顯示地調用close方法。

    雖然在大多數的時候可以直接使用Socket類或輸入輸出流的close方法關閉網絡連接,但有時我們只希望關閉OutputStreamInputStream,而在關閉輸入輸出流的同時,并不關閉網絡連接。這就需要用到Socket類的另外兩個方法:shutdownInputshutdownOutput,這兩個方法只關閉相應的輸入、輸出流,而它們并沒有同時關閉網絡連接的功能。和isClosedisConnected方法一樣,Socket類也提供了兩個方法來判斷Socket對象的輸入、輸出流是否被關閉,這兩個方法是isInputShutdown()isOutputShutdown()。下面的代碼演示了只關閉輸入、輸出流的過程:

    package mysocket;

    import java.net.*;

    public class MyCloseConnection1
    {
        
    public static void printState(Socket socket)
        {
            System.out.println(
    "isInputShutdown:" + socket.isInputShutdown());
            System.out.println(
    "isOutputShutdown:" + socket.isOutputShutdown());
            System.out.println(
    "isClosed:" + socket.isClosed());
            System.out.println();
        }

        
    public static void main(String[] args) throws Exception
        {
            Socket socket 
    = new Socket("www.ptpress.com.cn"80);
            printState(socket);

            socket.shutdownInput();
            printState(socket);

            socket.shutdownOutput();
            printState(socket);
        }
    }

    在運行上面的代將得到如下的輸出結果

        isInputShutdown:false

    isOutputShutdown:false

    isClosed:false

    isInputShutdown:true

    isOutputShutdown:false

    isClosed:false

    isInputShutdown:true

    isOutputShutdown:true

    isClosed:false

    從輸出結果可以看出,isClosed方法一直返回false,因此,可以肯定,shutdownInputshutdownOutput并不影響Socket對象的狀態。

    下一篇:
    Java網絡編程從入門到精通(14):多種多樣的建立網絡連接的方式





    Android開發完全講義(第2版)(本書版權已輸出到臺灣)

    http://product.dangdang.com/product.aspx?product_id=22741502



    Android高薪之路:Android程序員面試寶典 http://book.360buy.com/10970314.html


    新浪微博:http://t.sina.com.cn/androidguy   昵稱:李寧_Lining

    posted on 2009-05-14 10:21 銀河使者 閱讀(9248) 評論(3)  編輯  收藏 所屬分類: java 原創網絡編程

    評論

    # re: Java網絡編程從入門到精通(13):使用Socket類接收和發送數據  回復  更多評論   

    不錯。。。
    2009-05-14 14:29 | .........

    # re: Java網絡編程從入門到精通(13):使用Socket類接收和發送數據  回復  更多評論   

    如果只是建立一個未連接的Socket對象,isClose也同樣返回true。如下面的代碼將輸出false。

    Socket socket = new Socket();
    System.out.println(socket.isClosed());



    這里前后矛盾了。isClose也同樣返回true,這個說法是錯的吧,應該是返回false
    2009-05-25 10:06 | MC

    # re: Java網絡編程從入門到精通(13):使用Socket類接收和發送數據  回復  更多評論   

    @MC
    對,這個寫錯了,已經改過來了。感謝MC的提醒。哈哈
    2009-05-25 11:07 | 銀河使者
    主站蜘蛛池模板: 日本免费污片中国特一级| 久久精品国产亚洲AV大全| 久久精品国产亚洲av瑜伽| 黄色网址免费观看| 亚洲综合综合在线| 每天更新的免费av片在线观看| 亚洲图片一区二区| 67194成手机免费观看| 亚洲精品国产手机| 免费国产成人高清在线观看网站| 亚洲一区二区三区久久久久| 国产免费看JIZZ视频| 亚洲熟妇少妇任你躁在线观看| 女人张开腿给人桶免费视频 | 日韩毛片免费无码无毒视频观看 | 国产精品亚洲专区无码WEB| 亚洲国产精品乱码在线观看97| 亚洲乱码中文字幕手机在线| 一二三四免费观看在线视频中文版 | 亚洲熟妇无码一区二区三区导航| 亚洲午夜在线一区| jzzijzzij在线观看亚洲熟妇| 国产成人va亚洲电影| 两个人看的www视频免费完整版| 99爱视频99爱在线观看免费| 无码一区二区三区免费视频| 国产一区二区三区在线免费| 在线播放亚洲第一字幕| 亚洲精品国产电影午夜| 无人视频免费观看免费视频| 一级成人a毛片免费播放| 成年女人毛片免费播放人| 成人亚洲性情网站WWW在线观看| 色婷婷六月亚洲婷婷丁香| AV激情亚洲男人的天堂国语| 老司机免费午夜精品视频| 国产成人精品免费视频大全麻豆| 亚洲狠狠狠一区二区三区| 国产片AV片永久免费观看 | 日韩免费视频在线观看| 久久www免费人成看国产片|