java socket android_Android:这是一份很详细的Socket使用攻略
前言
Socket的使用在 Android網絡編程中非常重要
今天我將帶大家全面了解 Socket 及 其使用方法
目錄
示意圖
1.網絡基礎
閱讀本文前,請先了解 關于計算機網絡基礎,如計算機體系結構、TCP、UDP等知識
2. Socket定義
即套接字,是應用層 與 TCP/IP 協議族通信的中間軟件抽象層,表現為一個封裝了 TCP / IP協議族 的編程接口(API)
示意圖
Socket不是一種協議,而是一個編程調用接口(API),屬于傳輸層(主要解決數據如何在網絡中傳輸)
即:通過Socket,我們才能在Andorid平臺上通過 TCP/IP協議進行開發
對用戶來說,只需調用Socket去組織數據,以符合指定的協議,即可通信
成對出現,一對套接字:
Socket ={(IP地址1:PORT端口號),(IP地址2:PORT端口號)}
一個 Socket 實例 唯一代表一個主機上的一個應用程序的通信鏈路
3. 建立Socket連接過程
示意圖
4. 原理
Socket的使用類型主要有兩種:
流套接字(streamsocket) :基于 TCP協議,采用 流的方式 提供可靠的字節流服務
數據報套接字(datagramsocket):基于 UDP協議,采用 數據報文 提供數據打包發送的服務
具體原理圖如下:
原理圖
5. Socket 與 Http 對比
Socket屬于傳輸層,因為 TCP / IP協議屬于傳輸層,解決的是數據如何在網絡中傳輸的問題
HTTP協議 屬于 應用層,解決的是如何包裝數據
由于二者不屬于同一層面,所以本來是沒有可比性的。但隨著發展,默認的Http里封裝了下面幾層的使用,所以才會出現Socket & HTTP協議的對比:(主要是工作方式的不同):
Http:采用 請求—響應 方式。
即建立網絡連接后,當 客戶端 向 服務器 發送請求后,服務器端才能向客戶端返回數據。
可理解為:是客戶端有需要才進行通信
Socket:采用 服務器主動發送數據 的方式
即建立網絡連接后,服務器可主動發送消息給客戶端,而不需要由客戶端向服務器發送請求
可理解為:是服務器端有需要才進行通信
6. 使用步驟
Socket可基于TCP或者UDP協議,但TCP更加常用
所以下面的使用步驟 & 實例的Socket將基于TCP協議
// 步驟1:創建客戶端 & 服務器的連接
// 創建Socket對象 & 指定服務端的IP及端口號
Socket socket = new Socket("192.168.1.32", 1989);
// 判斷客戶端和服務器是否連接成功
socket.isConnected());
// 步驟2:客戶端 & 服務器 通信
// 通信包括:客戶端 接收服務器的數據 & 發送數據 到 服務器
// 步驟1:創建輸入流對象InputStream
InputStream is = socket.getInputStream()
// 步驟2:創建輸入流讀取器對象 并傳入輸入流對象
// 該對象作用:獲取服務器返回的數據
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
// 步驟3:通過輸入流讀取器對象 接收服務器發送過來的數據
br.readLine();
// 步驟1:從Socket 獲得輸出流對象OutputStream
// 該對象作用:發送數據
OutputStream outputStream = socket.getOutputStream();
// 步驟2:寫入需要發送的數據到輸出流對象中
outputStream.write(("Carson_Ho"+"\n").getBytes("utf-8"));
// 特別注意:數據的結尾加上換行符才可讓服務器端的readline()停止阻塞
// 步驟3:發送數據到服務端
outputStream.flush();
// 步驟3:斷開客戶端 & 服務器 連接
os.close();
// 斷開 客戶端發送到服務器 的連接,即關閉輸出流對象OutputStream
br.close();
// 斷開 服務器發送到客戶端 的連接,即關閉輸入流讀取器對象BufferedReader
socket.close();
// 最終關閉整個Socket連接
7. 具體實例
實例 Demo 代碼包括:客戶端 & 服務器
本文著重講解客戶端,服務器僅采用最簡單的寫法進行展示
7.1 客戶端 實現
步驟1:加入網絡權限
步驟2:主布局界面設置
包括創建Socket連接、客戶端 & 服務器通信的按鈕
android:id="@+id/connect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="connect" />
android:id="@+id/disconnect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="disconnect" />
android:id="@+id/receive_message"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:id="@+id/Receive"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Receive from message" />
android:id="@+id/edit"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:id="@+id/send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="send"/>
步驟3:創建Socket連接、客戶端 & 服務器通信
具體請看注釋
MainActivity.java
package scut.carson_ho.socket_carson;
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 android.widget.TextView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainActivity extends AppCompatActivity {
/**
* 主 變量
*/
// 主線程Handler
// 用于將從服務器獲取的消息顯示出來
private Handler mMainHandler;
// Socket變量
private Socket socket;
// 線程池
// 為了方便展示,此處直接采用線程池進行線程管理,而沒有一個個開線程
private ExecutorService mThreadPool;
/**
* 接收服務器消息 變量
*/
// 輸入流對象
InputStream is;
// 輸入流讀取器對象
InputStreamReader isr ;
BufferedReader br ;
// 接收服務器發送過來的消息
String response;
/**
* 發送消息到服務器 變量
*/
// 輸出流對象
OutputStream outputStream;
/**
* 按鈕 變量
*/
// 連接 斷開連接 發送數據到服務器 的按鈕變量
private Button btnConnect, btnDisconnect, btnSend;
// 顯示接收服務器消息 按鈕
private TextView Receive,receive_message;
// 輸入需要發送的消息 輸入框
private EditText mEdit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
* 初始化操作
*/
// 初始化所有按鈕
btnConnect = (Button) findViewById(R.id.connect);
btnDisconnect = (Button) findViewById(R.id.disconnect);
btnSend = (Button) findViewById(R.id.send);
mEdit = (EditText) findViewById(R.id.edit);
receive_message = (TextView) findViewById(R.id.receive_message);
Receive = (Button) findViewById(R.id.Receive);
// 初始化線程池
mThreadPool = Executors.newCachedThreadPool();
// 實例化主線程,用于更新接收過來的消息
mMainHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
receive_message.setText(response);
break;
}
}
};
/**
* 創建客戶端 & 服務器的連接
*/
btnConnect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 利用線程池直接開啟一個線程 & 執行該線程
mThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
// 創建Socket對象 & 指定服務端的IP 及 端口號
socket = new Socket("192.168.1.172", 8989);
// 判斷客戶端和服務器是否連接成功
System.out.println(socket.isConnected());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
});
/**
* 接收 服務器消息
*/
Receive.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 利用線程池直接開啟一個線程 & 執行該線程
mThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
// 步驟1:創建輸入流對象InputStream
is = socket.getInputStream();
// 步驟2:創建輸入流讀取器對象 并傳入輸入流對象
// 該對象作用:獲取服務器返回的數據
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
// 步驟3:通過輸入流讀取器對象 接收服務器發送過來的數據
response = br.readLine();
// 步驟4:通知主線程,將接收的消息顯示到界面
Message msg = Message.obtain();
msg.what = 0;
mMainHandler.sendMessage(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
});
/**
* 發送消息 給 服務器
*/
btnSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 利用線程池直接開啟一個線程 & 執行該線程
mThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
// 步驟1:從Socket 獲得輸出流對象OutputStream
// 該對象作用:發送數據
outputStream = socket.getOutputStream();
// 步驟2:寫入需要發送的數據到輸出流對象中
outputStream.write((mEdit.getText().toString()+"\n").getBytes("utf-8"));
// 特別注意:數據的結尾加上換行符才可讓服務器端的readline()停止阻塞
// 步驟3:發送數據到服務端
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
});
/**
* 斷開客戶端 & 服務器的連接
*/
btnDisconnect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
// 斷開 客戶端發送到服務器 的連接,即關閉輸出流對象OutputStream
outputStream.close();
// 斷開 服務器發送到客戶端 的連接,即關閉輸入流讀取器對象BufferedReader
br.close();
// 最終關閉整個Socket連接
socket.close();
// 判斷客戶端和服務器是否已經斷開連接
System.out.println(socket.isConnected());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
7.2 服務器 實現
因本文主要講解客戶端,所以服務器僅僅是為了配合客戶端展示;
為了簡化服務器使用,此處采用Mina框架
服務器代碼請在eclipse平臺運行
按照我的步驟一步步實現就可以無腦運行了
步驟1:導入Mina包
示意圖
步驟2:創建服務器線程
TestHandler.java
package mina;
// 導入包
public class TestHandler extends IoHandlerAdapter {
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
System.out.println("exceptionCaught: " + cause);
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
System.out.println("recieve : " + (String) message);
session.write("hello I am server");
}
@Override
public void messageSent(IoSession session, Object message) throws Exception {
}
@Override
public void sessionClosed(IoSession session) throws Exception {
System.out.println("sessionClosed");
}
@Override
public void sessionOpened(IoSession session) throws Exception {
System.out.println("sessionOpen");
}
@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
}
}
步驟3:創建服務器主代碼
TestHandler.java
package mina;
import java.io.IOException;
import java.net.InetSocketAddress;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
public class TestServer {
public static void main(String[] args) {
NioSocketAcceptor acceptor = null;
try {
acceptor = new NioSocketAcceptor();
acceptor.setHandler(new TestHandler());
acceptor.getFilterChain().addLast("mFilter", new ProtocolCodecFilter(new TextLineCodecFactory()));
acceptor.setReuseAddress(true);
acceptor.bind(new InetSocketAddress(8989));
} catch (Exception e) {
e.printStackTrace();
}
}
}
至此,客戶端 & 服務器的代碼均實現完畢。
7.3 測試結果
點擊 Connect按鈕: 連接成功
示意圖
輸入發送的消息,點擊 Send 按鈕發送
示意圖
服務器接收到客戶端發送的消息
示意圖
點擊 Receive From Message按鈕,客戶端 讀取 服務器返回的消息
示意圖
點擊 DisConnect按鈕,斷開 客戶端 & 服務器的連接
客戶端示意圖
服務器示意圖
7.4 源碼地址
8. 總結
相信大家已經非常了解關于Socket的使用
下面我將繼續對 Android 的網絡編程進行講解,感興趣的同學可以繼續關注本人運營的Wechat Public Account:
請點贊!因為你的鼓勵是我寫作的最大動力!
不定期分享關于安卓開發的干貨,追求短、平、快,但卻不缺深度。
總結
以上是生活随笔為你收集整理的java socket android_Android:这是一份很详细的Socket使用攻略的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql memcached java
- 下一篇: android显示过程,Android