和網絡編程有關的基本API位於java.net包中,該包中包含了基本的網絡編程實現,該包是網絡編程的基礎。該包中既包含基礎的網絡編程類,也包含封裝後的專門處理WEB相關的處理類。在本章中,將只介紹基礎的網絡編程類。html
首先來介紹一個基礎的網絡類——InetAddress類。該類的功能是表明一個IP地址,而且將IP地址和域名相關的操做方法包含在該類的內部。java
關於該類的使用,下面經過一個基礎的代碼示例演示該類的使用,代碼以下:android
import java.net.*; /** * 演示InetAddress類的基本使用 */ public class InetAddressDemo { public static void main(String[] args) { try{ //使用域名建立對象 InetAddress inet1 = InetAddress.getByName("www.163.com"); System.out.println(inet1); //使用IP建立對象 InetAddress inet2 = InetAddress.getByName("127.0.0.1"); System.out.println(inet2); //得到本機地址對象 InetAddress inet3 = InetAddress.getLocalHost(); System.out.println(inet3); //得到對象中存儲的域名 String host = inet3.getHostName(); System.out.println("域名:" + host); //得到對象中存儲的IP String ip = inet3.getHostAddress(); System.out.println("IP:" + ip); }catch(Exception e){} } }
運行結果:web
www.163.com/60.174.243.159 /127.0.0.1 ifly-XXX/10.1.xxx.xxx 域名:ifly-XXX IP:10.1.xxx.xxx
Socket編程編程
Java爲TCP協議提供了兩個類,分別在客戶端編程和服務器端編程中使用它們。在應用程序開始通訊以前,須要先建立一個鏈接,由客戶端程序發起;而服務器端的程序須要一直監聽着主機的特定端口號,等待客戶端的鏈接。在客戶端中咱們只須要使用Socket實例,而服務端要同時處理ServerSocket實例和Socket實例;兩者而且都使用OutputStream和InpuStream來發送和接收數據。api
服務器端:瀏覽器
學習一種知識最好的方式就是使用它,經過前面的筆記,咱們已經知道如何獲取主機的地址信息,如今咱們經過一個簡單的程序來初步學習傳輸層使用了TCP協議的Socket編程。緩存
在Socket編程中,服務器端遠比客戶端要複雜得多。服務器端的工做就是創建一個通訊終端,被動的等待客戶端的鏈接。下面這個服務器端程序的示例的做用是:監遵從控制檯輸入獲取的端口號,而且將客戶端發送過來的消息,再發送回去。服務器
import java.net.*; import java.text.MessageFormat; import java.io.*; public class TCPEchoServer { private static final int BUFSIZE = 32; /** * @param args */ public static void main(String[] args) throws IOException { // TODO Auto-generated method stub // 從控制檯獲取須要監聽的端口號 if (args.length != 1) throw new IllegalArgumentException("Parameter(s):<Port>"); // 獲取端口號 int servPort = Integer.parseInt(args[0]); // 實例化一個ServerSocket對象實例 ServerSocket servSocket = new ServerSocket(servPort); System.out.println(MessageFormat.format("開始啓動監聽,端口號:{0}", args[0])); // 初始接收數據的總字節數 int recvMsgSize; // 接收數據的緩衝區 byte[] receiveBuf = new byte[BUFSIZE]; // 循環迭代,監聽端口號,處理新的鏈接請求 while (true) { // 阻塞等待,每接收到一個請求就建立一個新的鏈接實例 Socket clntSocket = servSocket.accept(); // 獲取鏈接的客戶端的 SocketAddress SocketAddress clientAddress = clntSocket.getRemoteSocketAddress(); // 打印輸出鏈接客戶端地址信息 System.out.println("Handling client at" + clientAddress); // 從客戶端接收數據的對象 InputStream in = clntSocket.getInputStream(); // 向客戶端發送數據的對象 OutputStream out = clntSocket.getOutputStream(); // 讀取客戶端發送的數據後,再發送到客戶端 while ((recvMsgSize = in.read(receiveBuf)) != -1) { out.write(receiveBuf, 0, recvMsgSize); } // 客戶端關閉鏈接時,關閉鏈接 System.out.println(" 客戶端關閉鏈接"); clntSocket.close(); } } }
while ((recvMsgSize = in.read(receiveBuf)) != -1) { out.write(receiveBuf, 0, recvMsgSize); }
上述代碼中,服務端能夠屢次接受 客戶端的消息,並將消息回傳給客戶端,直到客戶端關閉socket時,循環才結束。網絡
在Socket編程中,首先客戶端須要向服務器端發送,而後被動的等待服務器端的響應。下面的示例中:咱們向服務器端發送信息,等待服務器端發送的消息,並打印顯示出來。
import java.io.*; import java.net.Socket; import java.net.SocketException; public class TCPEchoClient { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // TODO Auto-generated method stub // 判斷從控制檯接受的參數是否正確 if ((args.length < 2) || (args.length > 3)) throw new IllegalArgumentException("Parameter(s):<Server><Word>[<Port>]]"); // 獲取服務器地址 String server = args[0]; // 獲取須要發送的信息 byte[] data = args[1].getBytes(); // 若是有三個從參數那麼就獲取發送信息的端口號,默認端口號爲8099 int servPort = (args.length == 3) ? Integer.parseInt(args[2]) : 8099; // 根據服務器地址和端口號實例化一個Socket實例 Socket socket = new Socket(server, servPort); System.out.println("Connected to server...sending echo string"); // 返回此套接字的輸入流,即從服務器接受的數據對象 InputStream in = socket.getInputStream(); // 返回此套接字的輸出流,即向服務器發送的數據對象 OutputStream out = socket.getOutputStream(); // 向服務器發送從控制檯接收的數據 out.write(data); // 接收數據的計數器,將寫入數據的初始偏移量 int totalBytesRcvd = 0; // 初始化接收數據的總字節數 int bytesRcvd; while (totalBytesRcvd < data.length) { // 服務器關閉鏈接,則返回 -1,read方法返回接收數據的總字節數 if ((bytesRcvd = in.read(data, totalBytesRcvd, data.length - totalBytesRcvd)) == -1) throw new SocketException("與服務器的鏈接已關閉"); else System.out.println("client received data from client"); totalBytesRcvd += bytesRcvd; } // 打印服務器發送來的數據 System.out.println("Received:" + new String(data)); // 關閉鏈接 socket.close(); } }
運行服務端程序: 對8099端口進行監聽:
運行客戶端程序: 向服務器端發送消息(默認端口號爲8099),同時服務器發回消息
服務器端變成:監聽到 ip地址爲 10.1.206.238:59918 的socket發送消息。
HTTP是hypertext transfer protocol(超文本傳輸協議)的簡寫,它是TCP/IP協議的一個應用層協議,用於定義WEB瀏覽器與WEB服務器之間交換數據的過程。客戶端連上web服務器後,若想得到web服務器中的某個web資源,需遵照必定的通信格式,HTTP協議用於定義客戶端與web服務器通迅的格式。
http協議主要有兩個版本,他們的區別:
在HTTP1.0協議中,客戶端與web服務器創建鏈接後,只能得到一個web資源。
在HTTP1.1協議,容許客戶端與web服務器創建鏈接後,在一個鏈接上獲取多個web資源。
客戶端連上服務器後,向服務器請求某個web資源,稱之爲客戶端向服務器發送了一個HTTP請求。
一個完整的HTTP請求包括以下內容:一個請求行、若干消息頭、以及實體內容
請求行以一個方法符號開頭,以空格分開,後面跟着請求的URI和協議的版本,格式以下:Method Request-URI HTTP-Version CRLF
其中 Method表示請求方法;Request-URI是一個統一資源標識符;HTTP-Version表示請求的HTTP協議版本;CRLF表示回車和換行(除了做爲結尾的CRLF外,不容許出現單獨的CR或LF字符)。
請求方法(全部方法全爲大寫)有多種,各個方法的解釋以下:
GET 請求獲取Request-URI所標識的資源
POST 在Request-URI所標識的資源後附加新的數據
HEAD 請求獲取由Request-URI所標識的資源的響應消息報頭
PUT 請求服務器存儲一個資源,並用Request-URI做爲其標識
DELETE 請求服務器刪除Request-URI所標識的資源
TRACE 請求服務器回送收到的請求信息,主要用於測試或診斷
CONNECT 保留未來使用
OPTIONS 請求查詢服務器的性能,或者查詢與資源相關的選項和需求
HTTP請求中的經常使用消息頭
accept:瀏覽器經過這個頭告訴服務器,它所支持的數據類型
Accept-Charset: 瀏覽器經過這個頭告訴服務器,它支持哪一種字符集
Accept-Encoding:瀏覽器經過這個頭告訴服務器,支持的壓縮格式
Accept-Language:瀏覽器經過這個頭告訴服務器,它的語言環境
Host:瀏覽器經過這個頭告訴服務器,想訪問哪臺主機
If-Modified-Since: 瀏覽器經過這個頭告訴服務器,緩存數據的時間
Referer:瀏覽器經過這個頭告訴服務器,客戶機是哪一個頁面來的 防盜鏈
Connection:瀏覽器經過這個頭告訴服務器,請求完後是斷開連接仍是何持連接
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Referer: http://localhost:8080/JavaWebDemoProject/Web/2.jsp Accept-Language: zh-CN User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3) Accept-Encoding: gzip, deflate Host: localhost:8080 Connection: Keep-Alive
一個HTTP響應表明服務器向客戶端回送的數據,它包括: 一個狀態行、若干消息頭、以及實體內容
HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: text/html;charset=ISO-8859-1 Content-Length: 105 Date: Tue, 27 May 2014 16:23:28 GMT <html> <head> <title>Hello World JSP</title> </head> <body> Hello World! </body> </html>
狀態行格式: HTTP版本號 狀態碼 緣由敘述<CRLF>
舉例:HTTP/1.1 200 OK
狀態碼用於表示服務器對請求的處理結果,它是一個三位的十進制數。響應狀態碼分爲5類,以下所示:
HTTP響應中的經常使用響應頭(消息頭)
Location: 服務器經過這個頭,來告訴瀏覽器跳到哪裏
Server:服務器經過這個頭,告訴瀏覽器服務器的型號
Content-Encoding:服務器經過這個頭,告訴瀏覽器,數據的壓縮格式
Content-Length: 服務器經過這個頭,告訴瀏覽器回送數據的長度
Content-Language: 服務器經過這個頭,告訴瀏覽器語言環境
Content-Type:服務器經過這個頭,告訴瀏覽器回送數據的類型
Refresh:服務器經過這個頭,告訴瀏覽器定時刷新
Content-Disposition: 服務器經過這個頭,告訴瀏覽器如下載方式打數據
Transfer-Encoding:服務器經過這個頭,告訴瀏覽器數據是以分塊方式回送的
Expires: -1 控制瀏覽器不要緩存
Cache-Control: no-cache
Pragma: no-cache
http請求自帶緩存機制,當緩存中存在請求的數據時再也不請求(即便沒有網絡返回碼也是200)或者不作數據傳輸處理具體以下:
獲取資源形式 | 狀態碼 | 發送請求到服務器 | |
強緩存 | 從緩存取200(from cache) | 否,直接從緩存取 | |
協商緩存 | 從緩存取 | 304(not modified) | 是,正如其名,經過服務器來告知緩存是否可用 |
緩存機制:http://www.javashuo.com/article/p-nhkwogbj-cy.html
http://www.javashuo.com/article/p-femliszd-gp.html
初始化 定義HttpURLConnection , 經過BufferReader 按行讀取傳輸的數據 ,最後將數據賦值給一個StringBuilder
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
InputStream in = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
package com.iflytek.internetcommunicate; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Button; import android.widget.TextView; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private TextView text; @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // button : push send an request for get infomation of www.baidu.com Button sendRequest = (Button) findViewById(R.id.ConnectInternet); // text : dis the info in text text = (TextView)findViewById(R.id.DisInfo); sendRequest.setOnClickListener(this); } public void onClick(View v){ if(v.getId()==R.id.ConnectInternet){ sendRequestWithHttp(); } } private void sendRequestWithHttp(){ //開啓線程發起網絡請求 new Thread(new Runnable() { @Override public void run() { HttpURLConnection connection = null; BufferedReader reader=null; try{ URL url =new URL("http://www.baidu.com"); // connection的初始化,而且調用openConnection()方法,打開鏈接 connection =(HttpURLConnection)url.openConnection(); connection.setRequestMethod("get"); connection.setConnectTimeout(8000); connection.setReadTimeout(8000); // inputstream 接受connect傳輸的數據 InputStream in = connection.getInputStream(); //經過reader讀取inputstream中的數據 reader = new BufferedReader(new InputStreamReader(in)); StringBuilder response = new StringBuilder(); String line; while((line= reader.readLine())!=null){ response.append(line); } showResponse(response.toString()); }catch(Exception e){ e.printStackTrace(); }finally { if(reader!=null){ try { reader.close(); }catch (IOException e){ e.printStackTrace(); } } if(connection!=null){ connection.disconnect(); } } } }).start(); } private void showResponse(final String response){ //android 不容許子線程 修改UI操做,必須切換到主線程才能對 UI進行修改 runOnUiThread(new Runnable() { @Override public void run() { text.setText(response); } }); } }
參考:
http://www.cnblogs.com/IPrograming/archive/2012/03/17/Java_Socket_3.htm