Java網絡編程由淺入深二 Socket的構造和連接服務端的相關異常

Socket構造方法和Socket的設置與異常

本文將介紹Socket的構造方法和Socket的相關屬性設置與異常處理

  • 構造Socket
  • 設置等待超時時間
  • 設置服務器地址
  • 設置客戶端地址
  • 客戶端連接服務器可能出現的異常

構造Socket

Socket的構造方法有如下幾種重載方式:

Socket();
Socket(InetAddress address, int port)
Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
Socket(Proxy proxy)
Socket(SocketImpl impl)
Socket(String host, int port)
Socket(String host, int port, InetAddress localAddr, int localPort)

除了第一個構造器外,其他構造器都會嘗試與服務器建立連接,如果連接成功返回Socket對象;如果因爲某些原因連接失敗,就拋出IOException。
如下代碼掃描主機上從1到1024之間的端口,判斷這些端口是否已經被服務器程序監聽。

public class PortScanner {
    public static void main(String[] args) {
        String host = "localhost";
        new PortScanner().scan(host);
    }

    public void scan(String host){
        Socket socket = null;
        for(int i=0;i<1024;i++){
            try {
                socket = new Socket(host, i);
                System.out.println("There is a server on port "+i);
            }  catch (IOException e) {
                System.out.println("Can't connect to port "+i);
            }finally{
                if(socket!=null){
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

設置等待建立連接的超時時間

使用不帶參數的構造方法,設置socket連接超時時間:

Socket socket = new Socket();
SocketAddress endPoint = new InetSocketAddress("localhost", 8000);
socket.connect(endPoint, 60000);

以上代碼表示用於連接本機上監聽的8000端口,等待連接的最長時間爲1分鐘。如果在1分鐘內連接成功,則connect()方法順利返回;如果在1分鐘之內出現異常,則拋出異常;如果超過1分鐘,既沒有連接成功,也沒有拋出異常,那麼會拋出SocketTimeoutExceptionsocket. connect(SocketAddress endpoint, int timeout);負責連接服務器,參數endpoint指定服務器地址,參數timeout設定超時時間,以毫秒爲單位。如果參數timeout爲0,表示永遠不超時。

設置服務器地址

Socket的構造方法中,除了第一個不帶參數的構造方法,其他構造方法都需要指定服務器地址,包括服務器的IP或主機名,以及端口:
Socket(InetAddress address, int port)
Socket(String host, int port)
InetAddress類表示服務器的IP地址,InetAddress提供了很多靜態方法:

    // 返回本地主機的IP地址
    InetAddress.getLocalHost();
    // 返回代表10.202.164.65的IP地址
    InetAddress.getByName("10.202.164.65");
    // 返回域名爲'www.csdn.net'的ip地址
    InetAddress.getByName("www.csdn.net");

設置客戶端的地址:

默認情況下,客戶端的IP地址來自於客戶端程序所在的主機,客戶端的端口則由操作系統隨機分配。但是Socket類還是提供了構造方法允許顯式地設置客戶端的IP和端口:

//參數localAddr和localPort用來設置客戶端的IP和端口。
Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
Socket(String host, int port, InetAddress localAddr, int localPort)

客戶端連接服務器可能拋出的異常

當Socket構造方法請求連接服務器時,可能會拋出下面的異常:
• UnknownHostException:如果無法識別主機的名字或IP地址,就會拋出這種異常

• ConnectException:如果沒有服務器進程監聽指定的端口,或者服務器拒絕連接,就會拋出這種異常。

• SocketTimeoutException:如果等待連接超時,就會拋出這種異常。

• BindException:如果無法把Socket對象與指定的本地IP地址或端口綁定,就會拋出這種異常。

通過下面測試類爲例,演示拋出異常的原因。

public class ConnectTester {
    public static void main(String[] args) {
        String host = "www.csdn.net";
        int port = 12312;

        new ConnectTester().connect(host, port);
    }

    public void connect(String host,int port){
        SocketAddress remoteAddress = new InetSocketAddress(host, port);
        Socket socket = null;
        String result = null;
        try{
            socket = new Socket();
            long start = System.currentTimeMillis();
            socket.connect(remoteAddress,1000);
            long end = System.currentTimeMillis();
            result = (end-start)+"ms";
        }catch(BindException bindException){
            result = "BindException,Socket對象與指定的本地IP地址或端口綁定,異常";
        }catch (UnknownHostException unknownHostException) {
            result = "UnknownHostException,無法識別的主機";
        }catch (ConnectException connectException) {
            result = "ConnectException,連接被拒絕";
        }catch (SocketTimeoutException socketTimeoutException) {
            result = "SocketTimeoutException,連接超時";
        }catch(IOException e){
            result = "IOException,連接失敗";
        }finally{
            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println(remoteAddress+" : "+result);
    }
}

• 拋出UnknownHostException情況:
如果無法識別主機的名字或IP地址,就會拋出這種異常。例如:host爲:’ somehost11’。Socket的connect方法就會拋出UnknownHostException異常。
• 拋出ConnectException的情況:
在以下兩種情況會拋出ConnectException。
1) 沒有服務器進程監聽指定的端口。例如:host爲 ‘localhost’ port爲 12321 。如果本機的12321端口沒有被任何進程監聽,則Socket連接方法會拋出ConnectException。
2) 服務器進程拒絕連接。介紹服務器進程拒絕客戶的連接請求的情形。如下示例代碼,一個簡單的服務程序ServerSocket構造方法中的第二個參數表示請求隊列的長度。如果隊列的請求已滿,服務器就會拒絕其餘的請求。拋出ConnectException

public class SimplServer {
    public static void main(String[] args) throws Exception{
        ServerSocket serverSocket = new ServerSocket(8888, 2);
        Thread.sleep(3600000);
    }
}
public class SimpleClient {
    public static void main(String[] args) throws Exception{
        String host = "localhost";
        int port = 8888;
        Socket s1 = new Socket(host, port);
        System.out.println("第一次連接成功");
        Socket s2 = new Socket(host, port);
        System.out.println("第二次連接成功");
        Socket s3 = new Socket(host, port);
        System.out.println("第三次連接成功");
    }
}

• 拋出SocketTimeoutException的情形
如果客戶端連接超時,就會拋出這種異常。修改 socket.connect(remoteAddress, 1);由原來的1000毫秒修改爲1毫秒,這樣增加了超時的可能性。

• 拋出BindException的情形:
將代碼

socket = new Socket();
socket.connect(remoteAddress, 1000);

修改爲:

socket = new Socket();
socket.bind(new InetSocketAddress(InetAddress.getByName("222.34.5.6"), 5678));
socket.connect(remoteAddress, 1000);

修改後的代碼試圖把Socket的本地IP地址設爲222.34.5.6,把本地端口設置爲5678。如果本機不具有該IP,或者端口被佔用,那麼就會出現BindException。


歡迎關注微信公衆號 在路上的coder 每天分享優秀的Java技術文章!
掃描二維碼關注:這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章