第二章 Socket用法詳解


構造Socket

  Socket構造方法如下:

 1 Socket() 
 2 //Creates an unconnected socket, with the system-default type of SocketImpl.
 3  
 4 Socket(InetAddress address, int port) 
 5 //Creates a stream socket and connects it to the specified port number at the  
 6 //specified IP address.
 7  
 8 Socket(InetAddress host, int port, boolean stream) 
 9 //Deprecated.  
10 //Use DatagramSocket instead for UDP transport.
11  
12 Socket(InetAddress address, int port, InetAddress localAddr, int localPort) 
13 //Creates a socket and connects it to the specified remote address on the 
14 //specified remote port.
15  
16 Socket(Proxy proxy) 
17 //Creates an unconnected socket, specifying the type of proxy, if any, that 
18 //should be used regardless of any other settings.
19  
20 Socket(SocketImpl impl) 
21 //Creates an unconnected Socket with a user-specified SocketImpl.
22  
23 Socket(String host, int port) 
24 //Creates a stream socket and connects it to the specified port number on the  
25 named host.
26  
27 Socket(String host, int port, boolean stream) 
28 //Deprecated.  
29 //Use DatagramSocket instead for UDP transport.
30  
31 Socket(String host, int port, InetAddress localAddr, int localPort) 
32 //Creates a socket and connects it to the specified remote host on the specified 
33 //remote port. 

  除了第一個無參,其余構造方法都試圖建立與服務器的連接,如果成功則返回Socket對象,否在拋出異常。

  根據以上構造方法來創建一個類,用於掃描主機上1-1024之間的端口是否被服務器程序監聽(如果被監聽,就可以返回Socket對象)。代碼如下:

import java.io.IOException;
import java.net.Socket;

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 port=1;port<=1024;port++){
            try{
                socket=new Socket(host,port);
                System.out.println("There is a server on port "+port);
            }catch(IOException e){
                System.out.println("Can't connect to port "+port);
            }finally{
                if(socket!=null){
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}

  2.1.1 設定等待建立連接的超時時間

    當需要設定連接超時時間時,則需要調用Socket的無參構造函數。   

Socket socket = new Socket();
//SocketAddress 提供不可變對象,供套接字用於綁定、連接或用作返回值。
SocketAddress remoteAddr = new InetSocketAddress("localhost",8000);
//超時未連接時,會拋出超時異常。
socket.connect(remoteAddr,60000);//毫秒為單位,0表示用於不超時

  2.1.2 設定服務器地址

    除了第一個無參構造函數,其余都需要提供服務器IP或主機名,以及端口號。    

1 Socket(InetAddress address,int port)  //第一個參數表示主機IP地址
2 Socket(String host,int port)  //第一個表示主機名
View Code

    InetAddress類表示服務器的IP地址,詳情查看這里

  2.1.3 設定客戶端地址

  一個socket對象應該包含遠程服務器的IP和端口信息,也包含本地客戶機的IP地址和端口信息。默認情況下,客戶機的IP來自於本地主機,端口有操作系統自動分配。也可以顯式的設置客戶端的IP和端口。  

 Socket(InetAddress address,int port,InetAddress localAddress,int localPort)
 Socket(String host,int port,int port,InetAddress localAddress,int localPort)

  2.1.4 客戶機連接服務器可能出現的異常

UnKnownHostException 無法識別主機的名字或IP地址
ConnectException 服務器沒有對應的端口或服務器拒絕連接
SocketTimeoutException 等待連接超時
BindException 無法與指定的本地IP或端口綁定
   
   
   
   

獲取Socket信息

  以下方法可獲得Socket相關信息:

 1 InetAddress getInetAddress() 
 2 //Returns the address to which the socket is connected.
 3  
 4 InputStream getInputStream() 
 5 //Returns an input stream for this socket.
 6 
 7 OutputStream getOutputStream() 
 8 //Returns an output stream for this socket. 
 9  
10 int getPort() 
11 //Returns the remote port number to which this socket is connected. 
12  
13 InetAddress getLocalAddress() 
14 //Gets the local address to which the socket is bound.
15  
16 int getLocalPort() 
17 //Returns the local port number to which this socket is bound. 

關閉Socket

  部分代碼如下:

if(socket!=null){
    try {
        socket.close();
    } catch (IOException e) {
        e.printStackTrace();
        }
    }
}

  Socket提供了3個狀態測試方法:

boolean isBound() 
//Returns the binding state of the socket.
 
boolean isClosed() 
//Returns the closed state of the socket.
 
boolean isConnected()  
//Returns the connection state of the socket.

 

半關閉Socket

  進程A與進程B通信時,A傳輸數據到B,如何告知B所有數據已經傳輸完畢呢?以下幾個方法:

  (1) 發送一行特殊的字符串,如前一章使用的“bye”,告知輸出完畢。

  (2) A先告訴B字符串的長度,在向B傳輸數據。

  (3) A發送完成后,關閉Socket。此時B讀完數據后,在此執行read()時,該方法返回-1,如果執行BufferedReader的readLine()方法,則返回null。以此表示到達輸入流末尾。

  (4) 關閉Socket的輸入輸出流

shutdownInput():關閉輸入流

shutdownOutput():關閉輸出流

對應兩個狀態測試方法:

  isInputShutdown()

  isOutputShutdown()

設置Socket的選項 

  TCP_NODEALY:表示立即 發送數據。

  默認情況下,發送數據會先放在緩沖區,緩存區滿了在發出去,並等待接收方的響應,然后再發下一批數據。這種模式適合發送大量數據,並且會得到及時響應的場合。發送小數據時這種模式速度很慢,調用setTcpNoDelay(true)可以關閉緩存區。設置之前先調用socket.getTcpNoDelay()方法查看底層是否支持TCP_NODEALY選項。

  SO_RESUSEADDR:表示是否允許重用Socket所綁定的本地地址。

  當socket執行close()方法后,底層Socket不會立刻釋放本地端口,而是等待一會,確保接收到網絡發送的延遲數據,然后再釋放。這樣可以確保這些數據不會被其他綁定到該端口的新進程接收到。為了確保Socket關閉后即使端口未被釋放,其他進程也可以綁定該端口,可以調用setResuseAddress(true).

  SO_TIMEOUT:表示接受數據時的等待時間。

  設定接收數據的等待超時時間,超過后拋出異常。

  SO_LINGER:當執行close()關閉Socket時,是否立即關閉底層的Socket。

  用來控制Socket關閉后的行為。默認下,關閉Socket后,底層不會立即關閉,延遲一段時間,等待剩余數據發送完成,才會關閉底層。

  執行以下方法:socket.setSoLinger(true,0);則Socket執行close()方法后,會立即關閉底層,未發送完的數據被丟棄。

  執行以下方法:socket.setSoLinger(true,3600);表示執行close()方法后進入阻塞狀態。當所有數據發送完成后或者阻塞時間超過3600秒(以秒為單位)才會返回。

  SO_SNDBUF:發送數據的緩沖區大小。

    設置輸出數據的緩存區大小。

  SO_RCVBUF:接收數據的緩沖區大小。

    設置輸入數據的緩存區大小。

  SO_KEEPALIVE:表示對於空閑的Socket,是否把它關閉。

  選項為true時,底層TCP會對該連接進行監視。當連接空閑狀態超過2小時,本地TCP會發送一個數據包給遠程Socket。如果未收到響應,則持續嘗試11分鍾。在12分鍾內沒有收到響應的話,TCP會自動關閉本地Socket。

  OOBINLINE:表示是否支持發送一個字節的TCP緊急數。

 

  服務類型選項

  設置服務類型:setTrafficClass(int trafficClass)

  低成本:0x02

  高可靠性:0x04

  最高吞吐量:0x08

  最小延遲:0x10

  以下代碼設置請求高可靠性和最小延遲:

  socket.setTrafficClass(0x04|0x10);

 

送郵件的SMTP客戶程序

  略


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM