java Socket應用

一、Socket使用時應當注意的一些問題

1.設置超時,從套接字讀取信息時,在有數據可供訪問之前,讀操作會被阻塞,如果此時主機不可達,那麼程序將會等待很長時間,並因爲系統操作系統的限制最終導致超時

調用setSoTimeout方法設置

Socket s = new Socket(...);
s.setSoTimeout(10000);

對構造器Socket(String host,int port),可以先構建一個無連接的套接字,再使用超時

Socket s = new Socket();
s.connect(new InetSocketAddress(host,port),timeout);
````

2.可中斷套接字,用`SocketChannel`類
3.需要解析因特網地址時,可以用`InetAddress`類
4.爲多個客戶端服務時,可以用多線程解決
5.半關閉:套接字連接的一段UN可以終止其輸出,同時仍可以接受來自另一端的數據,反過來也一樣,調用`Socket.shutdownInput`或`Socket.shutdownOutput`

----




<div class="se-preview-section-delimiter"></div>

### 二、獲取Web數




<div class="se-preview-section-delimiter"></div>

#### URI和URL
- URL是URI的一個特例,URI是個純粹的語法結構,包含用來點位Web資源的字符串和各種組成功哪部分,URL包含了用於定位Web資源的足夠信息,其他無法定位任何數據的URI,稱之爲URN
- 一個URI具有一下語法:`[scema:]schemaSpecficPart[#fragment]`

> i.包含schema:部分的URI成爲絕對URI,否則爲相對URI
ii.絕對URI的schemaSpecficPart不是以`/`揩油,則稱爲不透明的,如:`mialto:pinnuli!hostname.com`
iii.所有絕對的透明URI和所有相對URI都是分層的,如:`http://hostname.com/index.html`,`../../java/net/Socket.html#Socket()`
iv.一個分層URI的URI的schemaSpecficPart具有一下結構:[//authority][path][?query],基於服務器的URI,authority具有一下形式:[user-info@]host[:port]

- java中URI類的作用
    - 解析表示福並將它分解成各種不同組成成分
    - 標識符的相對化和解析相對標識符





<div class="se-preview-section-delimiter"></div>

#### 使用URLCollection
    > URLConnection類可以比URL類有更多的控制

必須嚴格按照以下步驟進行操作:
1.調用URL類中的openConnection方法得到URLConnection對象:`URLConnection connection = url.openConnection();`
2.設置請求屬性
3.調用connect方法連接遠程資源:connection.connect();
4.建立連接後,可以查詢頭信息
5.訪問資源數據,使用getInputStream方法獲取一個輸入流

> 這裏的getInputStream/getOutputStream與Socket類的又很大的不同,這裏具有很多處理請求和響應消息頭時的強大功能

----




<div class="se-preview-section-delimiter"></div>

### 三、提交表單
1.提交數據之前,需要創建一個URLConnection對象




<div class="se-preview-section-delimiter"></div>

```java
URL url = new URL("http;??host/script");
URLConnection connection = url.openConnection();

2.調用setDoOutput方法建立一個輸出的連接

connection.setRequestMethod("POST");
connection.setDoOutput(true);

3.調用getOutputStream方法獲得一個輸出流,想服務器發送數據

OutputStreamWriter osw = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
osw.write(name1 + "=" + URLEncoder.eccode(value1,"UTF-8") + "&);
osw.write(name2 + "=" + URLEncoder.encode(value2,"UTF-8"));
    ```
4.關閉輸出流
```java
osw.flush();
osw.close();




<div class="se-preview-section-delimiter"></div>

5.調用getInputStream方法對服務器的響應

BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
StringBuffer response = new StringBuffer();
String temp;
while ((temp = br.readLine()) != null) {
    response.append(temp);
    response.append("\n");
}




<div class="se-preview-section-delimiter"></div>

i.設置請求方法時,必須使用大寫,如POST,使用post無法識別
ii.如果想要獲取錯誤頁面,可以將URLConnection轉型爲HTTPURLConnection類並調用getErrorStream方法
InputStream err = ((HTTPURLConnection) connection).getErrorStream();

URL編碼需遵循以下規則:

i.保留字符A-Z、a-z、0-9 以及.-*_
ii.用+替換所有空格
iii.將其他所有字符編碼爲UTF-8,並將每個字節都編碼爲%後面緊跟一個兩位的十六進制數字
比如發送”New York, NY”,可以使用New+York%2C+NY


四、基於TCP的Socket通信

1.創建ServerSocket和Socket
2.打開連接到Socket的輸入/輸出流
3.按照協議對Socket進行讀/寫操作
4.關閉輸入/輸出流,關閉Socket
服務端(多線程響應多個客戶端)

//創建一個服務器端Socket,即ServerSocket,指定綁定的端口,並負責監聽此端口
ServerSocket serverSocket=new ServerSocket(8888);
Socket socket=null;
System.out.println("***服務器即將啓動,等待客戶端的連接***");
while(true){
    //調用accept()方法開始監聽,等待客戶端的連接
    socket=serverSocket.accept();
    //創建一個新的線程
    ServerThread serverThread=new ServerThread(socket);
    //啓動線程
    serverThread.start();
}




<div class="se-preview-section-delimiter"></div>

ServerThread類

```java
public class ServerThread extends Thread {
Socket socket = null;
public ServerThread(Socket socket) {
    this.socket = socket;
}

//線程執行的操作,響應客戶端的請求
public void run(){
    InputStream is=null;
    InputStreamReader isr=null;
    BufferedReader br=null;
    OutputStream os=null;
    PrintWriter pw=null;
    try {
        //獲取輸入流,並讀取客戶端信息
        is = socket.getInputStream();
        isr = new InputStreamReader(is);
        br = new BufferedReader(isr);
        String info=null;
        while((info=br.readLine())!=null){//循環讀取客戶端的信息
            System.out.println("我是服務器,客戶端說:"+info);
        }
        socket.shutdownInput();//關閉輸入流,半關閉
        //獲取輸出流,響應客戶端的請求
        os = socket.getOutputStream();
        pw = new PrintWriter(os);
        pw.write("歡迎您!");
        pw.flush();//調用flush()方法將緩衝輸出
    } catch (IOException e) {
        e.printStackTrace();
    } finally{
        //關閉資源
        try {
            if(pw!=null)
                pw.close();
            if(os!=null)
                os.close();
            if(br!=null)
                br.close();
            if(isr!=null)
                isr.close();
            if(is!=null)
                is.close();
            if(socket!=null)
                socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

}

```

客戶端

```java
//1.創建客戶端Socket,指定服務器地址和端口
Socket socket=new Socket("localhost", 8888);
//2.獲取輸出流,向服務器端發送信息
OutputStream os=socket.getOutputStream();//字節輸出流
PrintWriter pw=new PrintWriter(os);//將輸出流包裝爲打印流
pw.write("用戶名:alice;密碼:789");
pw.flush();
socket.shutdownOutput();//關閉輸出流
//3.獲取輸入流,並讀取服務器端的響應信息
InputStream is=socket.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is));
String info=null;
while((info=br.readLine())!=null){
    System.out.println("我是客戶端,服務器說:"+info);
}
//4.關閉資源
br.close();
is.close();
pw.close();
os.close();
socket.close();
```

五、基於UDP的SOcket通信

1.定義發送信息
2.創建DatagramPacket,包含將要發送的信息
3.創建DatagramSocket
4.發送數據
服務端
- 接收客戶端發送的數據

//1.創建服務器端DatagramSocket,指定端口
DatagramSocket socket=new DatagramSocket(8800);
//2.創建數據報,用於接收客戶端發送的數據
byte[] data =new byte[1024];//創建字節數組,指定接收的數據包的大小
DatagramPacket packet=new DatagramPacket(data, data.length);
//3.接收客戶端發送的數據
socket.receive(packet);//此方法在接收到數據報之前會一直阻塞
//4.讀取數據
String info=new String(data, 0, packet.getLength());
System.out.println("我是服務器,客戶端說:"+info);




<div class="se-preview-section-delimiter"></div>
  • 向客戶端響應數據
//1.定義客戶端的地址、端口號、數據
InetAddress address=packet.getAddress();
int port=packet.getPort();
byte[] data2="歡迎您!".getBytes();
//2.創建數據報,包含響應的數據信息
DatagramPacket packet2=new DatagramPacket(data2, data2.length, address, port);
//3.響應客戶端
socket.send(packet2);
//4.關閉資源
socket.close();




<div class="se-preview-section-delimiter"></div>

客戶端
- 向服務器端發送數據

//1.定義服務器的地址、端口號、數據
InetAddress address=InetAddress.getByName("localhost");
int port=8800;
byte[] data="用戶名:admin;密碼:123".getBytes();
//2.創建數據報,包含發送的數據信息
DatagramPacket packet=new DatagramPacket(data, data.length, address, port);
//3.創建DatagramSocket對象
DatagramSocket socket=new DatagramSocket();
//4.向服務器端發送數據報
socket.send(packet);




<div class="se-preview-section-delimiter"></div>
  • 接收服務器端響應的數據
//1.創建數據報,用於接收服務器端響應的數據
byte[] data2=new byte[1024];
DatagramPacket packet2=new DatagramPacket(data2, data2.length);
//2.接收服務器響應的數據
socket.receive(packet2);
//3.讀取數據
String reply=new String(data2, 0, packet2.getLength());
System.out.println("我是客戶端,服務器說:"+reply);
//4.關閉資源
socket.close();

當Socket關閉時,輸入輸出流也就關閉了

參閱:
慕課網:Java Socket應用—通信是這樣練成的
java核心技術 卷II:高級特性

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