前言
- Socket的使用在 Android網(wǎng)絡(luò)編程中非常重要;
- 今天我將帶大家全面了解 Socket 及 其使用方法。
目錄
?1.網(wǎng)絡(luò)基礎(chǔ)
1.1 計(jì)算機(jī)網(wǎng)絡(luò)分層
計(jì)算機(jī)網(wǎng)絡(luò)分為五層:物理層拳亿、數(shù)據(jù)鏈路層瓦阐、網(wǎng)絡(luò)層、運(yùn)輸層鲤拿、應(yīng)用層
其中:
- 網(wǎng)絡(luò)層:負(fù)責(zé)根據(jù)IP找到目的地址的主機(jī)
- 運(yùn)輸層:通過端口把數(shù)據(jù)傳到目的主機(jī)的目的進(jìn)程,來實(shí)現(xiàn)進(jìn)程與進(jìn)程之間的通信
1.2 端口號(PORT)
端口號規(guī)定為16位歼疮,即允許一個IP主機(jī)有2的16次方65535個不同的端口杂抽。其中:
-
0~1023:分配給系統(tǒng)的端口號
我們不可以亂用
-
1024~49151:登記端口號,主要是讓第三方應(yīng)用使用
但是必須在IANA(互聯(lián)網(wǎng)數(shù)字分配機(jī)構(gòu))按照規(guī)定手續(xù)登記韩脏,
49152~65535:短暫端口號缩麸,是留給客戶進(jìn)程選擇暫時使用,一個進(jìn)程使用完就可以供其他進(jìn)程使用骤素。
在Socket使用時匙睹,可以用1024~65535的端口號
1.3 C/S結(jié)構(gòu)
定義:即客戶端/服務(wù)器結(jié)構(gòu),是軟件系統(tǒng)體系結(jié)構(gòu)
-
作用:充分利用兩端硬件環(huán)境的優(yōu)勢济竹,將任務(wù)合理分配到Client端和Server端來實(shí)現(xiàn),降低了系統(tǒng)的通訊開銷霎槐。
Socket正是使用這種結(jié)構(gòu)建立連接的送浊,一個套接字接客戶端,一個套接字接服務(wù)器丘跌。
?
可以看出,Socket的使用可以基于TCP或者UDP協(xié)議。
1.4 TCP協(xié)議
-
定義:Transmission Control Protocol称簿,即傳輸控制協(xié)議丰歌,是一種傳輸層通信協(xié)議
基于TCP的應(yīng)用層協(xié)議有FTP、Telnet报辱、SMTP与殃、HTTP、POP3與DNS碍现。
-
特點(diǎn):面向連接幅疼、面向字節(jié)流、全雙工通信昼接、可靠
面向連接:指的是要使用TCP傳輸數(shù)據(jù)爽篷,必須先建立TCP連接,傳輸完成后釋放連接慢睡,就像打電話一樣必須先撥號建立一條連接逐工,打完后掛機(jī)釋放連接。
全雙工通信:即一旦建立了TCP連接漂辐,通信雙方可以在任何時候都能發(fā)送數(shù)據(jù)泪喊。
可靠的:指的是通過TCP連接傳送的數(shù)據(jù),無差錯者吁,不丟失窘俺,不重復(fù),并且按序到達(dá)。
面向字節(jié)流:流瘤泪,指的是流入到進(jìn)程或從進(jìn)程流出的字符序列灶泵。簡單來說,雖然有時候要傳輸?shù)臄?shù)據(jù)流太大对途,TCP報(bào)文長度有限制赦邻,不能一次傳輸完,要把它分為好幾個數(shù)據(jù)塊实檀,但是由于可靠性保證惶洲,接收方可以按順序接收數(shù)據(jù)塊然后重新組成分塊之前的數(shù)據(jù)流,所以TCP看起來就像直接互相傳輸字節(jié)流一樣膳犹,面向字節(jié)流恬吕。
-
TCP建立連接
必須進(jìn)行三次握手:若A要與B進(jìn)行連接,則必須- 第一次握手:建立連接须床☆砹希客戶端發(fā)送連接請求報(bào)文段,將SYN位置為1豺旬,Sequence Number為x钠惩;然后,客戶端進(jìn)入SYN_SEND狀態(tài)族阅,等待服務(wù)器的確認(rèn)篓跛。即A發(fā)送信息給B
- 第二次握手:服務(wù)器收到客戶端的SYN報(bào)文段,需要對這個SYN報(bào)文段進(jìn)行確認(rèn)坦刀。即B收到連接信息后向A返回確認(rèn)信息
- 第三次握手:客戶端收到服務(wù)器的(SYN+ACK)報(bào)文段愧沟,并向服務(wù)器發(fā)送ACK報(bào)文段。即A收到確認(rèn)信息后再次向B返回確認(rèn)連接信息
此時求泰,A告訴自己上層連接建立央渣;B收到連接信息后告訴上層連接建立。
這樣就完成TCP三次握手 = 一條TCP連接建立完成 = 可以開始發(fā)送數(shù)據(jù)
- 三次握手期間任何一次未收到對面回復(fù)都要重發(fā)渴频。
- 最后一個確認(rèn)報(bào)文段發(fā)送完畢以后芽丹,客戶端和服務(wù)器端都進(jìn)入ESTABLISHED狀態(tài)。
為什么TCP建立連接需要三次握手卜朗?
答:防止服務(wù)器端因?yàn)榻邮樟?strong>早已失效的連接請求報(bào)文從而一直等待客戶端請求拔第,從而浪費(fèi)資源
- “已失效的連接請求報(bào)文段”的產(chǎn)生在這樣一種情況下:Client發(fā)出的第一個連接請求報(bào)文段并沒有丟失,而是在某個網(wǎng)絡(luò)結(jié)點(diǎn)長時間的滯留了场钉,以致延誤到連接釋放以后的某個時間才到達(dá)server蚊俺。
- 這是一個早已失效的報(bào)文段。但Server收到此失效的連接請求報(bào)文段后逛万,就誤認(rèn)為是Client再次發(fā)出的一個新的連接請求泳猬。
- 于是就向Client發(fā)出確認(rèn)報(bào)文段,同意建立連接。
- 假設(shè)不采用“三次握手”:只要Server發(fā)出確認(rèn)得封,新的連接就建立了埋心。
- 由于現(xiàn)在Client并沒有發(fā)出建立連接的請求,因此不會向Server發(fā)送數(shù)據(jù)忙上。
- 但Server卻以為新的運(yùn)輸連接已經(jīng)建立拷呆,并一直等待Client發(fā)來數(shù)據(jù)。>- 這樣疫粥,Server的資源就白白浪費(fèi)掉了茬斧。
采用“三次握手”的辦法可以防止上述現(xiàn)象發(fā)生:
Client不會向Server的確認(rèn)發(fā)出確認(rèn)
Server由于收不到確認(rèn),就知道Client并沒有要求建立連接
所以Server不會等待Client發(fā)送數(shù)據(jù)梗逮,資源就沒有被浪費(fèi)
-
TCP釋放連接
TCP釋放連接需要四次揮手過程项秉,現(xiàn)在假設(shè)A主動釋放連接:(數(shù)據(jù)傳輸結(jié)束后,通信的雙方都可釋放連接)第一次揮手:A發(fā)送釋放信息到B慷彤;(發(fā)出去之后伙狐,A->B發(fā)送數(shù)據(jù)這條路徑就斷了)
第二次揮手:B收到A的釋放信息之后,回復(fù)確認(rèn)釋放的信息:我同意你的釋放連接請求
第三次揮手:B發(fā)送“請求釋放連接“信息給A
-
第四次揮手:A收到B發(fā)送的信息后向B發(fā)送確認(rèn)釋放信息:我同意你的釋放連接請求
B收到確認(rèn)信息后就會正式關(guān)閉連接瞬欧;
A等待2MSL后依然沒有收到回復(fù),則證明B端已正常關(guān)閉罢防,于是A關(guān)閉連接
為什么TCP釋放連接需要四次揮手艘虎?
為了保證雙方都能通知對方“需要釋放連接”,即在釋放連接后都無法接收或發(fā)送消息給對方
需要明確的是:TCP是全雙工模式咒吐,這意味著是雙向都可以發(fā)送野建、接收的
釋放連接的定義是:雙方都無法接收或發(fā)送消息給對方,是雙向的
-
當(dāng)主機(jī)1發(fā)出“釋放連接請求”(FIN報(bào)文段)時恬叹,只是表示主機(jī)1已經(jīng)沒有數(shù)據(jù)要發(fā)送 / 數(shù)據(jù)已經(jīng)全部發(fā)送完畢候生;
但是,這個時候主機(jī)1還是可以接受來自主機(jī)2的數(shù)據(jù)绽昼。
當(dāng)主機(jī)2返回“確認(rèn)釋放連接”信息(ACK報(bào)文段)時唯鸭,表示它已經(jīng)知道主機(jī)1沒有數(shù)據(jù)發(fā)送了
但此時主機(jī)2還是可以發(fā)送數(shù)據(jù)給主機(jī)1當(dāng)主機(jī)2也發(fā)送了FIN報(bào)文段時,即告訴主機(jī)1我也沒有數(shù)據(jù)要發(fā)送了
此時硅确,主機(jī)1和2已經(jīng)無法進(jìn)行通信:主機(jī)1無法發(fā)送數(shù)據(jù)給主機(jī)2目溉,主機(jī)2也無法發(fā)送數(shù)據(jù)給主機(jī)1,此時菱农,TCP的連接才算釋放
1.5 UDP協(xié)議
-
定義:User Datagram Protocol缭付,即用戶數(shù)據(jù)報(bào)協(xié)議,是一種傳輸層通信協(xié)議循未。
基于UDP的應(yīng)用層協(xié)議有TFTP陷猫、SNMP與DNS。
-
特點(diǎn):無連接的、不可靠的绣檬、面向報(bào)文足陨、沒有擁塞控制
無連接的:和TCP要建立連接不同,UDP傳輸數(shù)據(jù)不需要建立連接河咽,就像寫信钠右,在信封寫上收信人名稱、地址就可以交給郵局發(fā)送了忘蟹,至于能不能送到飒房,就要看郵局的送信能力和送信過程的困難程度了。
不可靠的:因?yàn)閁DP發(fā)出去的數(shù)據(jù)包發(fā)出去就不管了媚值,不管它會不會到達(dá)狠毯,所以很可能會出現(xiàn)丟包現(xiàn)象,使傳輸?shù)臄?shù)據(jù)出錯褥芒。
面向報(bào)文:數(shù)據(jù)報(bào)文嚼松,就相當(dāng)于一個數(shù)據(jù)包,應(yīng)用層交給UDP多大的數(shù)據(jù)包锰扶,UDP就照樣發(fā)送献酗,不會像TCP那樣拆分。
沒有擁塞控制:擁塞坷牛,是指到達(dá)通信子網(wǎng)中某一部分的分組數(shù)量過多罕偎,使得該部分網(wǎng)絡(luò)來不及處理,以致引起這部分乃至整個網(wǎng)絡(luò)性能下降的現(xiàn)象京闰,嚴(yán)重時甚至?xí)?dǎo)致網(wǎng)絡(luò)通信業(yè)務(wù)陷入停頓颜及,即出現(xiàn)死鎖現(xiàn)象,就像交通堵塞一樣蹂楣。TCP建立連接后如果發(fā)送的數(shù)據(jù)因?yàn)樾诺蕾|(zhì)量的原因不能到達(dá)目的地俏站,它會不斷重發(fā),有可能導(dǎo)致越來越塞痊土,所以需要一個復(fù)雜的原理來控制擁塞肄扎。而UDP就沒有這個煩惱,發(fā)出去就不管了施戴。
應(yīng)用場景
很多的實(shí)時應(yīng)用(如IP電話反浓、實(shí)時視頻會議、某些多人同時在線游戲等)要求源主機(jī)以很定的速率發(fā)送數(shù)據(jù)赞哗,并且允許在網(wǎng)絡(luò)發(fā)生擁塞時候丟失一些數(shù)據(jù)雷则,但是要求不能有太大的延時,UDP就剛好適合這種要求肪笋。所以說月劈,只有不適合的技術(shù)度迂,沒有真正沒用的技術(shù)。
1.6 HTTP協(xié)議
詳情請看我寫的另外一篇文章你需要了解的HTTP知識都在這里了猜揪!
2. Socket定義
-
即套接字惭墓,是一個對 TCP / IP協(xié)議進(jìn)行封裝 的編程調(diào)用接口(API)
- 即通過
Socket
,我們才能在Andorid平臺上通過TCP/IP
協(xié)議進(jìn)行開發(fā) -
Socket
不是一種協(xié)議而姐,而是一個編程調(diào)用接口(API
)腊凶,屬于傳輸層(主要解決數(shù)據(jù)如何在網(wǎng)絡(luò)中傳輸)
- 即通過
成對出現(xiàn),一對套接字:
Socket ={(IP地址1:PORT端口號)拴念,(IP地址2:PORT端口號)}
- 1
3. 原理
Socket
的使用類型主要有兩種:
- 流套接字(
streamsocket
) :基于TCP
協(xié)議钧萍,采用 流的方式 提供可靠的字節(jié)流服務(wù) - 數(shù)據(jù)報(bào)套接字(
datagramsocket
):基于UDP
協(xié)議,采用 數(shù)據(jù)報(bào)文 提供數(shù)據(jù)打包發(fā)送的服務(wù)
具體原理圖如下:
4. Socket 與 Http 對比
-
Socket
屬于傳輸層政鼠,因?yàn)?TCP / IP
協(xié)議屬于傳輸層风瘦,解決的是數(shù)據(jù)如何在網(wǎng)絡(luò)中傳輸?shù)膯栴} -
HTTP
協(xié)議 屬于 應(yīng)用層,解決的是如何包裝數(shù)據(jù)
由于二者不屬于同一層面公般,所以本來是沒有可比性的万搔。但隨著發(fā)展,默認(rèn)的Http里封裝了下面幾層的使用官帘,所以才會出現(xiàn)Socket
& HTTP
協(xié)議的對比:(主要是工作方式的不同):
-
Http
:采用 請求—響應(yīng) 方式瞬雹。- 即建立網(wǎng)絡(luò)連接后,當(dāng) 客戶端 向 服務(wù)器 發(fā)送請求后刽虹,服務(wù)器端才能向客戶端返回?cái)?shù)據(jù)挖炬。
- 可理解為:是客戶端有需要才進(jìn)行通信
-
Socket
:采用 服務(wù)器主動發(fā)送數(shù)據(jù) 的方式- 即建立網(wǎng)絡(luò)連接后,服務(wù)器可主動發(fā)送消息給客戶端状婶,而不需要由客戶端向服務(wù)器發(fā)送請求
- 可理解為:是服務(wù)器端有需要才進(jìn)行通信
5. 使用步驟
- Socket可基于TCP或者UDP協(xié)議,但TCP更加常用
- 所以下面的使用步驟 & 實(shí)例的Socket將基于TCP協(xié)議
// 步驟1:創(chuàng)建客戶端 & 服務(wù)器的連接
// 創(chuàng)建Socket對象 & 指定服務(wù)端的IP及端口號
Socket socket = new Socket("192.168.1.32", 1989);
// 判斷客戶端和服務(wù)器是否連接成功
socket.isConnected());
// 步驟2:客戶端 & 服務(wù)器 通信
// 通信包括:客戶端 接收服務(wù)器的數(shù)據(jù) & 發(fā)送數(shù)據(jù) 到 服務(wù)器
<-- 操作1:接收服務(wù)器的數(shù)據(jù) -->
// 步驟1:創(chuàng)建輸入流對象InputStream
InputStream is = socket.getInputStream()
// 步驟2:創(chuàng)建輸入流讀取器對象 并傳入輸入流對象
// 該對象作用:獲取服務(wù)器返回的數(shù)據(jù)
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
// 步驟3:通過輸入流讀取器對象 接收服務(wù)器發(fā)送過來的數(shù)據(jù)
br.readLine()馅巷;
<-- 操作2:發(fā)送數(shù)據(jù) 到 服務(wù)器 -->
// 步驟1:從Socket 獲得輸出流對象OutputStream
// 該對象作用:發(fā)送數(shù)據(jù)
OutputStream outputStream = socket.getOutputStream();
// 步驟2:寫入需要發(fā)送的數(shù)據(jù)到輸出流對象中
outputStream.write(("Carson_Ho"+"\n").getBytes("utf-8"))膛虫;
// 特別注意:數(shù)據(jù)的結(jié)尾加上換行符才可讓服務(wù)器端的readline()停止阻塞
// 步驟3:發(fā)送數(shù)據(jù)到服務(wù)端
outputStream.flush();
// 步驟3:斷開客戶端 & 服務(wù)器 連接
os.close();
// 斷開 客戶端發(fā)送到服務(wù)器 的連接,即關(guān)閉輸出流對象OutputStream
br.close();
// 斷開 服務(wù)器發(fā)送到客戶端 的連接钓猬,即關(guān)閉輸入流讀取器對象BufferedReader
socket.close();
// 最終關(guān)閉整個Socket連接
6. 具體實(shí)例
- 實(shí)例
Demo
代碼包括:客戶端 & 服務(wù)器 - 本文著重講解客戶端稍刀,服務(wù)器僅采用最簡單的寫法進(jìn)行展示
6.1 客戶端 實(shí)現(xiàn)
步驟1:加入網(wǎng)絡(luò)權(quán)限
<uses-permission android:name="android.permission.INTERNET" />
步驟2:主布局界面設(shè)置
包括創(chuàng)建Socket連接、客戶端 & 服務(wù)器通信的按鈕
<Button
android:id="@+id/connect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="connect" />
<Button
android:id="@+id/disconnect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="disconnect" />
<TextView
android:id="@+id/receive_message"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/Receive"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Receive from message" />
<EditText
android:id="@+id/edit"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="send"/>
步驟3:創(chuàng)建Socket連接敞曹、客戶端 & 服務(wù)器通信
具體請看注釋 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
// 用于將從服務(wù)器獲取的消息顯示出來
private Handler mMainHandler;
// Socket變量
private Socket socket;
// 線程池
// 為了方便展示,此處直接采用線程池進(jìn)行線程管理,而沒有一個個開線程
private ExecutorService mThreadPool;
/**
* 接收服務(wù)器消息 變量
*/
// 輸入流對象
InputStream is;
// 輸入流讀取器對象
InputStreamReader isr ;
BufferedReader br ;
// 接收服務(wù)器發(fā)送過來的消息
String response;
/**
* 發(fā)送消息到服務(wù)器 變量
*/
// 輸出流對象
OutputStream outputStream;
/**
* 按鈕 變量
*/
// 連接 斷開連接 發(fā)送數(shù)據(jù)到服務(wù)器 的按鈕變量
private Button btnConnect, btnDisconnect, btnSend;
// 顯示接收服務(wù)器消息 按鈕
private TextView Receive,receive_message;
// 輸入需要發(fā)送的消息 輸入框
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();
// 實(shí)例化主線程,用于更新接收過來的消息
mMainHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
receive_message.setText(response);
break;
}
}
};
/**
* 創(chuàng)建客戶端 & 服務(wù)器的連接
*/
btnConnect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 利用線程池直接開啟一個線程 & 執(zhí)行該線程
mThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
// 創(chuàng)建Socket對象 & 指定服務(wù)端的IP 及 端口號
socket = new Socket("192.168.1.172", 8989);
// 判斷客戶端和服務(wù)器是否連接成功
System.out.println(socket.isConnected());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
});
/**
* 接收 服務(wù)器消息
*/
Receive.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 利用線程池直接開啟一個線程 & 執(zhí)行該線程
mThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
// 步驟1:創(chuàng)建輸入流對象InputStream
is = socket.getInputStream();
// 步驟2:創(chuàng)建輸入流讀取器對象 并傳入輸入流對象
// 該對象作用:獲取服務(wù)器返回的數(shù)據(jù)
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
// 步驟3:通過輸入流讀取器對象 接收服務(wù)器發(fā)送過來的數(shù)據(jù)
response = br.readLine();
// 步驟4:通知主線程,將接收的消息顯示到界面
Message msg = Message.obtain();
msg.what = 0;
mMainHandler.sendMessage(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
});
/**
* 發(fā)送消息 給 服務(wù)器
*/
btnSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 利用線程池直接開啟一個線程 & 執(zhí)行該線程
mThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
// 步驟1:從Socket 獲得輸出流對象OutputStream
// 該對象作用:發(fā)送數(shù)據(jù)
outputStream = socket.getOutputStream();
// 步驟2:寫入需要發(fā)送的數(shù)據(jù)到輸出流對象中
outputStream.write((mEdit.getText().toString()+"\n").getBytes("utf-8"));
// 特別注意:數(shù)據(jù)的結(jié)尾加上換行符才可讓服務(wù)器端的readline()停止阻塞
// 步驟3:發(fā)送數(shù)據(jù)到服務(wù)端
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
});
/**
* 斷開客戶端 & 服務(wù)器的連接
*/
btnDisconnect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
// 斷開 客戶端發(fā)送到服務(wù)器 的連接账月,即關(guān)閉輸出流對象OutputStream
outputStream.close();
// 斷開 服務(wù)器發(fā)送到客戶端 的連接,即關(guān)閉輸入流讀取器對象BufferedReader
br.close();
// 最終關(guān)閉整個Socket連接
socket.close();
// 判斷客戶端和服務(wù)器是否已經(jīng)斷開連接
System.out.println(socket.isConnected());
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
6.2 服務(wù)器 實(shí)現(xiàn)
- 因本文主要講解客戶端澳迫,所以服務(wù)器僅僅是為了配合客戶端展示局齿;
- 為了簡化服務(wù)器使用,此處采用
Mina
框架
- 服務(wù)器代碼請?jiān)?code>eclipse平臺運(yùn)行
- 按照我的步驟一步步實(shí)現(xiàn)就可以無腦運(yùn)行了
步驟1:導(dǎo)入Mina
包
請直接移步到百度網(wǎng)盤:下載鏈接(密碼: q73e)
步驟2:創(chuàng)建服務(wù)器線程
TestHandler.java
package mina;
// 導(dǎo)入包
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:創(chuàng)建服務(wù)器主代碼
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();
}
}
}
-
至此橄登,客戶端 & 服務(wù)器的代碼均實(shí)現(xiàn)完畢抓歼。
6.3 測試結(jié)果
- 點(diǎn)擊
Connect
按鈕: 連接成功
示意圖- 輸入發(fā)送的消息讥此,點(diǎn)擊
Send
按鈕發(fā)送
示意圖- 服務(wù)器接收到客戶端發(fā)送的消息
示意圖- 點(diǎn)擊
Receive From Message
按鈕,客戶端 讀取 服務(wù)器返回的消息
示意圖- 點(diǎn)擊
DisConnect
按鈕谣妻,斷開 客戶端 & 服務(wù)器的連接
客戶端示意圖服務(wù)器示意圖
6.4 源碼地址
Carson_Ho的Github地址:Socket具體實(shí)例
原文地址:點(diǎn)擊打開鏈接
- 點(diǎn)擊