Android Socket原理和詳細(xì)使用攻略

前言

  1. Socket的使用在 Android網(wǎng)絡(luò)編程中非常重要;
  2. 今天我將帶大家全面了解 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)用層

計(jì)算機(jī)網(wǎng)絡(luò)

其中:

  1. 網(wǎng)絡(luò)層:負(fù)責(zé)根據(jù)IP找到目的地址的主機(jī)
  2. 運(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架構(gòu)

?
可以看出,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三次握手 = 一條TCP連接建立完成 = 可以開始發(fā)送數(shù)據(jù)

  1. 三次握手期間任何一次未收到對面回復(fù)都要重發(fā)渴频。
  2. 最后一個確認(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四次握手

為什么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)

    1. 即通過Socket,我們才能在Andorid平臺上通過 TCP/IP協(xié)議進(jìn)行開發(fā)
    2. 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) 方式瞬雹。

    1. 即建立網(wǎng)絡(luò)連接后,當(dāng) 客戶端 向 服務(wù)器 發(fā)送請求后刽虹,服務(wù)器端才能向客戶端返回?cái)?shù)據(jù)挖炬。
    2. 可理解為:是客戶端有需要才進(jìn)行通信
  • Socket:采用 服務(wù)器主動發(fā)送數(shù)據(jù) 的方式

    1. 即建立網(wǎng)絡(luò)連接后,服務(wù)器可主動發(fā)送消息給客戶端状婶,而不需要由客戶端向服務(wù)器發(fā)送請求
    2. 可理解為:是服務(wù)器端有需要才進(jìn)行通信

5. 使用步驟

  1. Socket可基于TCP或者UDP協(xié)議,但TCP更加常用
  2. 所以下面的使用步驟 & 實(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框架
  1. 服務(wù)器代碼請?jiān)?code>eclipse平臺運(yùn)行
  2. 按照我的步驟一步步實(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)擊打開鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末萄喳,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蹋半,更是在濱河造成了極大的恐慌他巨,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件减江,死亡現(xiàn)場離奇詭異染突,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)您市,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門觉痛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人茵休,你說我怎么就攤上這事薪棒。” “怎么了榕莺?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵俐芯,是天一觀的道長。 經(jīng)常有香客問我钉鸯,道長吧史,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任唠雕,我火速辦了婚禮贸营,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘岩睁。我一直安慰自己钞脂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布捕儒。 她就那樣靜靜地躺著冰啃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪刘莹。 梳的紋絲不亂的頭發(fā)上阎毅,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機(jī)與錄音点弯,去河邊找鬼扇调。 笑死,一個胖子當(dāng)著我的面吹牛抢肛,可吹牛的內(nèi)容都是我干的肃拜。 我是一名探鬼主播痴腌,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼燃领!你這毒婦竟也來了士聪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤猛蔽,失蹤者是張志新(化名)和其女友劉穎剥悟,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體曼库,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡区岗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了毁枯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片慈缔。...
    茶點(diǎn)故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖种玛,靈堂內(nèi)的尸體忽然破棺而出藐鹤,到底是詐尸還是另有隱情,我是刑警寧澤赂韵,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布娱节,位于F島的核電站,受9級特大地震影響祭示,放射性物質(zhì)發(fā)生泄漏肄满。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一质涛、第九天 我趴在偏房一處隱蔽的房頂上張望稠歉。 院中可真熱鬧,春花似錦汇陆、人聲如沸轧抗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至纠炮,卻和暖如春月趟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背恢口。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工孝宗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人耕肩。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓因妇,卻偏偏與公主長得像问潭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子婚被,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 個人認(rèn)為狡忙,Goodboy1881先生的TCP /IP 協(xié)議詳解學(xué)習(xí)博客系列博客是一部非常精彩的學(xué)習(xí)筆記,這雖然只是...
    貳零壹柒_fc10閱讀 5,060評論 0 8
  • 運(yùn)輸層協(xié)議概述 從通信和信息處理的角度看址芯,運(yùn)輸層向它上面的應(yīng)用層提供通信服務(wù)灾茁,它屬于面向通信部分的最高層,同時也是...
    srtianxia閱讀 2,410評論 0 2
  • 1谷炸、TCP狀態(tài)linux查看tcp的狀態(tài)命令:1)北专、netstat -nat 查看TCP各個狀態(tài)的數(shù)量2)、lso...
    北辰青閱讀 9,437評論 0 11
  • TCP連接: TCP(Transmission Control Protocol,傳輸控制協(xié)議)是基于連接的...
    python_菜鳥閱讀 1,406評論 0 2
  • 一: 網(wǎng)絡(luò)各個協(xié)議:TCP/IP描孟、SOCKET驶睦、HTTP 網(wǎng)絡(luò)七層由下往上分別為物理層、數(shù)據(jù)鏈路層画拾、網(wǎng)絡(luò)層啥繁、傳輸層...
    iYeso閱讀 1,438評論 0 13