熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

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

2013-11-23 18:52:50  來源: Java核心技術 

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

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

   連接服務器

   發送和接收數據

   關閉網絡連接

  連接服務器

  在客戶端可以通過兩種方式來連接服務器一種是通過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 *;

public class MyConnection
{
    public static void main(String[] args)
    {
        try
        {
            if (argslength > )
            {
                Socket socket = new Socket(args[] );
                Systemoutprintln(args[] + 已連接成功!);
            }
            else
                Systemoutprintln(請指定IP或域名!);
        }
        catch (Exception e)
        {
            Systemerrprintln(錯誤信息 + egetMessage());
        }
    }
}

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

  測試正確的IP

  java mysocketMyConnection

  輸出結果已經連接成功!

  測試錯誤的IP

  java mysocketMyConnection

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

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

  測試正確的域名

  java mysocketMyConnection

  輸出結果已經連接成功!

  測試錯誤的域名

  java mysocketMyConnection

  輸出結果錯誤信息

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

   package mysocket;

import *;

public class MyConnection extends Thread
{
    private int minPort maxPort;

    public MyConnection(int minPort int maxPort)
    {
        thisminPort = minPort;
        thismaxPort = maxPort;
    }

    public void run()
    {
        for (int i = minPort; i <= maxPort; i++)
        {
            try
            {
                Socket socket = new Socket( i);
                Systemoutprintln(StringvalueOf(i) + :ok);
                socketclose();
            }
            catch (Exception e)
            {
            }
        }
    }
    public static void main(String[] args)
    {
        int minPort = IntegerparseInt(args[]) maxPort = Integer
                parseInt(args[]);
        int threadCount = IntegerparseInt(args[]);
        int portIncrement = ((maxPort  minPort + ) / threadCount)
                + (((maxPort  minPort + ) % threadCount) ==  ?  : );
        MyConnection[] instances = new MyConnection[threadCount];
        for (int i = ; i < threadCount; i++)
        {
            instances[i] = new MyConnection(minPort + portIncrement * i minPort
                    + portIncrement   + portIncrement * i);
            instances[i]start();
        }
    }
}

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

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

   java mysocketMyConnection   

  發送和接收數據

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

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

   package mysocket;

import *;
import javaio*;

public class MyConnection
{
    public static void main(String[] args) throws Exception
    {
        Socket socket = new Socket( );
        // 向服務端程序發送數據
        OutputStream ops  = socketgetOutputStream();        
        OutputStreamWriter opsw = new OutputStreamWriter(ops);
        BufferedWriter bw = new BufferedWriter(opsw);
        
        bwwrite(hello world\r\n\r\n);
        bwflush();
        
        // 從服務端程序接收數據
        InputStream ips = socketgetInputStream();
        InputStreamReader ipsr = new InputStreamReader(ips);
        BufferedReader br = new BufferedReader(ipsr);
        String s = ;        
        while((s = brreadLine()) != null)
            Systemoutprintln(s);        
        socketclose();
    }
}

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

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

   在發送字符串時之所以在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如果是這樣服務端程序就不會向客戶端發送響應信息而brreadLine()將因無法讀以響應信息面被阻塞直到連接超時

  關閉網絡連接

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

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

    直接調用Socket類的close方法

    只要Socket類的InputStream和OutputStream有一個關閉網絡連接自動關閉(必須通過調用InputStream和OutputStream的close方法關閉流才能使網絡可愛接自動關閉)

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

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

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

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

   Socket socket = new Socket();
Systemoutprintln(socketisClosed());

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

   package mysocket;

import *;

public class MyCloseConnection
{
    public static void printState(Socket socket String name)
    {
        Systemoutprintln(name + isClosed(): + socketisClosed());
        Systemoutprintln(name + isConnected(): + socketisConnected());
        if (socketisClosed() == false && socketisConnected() == true)
            Systemoutprintln(name + 處於連接狀態!);
        else
            Systemoutprintln(name + 處於非連接狀態!);
        Systemoutprintln();
    }

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

        socket = new Socket( );
        printState(socket socket);

        socketgetOutputStream()close();
        printState(socket socket);

        socket = new Socket();
        printState(socket socket);

        socketclose();
        printState(socket socket);
    }
}

  運行上面的代碼後將有如下的輸出結果

  socketisClosed():false

  socketisConnected():true

  socket處於連接狀態!

  socketisClosed():true

  socketisConnected():true

  socket處於非連接狀態!

  socketisClosed():false

  socketisConnected():false

  socket處於非連接狀態!

  socketisClosed():true

  socketisConnected():false

  socket處於非連接狀態!

  從輸出結果可以看出在socket的OutputStream關閉後socket也自動關閉了而在上面的代碼我們可以看出對於一個並未連接到服務端的Socket對象socket它的isClosed方法為false而要想讓socket的isClosed方法返回true必須使用socketclose顯示地調用close方法

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

   package mysocket;

import *;

public class MyCloseConnection
{
    public static void printState(Socket socket)
    {
        Systemoutprintln(isInputShutdown: + socketisInputShutdown());
        Systemoutprintln(isOutputShutdown: + socketisOutputShutdown());
        Systemoutprintln(isClosed: + socketisClosed());
        Systemoutprintln();
    }

    public static void main(String[] args) throws Exception
    {
        Socket socket = new Socket( );
        printState(socket);

        socketshutdownInput();
        printState(socket);

        socketshutdownOutput();
        printState(socket);
    }
}

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

  isInputShutdown:false

  isOutputShutdown:false

  isClosed:false

  isInputShutdown:true

  isOutputShutdown:false

  isClosed:false

  isInputShutdown:true

  isOutputShutdown:true

  isClosed:false

  從輸出結果可以看出isClosed方法一直返回false因此可以肯定shutdownInput和shutdownOutput並不影響Socket對象的狀態


From:http://tw.wingwit.com/Article/program/Java/hx/201311/25915.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.