一、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關閉時,輸入輸出流也就關閉了