套接字,爲TCP/IP協議網絡通訊的網絡操做單元;html
而抽象上來講:Socket只是一個供上層調用的抽象接口,至關因而傳輸層下的數據,還沒通過應用層的封裝,或者說不須要應用層的封裝,由於直接使用socket鏈接的有兩種狀況,第一種狀況就是直接獲取傳輸層傳輸過來的輸入流,按順序合成以後就是一個完整的文件,或者是一個字符串等等不須要所謂的解析;另外一種狀況就是從socket鏈接返回的數據進行二次封裝,進行應用層解析,達到大牛的網絡訪問框架,實現應用層封裝,不過不少細節須要注意。java
聯繫:UrlConnection基於Http協議,只不過多了封裝,本質上也是創建Tcp鏈接,利用socket進行鏈接和數據傳輸,只不過每次鏈接以後都要手動關閉鏈接。所以直接使用Socket進行網絡通信得考慮線程治理、客戶狀態監控等,可是不用發送頭信息等,更省流量。android
區別web
HttpURLConnection
HttpURLConnection只是繼承UrlConnection,二者都是接口,只是在該接口的基礎上進行簡單封裝apache
從Android4.4開始HttpURLConnection的底層實現採用的是okHttp編程
HttpClientjson
HttpClient就是對java提供的方法的一些封裝,在HttpURLConnection的輸入輸出流操做,在HttpClient接口裏直接封裝成HttpPost、HttpGet、HttpResponse。很方便,另外須要注意的是post方式的狀況下,咱們須要進行字符編碼,不然會出錯。
Apache HttpClient早就不推薦httpclient,5.0以後乾脆廢棄,後續會刪除。6.0刪除了HttpClient。設計模式
OkHttp
okhttp是高性能的http庫,支持同步、異步,並且實現了spdy、http二、websocket協議,api很簡潔易用,和volley同樣實現了http協議的緩存。picasso就是利用okhttp的緩存機制實現其文件緩存,實現的很優雅,很正確,反例就是UIL(universal image loader),本身作的文件緩存,並且不遵照http緩存機制。api
OkHttp的最底層是Socket,而不是HTTP,它經過Platform的Class.forName()反射得到當前Runtime使用的socket庫跨域
volley
volley是一個簡單的異步http庫,僅此而已。缺點是不支持同步,這點會限制開發模式。自帶緩存,支持自定義請求。不適合大文件上傳和下載。
Volley在Android 2.3及以上版本,使用的是HttpURLConnection,而在Android 2.2及如下版本,使用的是HttpClient。
Volley本身的定位是輕量級網絡交互,適合大量的,小數據傳輸。
不過再怎麼封裝Volley在功能拓展性上始終沒法與OkHttp相比。Volley中止了更新,而OkHttp獲得了官方的承認,並在不斷優化。
android-async-http。
與volley同樣是異步網絡庫,但volley是封裝的httpUrlConnection,它是封裝的httpClient,而android平臺不推薦用HttpClient了,因此這個庫已經不適合android平臺了。
Retrofit
Retrofit 是一個 RESTful 的 HTTP 網絡請求框架的封裝。注意這裏並無說它是網絡請求框架,主要緣由在於網絡請求的工做並非 Retrofit 來完成的。Retrofit 2.0 開始內置 OkHttp,前者專一於接口的封裝,後者專一於網絡請求的高效,兩者分工協做,宛如古人的『你耕地來我織布』,小日子別提多幸福了。參考深刻淺出 Retrofit
retrofit與picasso同樣都是在okhttp基礎之上作的封裝,項目中能夠直接用了。Retrofit由於也是square出的,因此你們可能對它更崇拜些。Retrofit的跟Volley是一個套路,但解耦的更完全:比方說經過註解來配置請求參數,經過工廠來生成CallAdapter,Converter,你可使用不一樣的請求適配器(CallAdapter), 比方說RxJava,Java8, Guava。你可使用不一樣的反序列化工具(Converter),比方說json, protobuff, xml, moshi等等。炒雞解耦,裏面涉及到超多設計模式,我的以爲是很經典的學習案例。雖然支持Java8, Guava你可能也不須要用到。xml,protobuff等數據格式你也可能不須要解析。but,萬一遇到鬼了呢。至於性能上,我的以爲這徹底取決於請求client,也就是okhttp的性能,跟這些封裝工具沒太大關係。
對於咱們熟知的網絡訪問工具類HttpURLConnection和HttpClient,這兩個接口均可以用來開發Http訪問。
須要引入httpClient包,在本人AS環境,SDK處於23的狀況下,須要引入:
android { useLibrary 'org.apache.http.legacy'//httpClient須要包 }
須要的權限:
<uses-permission android:name="android.permission.INTERNET" />
簡單的使用:
httpURLConnection、HttpClient:
/** * 使用HTTPUrlConnection例子 * @param username * @param password * @return */ public static String login(String username,String password){ String msg = ""; try { username = URLEncoder.encode(username,"UTF-8");//這裏要注意編碼,若是參數含有漢字或是空格(尤爲是日期中的空格),不編碼會發生錯誤 password = URLEncoder.encode(password,"UTF-8"); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } //要訪問的HttpServlet String urlStr="http://127.0.0.1:8080/MyProject/getUser?"; //要傳遞的數 String params ="username="+username+"&password="+password; urlStr = urlStr+params; try{ URL url =new URL(urlStr); //得到鏈接 HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn.setConnectTimeout(6000); conn.setRequestMethod("GET");//請求方式 InputStream in = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in, HTTP.UTF_8)); String line = null; while ((line = reader.readLine()) != null) { if(msg==null){ msg=line; }else{ msg += line; } } reader.close(); in.close();//關閉數據流 conn.disconnect(); }catch(Exception e){ e.printStackTrace(); return null; } return msg; } /** * 使用HttpClient訪問,get方式,若是sdk版本爲23,須要引入org.apache.http.legacy * @return */ private static String loginHttpClientGet(){ // http地址 String httpUrl = "http://192.168.1.110:8080/httpget.jsp?par=HttpClient_android_Get"; //HttpGet鏈接對象 HttpGet httpRequest = new HttpGet(httpUrl); String strResult = ""; try { //取得HttpClient對象 HttpClient httpclient = new DefaultHttpClient(); //請求HttpClient,取得HttpResponse HttpResponse httpResponse = httpclient.execute(httpRequest); //請求成功 if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { //取得返回的字符串 strResult = EntityUtils.toString(httpResponse.getEntity()); // mTextView.setText(strResult); } else { // mTextView.setText("請求錯誤!"); } return strResult; } catch (ClientProtocolException e) { // mTextView.setText(e.getMessage().toString()); } catch (IOException e) { // mTextView.setText(e.getMessage().toString()); } catch (Exception e) { // mTextView.setText(e.getMessage().toString()); } return strResult; } /** * 使用HttpClient訪問,post方式 * @return */ private static String loginHttpClientPost(){ // http地址 String httpUrl = "http://192.168.1.110:8080/httpget.jsp"; //HttpPost鏈接對象 HttpPost httpRequest = new HttpPost(httpUrl); //使用NameValuePair來保存要傳遞的Post參數 List<NameValuePair> params = new ArrayList<NameValuePair>(); //添加要傳遞的參數 params.add(new BasicNameValuePair("par", "HttpClient_android_Post")); String strResult = ""; try { //設置字符集 HttpEntity httpentity = new UrlEncodedFormEntity(params, "gb2312"); //請求httpRequest httpRequest.setEntity(httpentity); //取得默認的HttpClient HttpClient httpclient = new DefaultHttpClient(); //取得HttpResponse HttpResponse httpResponse = httpclient.execute(httpRequest); //HttpStatus.SC_OK表示鏈接成功 if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { //取得返回的字符串 strResult = EntityUtils.toString(httpResponse.getEntity()); // mTextView.setText(strResult); } else { // mTextView.setText("請求錯誤!"); } return strResult; } catch (ClientProtocolException e) { // mTextView.setText(e.getMessage().toString()); } catch (IOException e) { // mTextView.setText(e.getMessage().toString()); } catch (Exception e) { // mTextView.setText(e.getMessage().toString()); } return strResult; }
socket:
//服務器端 public class MyServer { private static int count=0; public static void main(String[]args){ try { //實例化服務器套接字 設置端口號8888 ServerSocket server=new ServerSocket(8888); while(true){ //鏈接編號設置 count=count+1; //時間格式 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); //實例化客戶端 Socket client=server.accept(); //實例化時間 以及 id System.out.println(count+":"+sdf.format(System.currentTimeMillis())); //獲取輸出流 OutputStream out=client.getOutputStream(); //輸出字符串 String msg="Hello,Android!"; //寫字符串 out.write(msg.getBytes()); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } //客戶端 public class MyClientActivity extends Activity { /** Called when the activity is first created. */ private Button rev=null; private TextView revtext=null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); rev=(Button)findViewById(R.id.rev); revtext=(TextView)findViewById(R.id.receiver); rev.setOnClickListener(new receiverlistenr()); } class receiverlistenr implements OnClickListener{ public void onClick(View v) { // TODO Auto-generated method stub try { //實例化Socket Socket socket=new Socket("169.254.202.149",8888); //得到輸入流 InputStream in=socket.getInputStream(); //緩衝區 byte[] buffer=new byte[in.available()]; //讀取緩衝區 in.read(buffer); //轉換字符串 String msg=new String(buffer); //設置文本框的字符串 revtext.setText(msg); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
Socket鏈接---至少須要一對套接字,其中一個運行於客戶端,稱爲ClientSocket ,另外一個運行於服務器端,稱爲ServerSocket套接字之間的鏈接過程分爲三個步驟:服務器監聽,客戶端請求,鏈接確認
請求報文的通常格式:
一般來講一個HTTP請求報文由請求行、請求報頭、空行、和請求數據4個部分組成。
GET http://blog.csdn.net/itachi85 HTTP/1.1 //請求行 Host: blog.csdn.net //請求報頭 Connection: keep-alive Cache-Control: max-age=0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36 QQBrowser/9.3.6872.400 Accept-Encoding: gzip, deflate, sdch Accept-Language: zh-CN,zh;q=0.8 Cookie: bdshare_firstime=1443768140949; uuid_tt_dd=5028529250430960147_20151002;
//不能省略的空格
28b5 }ysI 1ߡFsgl n- ]{^_ { 'z! C , m# 0 !l ` 4x ly .ݪ*
...省略
響應報文的通常格式:
HTTP的響應報文由狀態行、消息報頭、空行、響應正文組成。
HTTP/1.1 200 OK //狀態行 Server: openresty //響應報頭 Date: Sun, 27 Mar 2016 08:26:54 GMT Content-Type: text/html; charset=utf-8 Transfer-Encoding: chunked Connection: keep-alive Keep-Alive: timeout=20 Vary: Accept-Encoding Cache-Control: private X-Powered-By: PHP 5.4.28 Content-Encoding: gzip //不能省略的空格 28b5 }ysI 1ߡFsgl n- ]{^_ { 'z! C , m# 0 !l ` 4x ly .ݪ* ڴzAt_Xl * 9'O ɬ ' ק 3 ^1a ...省略
若是是請求文件(下載)
要從文件已經下載的地方開始繼續下載。在之前版本的 HTTP 協議是不支持斷點的,HTTP/1.1 開始就支持了。通常斷點下載時纔用到 Range 和 Content-Range 實體頭。
Range
用於請求頭中,指定第一個字節的位置和最後一個字節的位置,通常格式:
Range:(unit=first byte pos)-[last byte pos]
Content-Range
用於響應頭,指定整個實體中的一部分的插入位置,他也指示了整個實體的長度。在服務器向客戶返回一個部分響應,它必須描述響應覆蓋的範圍和整個實體長度。通常格式:
Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity legth]
請求下載整個文件:
通常正常回應
注意:對於socket網絡訪問
服務器端:根據serversocket.accept()接收到請求socket,socket的inputstream爲請求報文(與Http請求報文一致),而且把數據寫入到socket的outputStream中(數據格式與HTTP響應報文一致)。
客戶端:根據socket,inputStream爲服務器返回的數據
據RFC2616標準(現行的HTTP/1.1)得知有如下8種方法:OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE和CONNECT。
con.setRequestMethod("");//設置請求狀態
HTTP請求方法有8種,分別是GET、POST、DELETE、PUT、HEAD、TRACE、CONNECT 、OPTIONS。其中PUT、DELETE、POST、GET分別對應着增刪改查,對於移動開發最經常使用的就是POST和GET了。
用來檢查服務器的性能。例如:AJAX進行跨域請求時的預檢,須要向另一個域名的資源發送一個HTTP OPTIONS請求頭,用以判斷實際發送的請求是否安全。
參考連接:
URLConnection/HttpURLConnection/HttpClient/socket 差異