編輯:關於Android編程
我們說過Android中客戶端與服務端通信有兩種方式,HTTP通信和Socket通信,前面我們介紹過HTTP通信了,現在,我們來學習一下Socket通信。學習Socket之前,我們需要先學習一下TCP/IP協議和UDP協議。
我們需要學習的有兩種網絡通信參考模型,分別是TCP/IP參考模型和OSI參考模型,下面我們分別學習一下這兩種參考模型:
TCP/IP參考模型是計算機網絡的祖父ARPANET和其後繼的因特網使用的參考模型。TCP/IP參考模型將協議分為以下的四個層次:
應用層:主要為用戶提供各種服務,例如:Telnet、FTP、DNS等,數據傳輸單位是:數據段
傳輸層:應用層實體提供端到端的通信功能,保證了數據包的順序傳送及數據的完整性。該層定義了兩個主要的協議:傳輸控制協議(TCP)和用戶數據報協議(UDP).數據傳輸單位是:數據包
網際互聯層:主要解決主機到主機的通信問題。它所包含的協議設計數據包在整個網絡上的邏輯傳輸。注重重新賦予主機一個IP地址來完成對主機的尋址,它還負責數據包在多種網絡中的路由。該層有三個主要協議:網際協議(IP)、互聯網組管理協議(IGMP)和互聯網控制報文協議(ICMP)。數據傳輸單位是:幀
網絡接入層:負責監視數據在主機和網絡之間的交換。事實上,TCP/IP本身並未定義該層的協議,而由參與互連的各網絡使用自己的物理層和數據鏈路層協議,然後與TCP/IP的網絡接入層進行連接。數據傳輸單位是:比特
OSI(Open System Interconnect)開放式系統互聯,是ISO(國際標准化組織)組織在1985年研究的網絡互聯模型。該體系結構標准定義了網絡互連的七層框架(物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層),即ISO開放系統互連參考模型。
第7層應用層:OSI中的最高層。為特定類型的網絡應用提供了訪問OSI環境的手段。應用層確定進程之間通信的性質,以滿足用戶的需要。應用層不僅要提供應用進程所需要的信息交換和遠程操作,而且還要作為應用進程的用戶代理,來完成一些為進行信息交換所必需的功能。它包括:文件傳送訪問和管理FTAM、虛擬終端VT、事務處理TP、遠程數據庫訪問RDA、制造報文規范MMS、目錄服務DS等協議;應用層能與應用程序界面溝通,以達到展示給用戶的目的。 在此常見的協議有:HTTP,HTTPS,FTP,TELNET,SSH,SMTP,POP3等。
第6層表示層:主要用於處理兩個通信系統中交換信息的表示方式。為上層用戶解決用戶信息的語法問題。它包括數據格式交換、數據加密與解密、數據壓縮與終端類型的轉換。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPrXaNbLju+G7sLLjo7rU2sG9uPa92rXj1q685L2owaK2y8GsvdOho86qtsvPtc2ztcTTptPDs8zQ8tauvOTM4bmpwcu21Luwv9jWxrv61saho7TLt/7O8bD8wKi9qMGiway908rH0tTIq8uruaS7ucrH0tSw68uruaS1xLe9yr29+NDQyejWw6OsvqG53L/J0tTU2rLjNNbQtKbA7curuaS3vcq9IKO7u+G7sLLjudzA7bXHyOu6zdeiz/q5/bPMoaPL/L7fzOW53MDtwb249tPDu6e6zb34s8zWrrzktcS21LuwoaPI57n71NrEs9K7yrG/zNa71MrQ7dK7uPbTw7un1rTQ0NK7z+7M2LaotcSy2df3o6y74buwsuPQrdLpvs274bncwO3V4tCpstnX96OsyOfX6Na5wb249tPDu6fNrMqxuPzQwsr9vt2/4tbQtcTNrNK71+nK/b7doaM8L3A+DQo8cD612jSy47SryuSy46O6Jm1kYXNoO7OjuebK/b7dtd3LzaOtw+bP8sGsvdO78s7eway906Gjzqq74buwsuPTw7unzOG5qdK7uPa2y7W9tsu1xL/Jv7+hos24w/e6zdPFu6+1xMr9vt20q8rkt/7O8bv61saho7D8wKjIq8uruaS78rDry6u5pKGiwfe/2NbGus207c7zu9a4tLf+zvGju7SryuSy47DRz/vPorfWs8nI9LjJuPa31tfpo6yyotTavdPK1bbLttTL/MPHvfjQ0NbY1+mho7K7zay1xLfW1+m/ydLUzai5/bK7zay1xMGsvdO0q8vNtb3W97v6oaPV4tH5vMjE3LvxtcO9z7jftcS0+L/to6zT1rK707DP7Lvhu7Cy46Gj1Nq9qMGiway908qxtKvK5LLjv8nS1Mfrx/O3/s7x1srBv6OsuMO3/s7x1srBv9a4tqi/yb3Tyty1xM7zwuvCyqGi0dOz2cG/oaKwssir0NS1yLLOyv2jrLu5v8nS1Mq1z9a7+dPatsu1vbbLtcTB98G/v9jWxrmmxNyhozwvcD4NCjxwPrXaM7LjzfjC57Ljo7qxvrLjzai5/dGw1rfAtL2owaLBvbj2vdq149auvOS1xMGsvdOjrM6q1LS2y7XE1MvK5LLjy83AtLXEt9bX6aOs0aHU8brPysq1xMK308m6zb27u7u92rXjo6zV/ci3zt7O87XYsLTV1bXY1re0q8vNuPjEv7XEtsu1xNTLyuSy46Gjy/yw/MCozai5/bulwazN+MLnwLTCt9PJus3W0LzMyv2+3SCju7P9wcvRodTxwrfTydauzeKjrM34wuey47u5uLrU8L2owaK6zc6su6TBrL3To6y/2NbGzfjC58nPtcTTtcj70tS8sNTasdjSqrXEyrG68sn6s8m8xrfR0MXPoqGjPC9wPg0KPHA+tdoysuPK/b7dwbTCt7Ljo7rU2rTLsuO9q8r9vt231taho6yyorSmwO3B97/Y1saho8bBsc7O78DtsuOjrM6qzfjC57LjzOG5qdK7uPbK/b7dwbTCt7XEway906Os1NrSu8z109C/ycTcs/ay7rTttcTO78Dtway908nPo6y9+NDQvLi69c7esu607bXEyv2+3bSryuSjqLLutO2/2NbGo6mho7G+suPWuLaozdjGy73hubmyoszhuanTsrz+0bDWt6Gjs6PTw8nosbjT0M34v6ihos34x8Whor27u7u7+qO7PC9wPg0KPHA+tdoxsuPO78DtsuOjurSm09pPU0myzr+8xKPQzbXE1+6117LjoaPO78DtsuO1xNb30qq5psTcysfA+9PDzu/A7bSryuS96dbKzqrK/b7dwbTCt7LjzOG5qc7vwO3BrL3To6zS1LHjzbjD97XEtKvLzbHIzNjB96Gjs6PTw8nosbjT0KOouPfW1s7vwO3J6LG4o6m8r8/fxvehotbQvMzG96GitffWxr3itffG96GizfjP36Giy6u9ys/foaLNrNbhtefAwqGjPC9wPg0KPHA+yv2+3beiy83KsaOstNO12sbfsuO0q7W9tdrSu7Ljo6y908rVyv2+3dTyz+C3tKGjyc/I/bLj19yzxtOm08Oy46Os08PAtL/Y1sbI7bz+t73D5qGjz8LLxLLj19yzxsr9vt3B97Ljo6zTw8C0udzA7dOyvP6ho7P9wcvO78DtsuPWrs3ixuTL+7LjtrzKx9PDyO28/sq1z9a1xKGjyv2+3dTat6LWwcr9vt3B97LjtcTKsbryvauxu7Lwt9aho9TatKvK5LLjtcTK/b7dvdC2zqOszfjC57LjvdCw/KOsyv2+3cG0wrey473Q1qGjrM7vwO2y473QscjM2MH3o6zV4tH5tcS90LeovdBQRFWjqNCt0unK/b7dtaXUqqOpPC9wPg0KPGg0IGlkPQ=="兩種參考模型比較">兩種參考模型比較
兩種參考模型比較圖
共同點
(1)OSI參考模型和TCP/IP參考模型都采用了層次結構的概念。
(2)都能夠提供面向連接和無連接兩種通信服務機制。
不同點
(1)OSI采用的七層模型,而TCP/IP是四層結構。
(2)TCP/IP參考模型的網絡接口層實際上並沒有真正的定義,只是一些概念性的描述。而OSI參考模型不僅分了兩層,而且每一層的功能都很詳盡,甚至在數據鏈路層又分出一個介質訪問子層,專門解決局域網的共享介質問題。
(3)OSI模型是在協議開發前設計的,具有通用性。TCP/IP是先有協議集然後建立模型,不適用於非TCP/IP網絡。
(4)OSI參考模型與TCP/IP參考模型的傳輸層功能基本相似,都是負責為用戶提供真正的端對端的通信服務,也對高層屏蔽了底層網絡的實現細節。所不同的是TCP/IP參考模型的傳輸層是建立在網絡互聯層基礎之上的,而網絡互聯層只提供無連接的網絡服務,所以面向連接的功能完全在TCP協議中實現,當然TCP/IP的傳輸層還提供無連接的服務,如UDP;相反OSI參考模型的傳輸層是建立在網絡層基礎之上的,網絡層既提供面向連接的服務,又提供無連接的服務,但傳輸層只提供面向連接的服務。
(5)OSI參考模型的抽象能力高,適合與描述各種網絡;而TCP/IP是先有了協議,才制定TCP/IP模型的。
(6)OSI參考模型的概念劃分清晰,但過於復雜;而TCP/IP參考模型在服務、接口和協議的 區別上不清楚,功能描述和實現細節混在一起。
(7)TCP/IP參考模型的網絡接口層並不是真正的一層;OSI參考模型的缺點是層次過多,劃分意義不大但增加了復雜性。
(8)OSI參考模型雖然被看好,由於沒把握好時機,技術不成熟,實現困難;相反,TCP/IP參考模型雖然有許多不盡人意的地方,但還是比較成功的。
TCP/IP(transmission Control Protocol/Internet Protocol)傳輸控制協議/互聯網絡協議,是一種網絡通信協議,它規范了網絡上的所有通信設備,尤其是一個主機與另一個主機之間的數據往來格式以及傳送方式。舉個簡單的例子就是:TCP和IP就像兩個信封,我們把要傳輸的數據分割開,放入到TCP信封裡面,TCP信封裡面會記錄有分段號;然後將TCP信封裝入IP這個大信封裡面,傳輸到網絡上。在接收端,一個TCP軟件包收集信封,抽出數據,按發送前的順序還原,並加以校驗,若發現差錯,TCP將會要求重發。
TCP/IP網絡連接需要經過三次握手,斷開連接需要三次揮手,在這裡就不在做很詳細的描述了,推薦一篇博文:TCP/IP三次握手與四次揮手 ,這裡已經很詳細的描述三次握手和四次揮手的過程,建議大家一定要閱讀並理解,面試經常會被問到。關於TCP/IP協議就簡單介紹到這裡,我們做應用開發不是網絡工程師,不需要做很深入的學習。
前面我們介紹了TCP/IP協議,接著我們介紹一下UDP協議。UDP(User Datagram Protocol)用戶數據報協議,是OSI(Open System Interconnection,開放式系統互聯) 參考模型中一種無連接的傳輸層協議,提供面向事務的簡單不可靠信息傳送服務,IETF RFC 768是UDP的正式規范。UDP在IP報文的協議號是17。
UDP協議全稱是用戶數據報協議 ,在網絡中它與TCP協議一樣用於處理數據包,是一種無連接的協議。在OSI模型中,在第四層——傳輸層,處於IP協議的上一層。UDP有不提供數據包分組、組裝和不能對數據包進行排序的缺點,也就是說,當報文發送之後,是無法得知其是否安全完整到達的。UDP用來支持那些需要在計算機之間傳輸數據的網絡應用。包括網絡視頻會議系統在內的眾多的客戶/服務器模式的網絡應用都需要使用UDP協議。UDP協議從問世至今已經被使用了很多年,雖然其最初的光彩已經被一些類似協議所掩蓋,但是即使是在今天UDP仍然不失為一項非常實用和可行的網絡傳輸層協議。
與所熟知的TCP(傳輸控制協議)協議一樣,UDP協議直接位於IP(網際協議)協議的頂層。根據OSI(開放系統互連)參考模型,UDP和TCP都屬於傳輸層協議。UDP協議的主要作用是將網絡數據流量壓縮成數據包的形式。一個典型的數據包就是一個二進制數據的傳輸單位。每一個數據包的前8個字節用來包含報頭信息,剩余字節則用來包含具體的傳輸數據。
前面介紹的都是概念性的東西,非常枯燥,下面正式開始學習基於TCP的Socket通信。
什麼是Socket?稱作”套接字”,用於描述IP地址和端口,是一個通信鏈的句柄,可以用來實現不同虛擬機或不同計算機之間的通信。在Internet上的主機一般運行了多個服務軟件,同時提供幾種服務。每種服務都打開一個Socket,並綁定到一個端口上,不同的端口對應於不同的服務。Socket通常用來實現客戶方和服務方的連接。Socket是TCP/IP協議的一個十分流行的編程界面,一個Socket由一個IP地址和一個端口號唯一確定。
Socket通信模型
Server端Listen(監聽)某個端口是否有連接請求,Client端向Server 端發出Connect(連接)請求,Server端向Client端發回Accept(接受)消息。一個連接就建立起來了。Server端和Client 端都可以通過Send,Write等方法與對方通信。
對於一個功能齊全的Socket,都要包含以下基本結構,其工作過程包含以下四個基本的步驟:
1、創建ServerSocket和Socket
2、打開連接到的Socket的輸入/輸出流
3、按照協議對Socket進行讀/寫操作
4、關閉輸入輸出流,以及Socket
下面我們通過Socket搭建一個簡單聊天室來體會一下Socket通信:
首先是服務端的編寫,服務端編寫步驟:
創建ServerSocket對象,綁定監聽的端口
通過調用accept()方法監聽客戶端的請求
建立連接之後,通過輸入流讀取客戶端的請求信息
獲取輸出流向客戶端輸出信息
關閉連接
客戶端的編寫步驟:
創建Socket對象,指明需要連接的服務器的地址和端號
連接建立後,通過輸出流向服務器發送請求信息
通過輸出流獲取服務器響應的信息
關閉連接,釋放相關資源
具體實例代碼:
服務端:
package com.example.socket; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by Devin on 2016/7/28. */ public class SocketServer { public static boolean isServerStart = true; private ServerSocket serverSocket; private static int LISTENER_PART = 10010; public ListsocketList = new ArrayList<>(); private ExecutorService executorService; public void startServer() { try { serverSocket = new ServerSocket(LISTENER_PART); executorService = Executors.newCachedThreadPool(); InetAddress address = InetAddress.getLocalHost(); System.out.println("服務器的IP地址是:" + address.getHostAddress() + ",監聽的端口號是:" + LISTENER_PART); System.out.println("--------------服務器啟動,等待連接-----------------"); Socket socket = null; while (isServerStart) { System.out.println("有用戶登錄進來了"); socket = serverSocket.accept(); socketList.add(socket); executorService.execute(new ServerThread(socket)); } serverSocket.close(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { SocketServer socketServer = new SocketServer(); socketServer.startServer(); } }
客戶端:
package com.example.socketdemo; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; import com.example.socketdemo.activity.FriendsActivity; import com.example.socketdemo.comm.Constans; import com.example.socketdemo.comm.ServerConn; import com.example.socketdemo.comm.ToastUtils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; public class MainActivity extends AppCompatActivity { private EditText et_user_id; private EditText et_user_name; private Button btn_user_login; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Bundle bundle = msg.getData(); String isSuccess = bundle.getString("isSuccess"); int userID = bundle.getInt("userID"); if (isSuccess.equals("success")) { Intent intent = new Intent(); intent.setClass(MainActivity.this, FriendsActivity.class); intent.putExtra("userID", userID); startActivity(intent); } else { ToastUtils.showToast(MainActivity.this, "登錄失敗"); return; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); et_user_id = (EditText) findViewById(R.id.et_user_id); et_user_name = (EditText) findViewById(R.id.et_user_name); btn_user_login = (Button) findViewById(R.id.btn_user_login); btn_user_login.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String userID = et_user_id.getText().toString().trim(); if (userID != null && !userID.equals("")) { login(Integer.parseInt(userID)); } else { ToastUtils.showToast(MainActivity.this, "請輸入用戶ID"); return; } } }); } private void login(int userID) { final int id = userID; new Thread(new Runnable() { @Override public void run() { try { Socket socket = new Socket(); socket.connect(new InetSocketAddress(Constans.SERVER_ADDRESS, Constans.SERVER_PORT), 5000); InetAddress address = InetAddress.getLocalHost(); PrintWriter writer = new PrintWriter(socket.getOutputStream()); writer.println(Constans.TAG_LOGIN + ":" + id + ":" + address.getHostAddress()); writer.flush(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String isSuccess = bufferedReader.readLine(); ServerConn.mSocket = socket; Message message = mHandler.obtainMessage(); Bundle bundle = new Bundle(); bundle.putString("isSuccess", isSuccess); bundle.putInt("userID", id); message.setData(bundle); mHandler.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } }).start(); } } package com.example.socketdemo.activity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import com.example.socketdemo.R; import com.example.socketdemo.comm.Constans; import com.example.socketdemo.comm.OnItemClickListener; import com.example.socketdemo.domain.UserInfo; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetSocketAddress; import java.net.Socket; import java.util.ArrayList; import java.util.List; /** * Created by Devin on 2016/8/1. */ public class FriendsActivity extends AppCompatActivity { private RecyclerView rv_friends; private FriendsAdapter mAdapter; private ListmUserInfos; private int userID; private FriendHandler mHandler; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_friends); userID = getIntent().getIntExtra("userID", 0); mHandler = new FriendHandler(); rv_friends = (RecyclerView) findViewById(R.id.rv_friends); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); rv_friends.setLayoutManager(linearLayoutManager); rv_friends.setItemAnimator(new DefaultItemAnimator()); mUserInfos = new ArrayList<>(); initData(userID); mAdapter = new FriendsAdapter(this, mUserInfos); rv_friends.setAdapter(mAdapter); mAdapter.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(View view, int position) { UserInfo userInfo = mUserInfos.get(position); Intent intent = new Intent(FriendsActivity.this, ChatActivity.class); intent.putExtra("sendID", userID); intent.putExtra("receiveID", userInfo.getUserID()); intent.putExtra("userName", userInfo.getUserName()); startActivity(intent); } }); } private void initData(final int userID) { new Thread(new Runnable() { @Override public void run() { Socket socket = new Socket(); try { socket.connect(new InetSocketAddress(Constans.SERVER_ADDRESS, Constans.SERVER_PORT), 5000); PrintWriter writer = new PrintWriter(socket.getOutputStream()); writer.println(Constans.TAG_FRIENDS + ":" + userID); writer.flush(); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String result = reader.readLine(); System.out.println("-----result---->>"+result); Bundle bundle = new Bundle(); bundle.putString("result", result); Message message = mHandler.obtainMessage(); message.setData(bundle); mHandler.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } }).start(); } private class FriendHandler extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Bundle bundle = msg.getData(); String result = bundle.getString("result"); List infos = new ArrayList<>(); try { JSONArray jsonArray = new JSONArray(result); for (int i = 0; i < jsonArray.length(); i++) { JSONObject jsonObject = jsonArray.getJSONObject(i); UserInfo userInfo = new UserInfo(); userInfo.setUserID(jsonObject.getInt("userID")); userInfo.setUserIcon(jsonObject.getInt("userIcon")); userInfo.setUserName(jsonObject.getString("userName")); userInfo.setUserSign(jsonObject.getString("userSign")); userInfo.setUserState(jsonObject.getInt("userState")); infos.add(userInfo); } } catch (JSONException e) { e.printStackTrace(); } mAdapter.addList(infos); } } }
實現效果圖
這樣可以實現簡單的Socket通信,當然只是很簡單的應用。
1、完整生命周期上圖是Android Activity的生命周期圖,其中Resumed、Paused、Stopped狀態是靜態的,這三個狀態下的Activity存在時間較
Android的一些內存知識垃圾回收(GC)垃圾回收包含兩個過程:判定階段,也就是判斷哪些對象可以被回收, 收集階段,是指具體的回收策略。判定階段主要有兩種方式引用計數,
Android的控件有很多種,其中就有一個Spinner的控件,這個控件其實就是一個下拉顯示列表。Spinner是位於 android.widget包下的,每
總結一下微信的本地圖片加載有以下幾個特點,也是提高用戶體驗的關鍵點1、縮略圖挨個加載,一個一個加載完畢,直到屏幕所有縮略圖都加載完成2、不等當前屏的所有縮略圖加載完,迅速