1. 程式人生 > >Android程序間通訊之Socket

Android程序間通訊之Socket

Socket也稱為“套接字”,是網路通訊中的概念,它分為流式套接字和使用者資料報套接字兩種,分別對應於網路傳輸控制層中的TCP和UDP協議。

TCP協議是面向連線的協議,提供穩定的雙向通訊功能,TCP連線的建立需要經過“三次握手”才能完成,為了提供穩定的資料傳輸功能,其本身提供了超時重傳機制,因此具有很高的穩定性。

而UDP是無連線的,提供不穩定的單向通訊功能,當然UDP也可以實現雙向通訊功能。

在效能上,UDP具有更好的效率,其缺點是不保證資料一定能夠正確傳輸,尤其是在網路擁塞的情況下。

TCP協議中的三次握手和四次揮手:

建立TCP需要三次握手才能完成連線,而斷開連線需要四次揮手。

連線過程:

1.客戶端傳送連線請求報文

2.服務端接收到連線請求後回覆ACK報文,併為這次連線分配資源

3.客戶端接收到ACK報文後也向服務端傳送ACK報文,並分配資源

經過上面三次握手,TCP連線就建立成功了。

斷開過程:斷開連線請求可以是客戶端發起,也可以是服務端發起,這裡以客戶端發起為例

1.客戶方傳送FIN報文

2.服務端接收到FIN報文後,傳送ACK給客戶端,客戶端接收到訊息後進入FIN_WAIT狀態等待服務端的FIN報文

3.服務端確定資料已經發送完成則向客戶端傳送FIN報文

4.客戶端收到FIN報文後傳送ACK報文給服務端,並進入TIME_WAIT狀態,如果服務端沒有收到ACK報文則可以重傳。服務端收到ACK後就知道可以斷開連線了。客戶端等待2MSL(最大報文段生存時間)後依然沒有收到回覆,則證明服務端已正常關閉,客戶端也可以關閉連線了。

經過上面四次揮手,連線就斷開了。

為什麼連線的時候是三次握手,關閉的時候卻是四次握手?

答:因為當服務端收到客戶端的SYN連線請求報文後,可以直接傳送SYN+ ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連線時,當服務端收到FIN報文時,很可能並不會立即關閉Socket,所以只能先回復一個ACK報文,告訴客戶端收到了客戶端傳送的FIN報文。只有等服務端所有的報文都發送完了,服務端才能傳送FIN報文,因此不能一起傳送。故需要四次握手。

為什麼TIME_WAIT狀態需要經過2MSL才能返回CLOSE狀態?

答:網路是不可靠的,有可能造成最後一個ACK丟失,所有TIME_WAIT狀態就是用來重發可能丟失的ACK報文的。

例子:聊天工具,服務端每收到一個連線請求後就建立一個Socket連線,這樣服務端就可以和不同的客戶端通訊了。服務端每收到一條訊息就自動回覆一條訊息。

服務端程式碼:

public class SocketTcpService extends Service {

    private boolean mIsServerRunning;
    private String[] mDefaultMsgs = new String[] { "hehe", "haha", "heihei" };

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mIsServerRunning = true;
        new Thread(){
            public void run() {
                ServerSocket serverSocket = null;
                try {
                    // 監聽本地8688介面
                    serverSocket = new ServerSocket(8688);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                while(mIsServerRunning) {
                    try {
                        // 接收客戶端請求
                        final Socket socket = serverSocket.accept();
                        System.out.println("accept");
                        new Thread() {
                            public void run() {
                                try {
                                    responseClient(socket);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            };
                        }.start();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            };
        }.start();
    }

    private void responseClient(Socket socket) throws IOException {
        // 接收客戶端訊息
        BufferedReader in = new BufferedReader(new InputStreamReader(
                socket.getInputStream()));
        // 向客戶端傳送訊息
        PrintWriter out = new PrintWriter(new BufferedWriter(
                new OutputStreamWriter(socket.getOutputStream())), true);
        System.out.println("歡迎來到聊天室");
        while (mIsServerRunning) {
            // 讀取客戶端發來的訊息
            String str = in.readLine();
            // 判斷客戶端斷開連線的方法很多,這裡用輸入流是否為null判斷
            if (str == null) {
                break;
            }
            int i = new Random().nextInt(mDefaultMsgs.length);
            String msg = mDefaultMsgs[i];
            // 服務端傳送訊息
            out.println("服務端:" + msg);
        }
        System.out.println("客戶端退出");
        out.close();
        in.close();
        socket.close();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mIsServerRunning = false;
    }

}


客戶端程式碼:客戶端和服務端是在同一個應用中的不同程序

public class MainActivity extends Activity {

    private Socket mSocket;
    private Button mButton;
    private TextView mText;
    private PrintWriter mWrite;
    private String[] mDefaultMsgs = new String[] { "xixi", "houhou", "aa" };
    private Handler mHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case 0:
                mButton.setEnabled(true);
                break;
            case 1:
                mText.setText((String)msg.obj);
                break;
            }
            super.handleMessage(msg);
        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mButton = (Button) findViewById(R.id.btn);
        mText = (TextView) findViewById(R.id.text);
        mButton.setOnClickListener(mClickListener);
        Intent service = new Intent(this, SocketTcpService.class);
        startService(service);
        startConnect();
    }

    private OnClickListener mClickListener = new OnClickListener() {
        
        @Override
        public void onClick(View v) {
            int i = new Random().nextInt(mDefaultMsgs.length);
            String msg = "客戶端:" + mDefaultMsgs[i];
            if (!TextUtils.isEmpty(msg) && mWrite != null) {
                // 客戶端發訊息
                mWrite.println(msg);
                mText.setText(mText.getText() + msg + "\n");
            }
        }
    };

    private void startConnect() {
        new Thread() {
            public void run() {
                while (mSocket == null) {
                    try {
                        mSocket = new Socket("localhost", 8688);
                        mWrite = new PrintWriter(
                                new BufferedWriter(new OutputStreamWriter(
                                        mSocket.getOutputStream())), true);
                        mHandler.sendEmptyMessage(0);
                        System.out.println("連線成功");
                    } catch (Exception e) {
                        SystemClock.sleep(1000);
                        System.out.println("連線失敗,重新連線");
                    }
                }
                try {
                    BufferedReader reader = new BufferedReader(
                            new InputStreamReader(mSocket.getInputStream()));
                    while (!MainActivity.this.isFinishing()) {
                        // 讀取服務端傳送來的訊息
                        String msg = reader.readLine();
                        msg = mText.getText() + msg + "\n";
                        if (msg != null) {
                            mHandler.obtainMessage(1,msg).sendToTarget();
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            };
        }.start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mSocket != null) {
            try {
                mSocket.shutdownInput();
                mSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

相關推薦

Android程序通訊Socket

Socket也稱為“套接字”,是網路通訊中的概念,它分為流式套接字和使用者資料報套接字兩種,分別對應於網路傳輸控制層中的TCP和UDP協議。 TCP協議是面向連線的協議,提供穩定的雙向通訊功能,TCP連線的建立需要經過“三次握手”才能完成,為了提供穩定的資料傳輸功能,其本身

Android-Android程序通訊messenger

轉自‘https://www.cnblogs.com/makaruila/p/4869912.html 平時一說程序間通訊,大家都會想到AIDL,其實messenger和AIDL作用一樣,都可以進行程序間通訊。它是基於訊息的程序間通訊,就像子執行緒和UI執行緒傳送訊息那樣,是不是很簡單,還不用去寫AIDL檔

[心得]Android程序通訊Broadcast Receiver

Android框架層提供的一些通訊機制Broadcast、Intent、Content Provider,主要用於應用程式開發時提供跨程序或應用程式內部的通訊。 android中,通過廣播(broadcast)可以通知廣播接受者某個事件發生了。比如一些系統時間:電源不足,

Android程序通訊(IPC)Socket

Socket也被稱為“套接字”程式設計,它分為流式套接字和使用者資料套接字兩種,分別對應於網路傳輸控制中層中TCP和UDP協議。TCP協議是面向連線的協議,提供穩定的雙向通訊功能,TCP連線的建立需要經過”三次握手”才能實現,為了實現穩定的資料傳輸功能,其本身提

Android native程序通訊例項-socket本地通訊——服務端程序異常退出解決辦法

 導讀:   好難受啊,為什麼服務端說掛就掛,明明只是客戶端關閉而已,服務端怎麼能掛呢? 想想,如果手機上使用一個聊天程式的時候,手機端關閉了聊天程式,那麼遠端伺服器程式總不能說掛就掛吧!所以一定要查明真相。   1. 跟蹤程式碼查詢到程序退出的源頭   之前服務端原始碼:htt

Android程序通訊 - Socket使用(TCP、UDP)

在使用Socket實現程序間通訊前,先對網路協議相關知識進行簡單回顧。 網路分層 一般情況會將網路分為5層: 應用層     常見協議:HTTP、FTP、POP3等 傳輸層     

Android開發——程序通訊Bundle和檔案

0.  前言不論是Android還是其他作業系統,都會有自己的IPC機制,所謂IPC(Inter-Process Communication)即程序間通訊。首先執行緒和程序是很不同的概念,執行緒是CPU

Android如何進行程序通訊——Binder

一. 為什麼會有Binder 首先我們來看一句話:Binder是Android中使用最廣泛的IPC(程序間呼叫)機制。所以說白了,Binder的存在是為了Android系統中的跨程序函式(包括服務等)呼叫。這是作業系統的基本功能之一。在Android系統中的具體表現形式

android程序通訊--Binder

深入理解之程序間通訊–Binder 同一個程式中的兩個方法能夠直接呼叫的根本原因是處於相同的記憶體空間中。兩個不同的應用程式因為不在同一個程序中,他們是沒有辦法直接通過記憶體地址來訪問到對方的函式和變數的。同一個程序中物件的傳遞是傳遞的記憶體地址,這個地址並不是真正的實體地址,而是邏

Linux:程序通訊管道通訊詳解

        在學習程序的時候,我們瞭解到了程序的獨立性:程序之間是相互獨立的,每個程序有自己的虛擬地址空間,並且虛擬地址空間通過頁表的對映,對映到屬於自己的實體記憶體上。並且各個程序之間互相不影響,執行自己的程式碼。    

linux 程序通訊FIFO

1.概述 FIFO與管道幾乎類似,所以FIFO也是一個位元組流,從FIFO讀取的順序也是與被寫入FIFO的順序一致,容量是也有限的,也是可以確保寫入不超過PIPE_BUF位元組的操作是原子的,FIFO的本質也是一個管道,但傳遞方向是可以雙向的,它們兩者之間的最大差別在於FIFO在檔案系統中擁有一個名稱,並且

程序通訊Linux C管道程式設計

管道簡述 管道(pipe)是Unix/Linux中最常見的程序間通訊方式之一,它在兩個程序之間實現一個數據流通的通道,資料以一種資料流的方式在程序間流動。在系統中,管道相當於檔案系統上的一個檔案,用於快取所要傳輸的資料。在某些特性上又不同於檔案,例如當資料讀出後,管道中就沒有資料了,但檔案沒

Android程序通訊——ContentProvider的使用

前言 ContentProvider作為四大元件之一,一直以來存在感都很低,但其實它的功能還是很強大的,尤其是在實現程序間通訊的時候。和AIDL一樣,ContentProvider的底層實現也是Binder,但是由於系統已經為我們做了封裝,所以它的使用過程要簡單的多。 一、什麼是Conten

Android程序通訊 - 幾種方式的對比總結

什麼是RPC RPC(Remote Procedure Call)即遠端過程呼叫,它是一種通過網路從遠端計算機程式上請求服務,在不需要了解底層網路技術的協議下,即可獲取計算機程序中的資料。RPC使得開發包括網路分散式多程式在內的應用程式更加容易。 RPC在OSI網路通訊7層模型中

Android程序通訊 - ContentProvider內容提供者

簡介 ContentProvider主要用於在不同的應用程式間實現資料共享的功能,允許一個程式訪問另外一個程式中的資料,還能保證資料訪問的安全性。 是Android跨程序實現資料共享的標準方式。 ContentProvider相當於程序間的搬運工,

程序通訊管道--pipe和fifo使用

匿名管道pipe 函式原型: #include <unistd.h> int pipe(int fildes[2]); 引數說明 fildes是我們傳入的陣列,也是一個傳出引數。fildes[0]是讀端,fildes[1]是寫端。 返回值 成功呼叫返回0。 失敗呼叫返回-1且

C++ MFC程序通訊剪貼簿【詳解】

Windows剪貼簿是一種比較簡單的程序間通訊機制,同時它的開銷相對較小。它的實現原理很簡單,其實就是由由作業系統維護的一塊記憶體區域, 這塊記憶體區域不屬於任何單獨的程序,但是每一個程序又都可以訪問這塊記憶體區域,當一個程序將資料放到該記憶體區域中,而另一個

程序通訊共享記憶體

一、共享記憶體的定義和原理 1、共享記憶體的定義   顧名思義,共享記憶體就是允許兩個不相關的程序訪問同一個邏輯記憶體。共享記憶體是在兩個正在執行的程序之間共享和傳遞資料的一種非常有效的方式。不同程序之

Android程序通訊的幾種方式

定義多程序 Android應用中使用多程序只有一個辦法(用NDK的fork來做除外),就是在AndroidManifest.xml中宣告元件時,用android:process屬性來指定。 不知定process屬性,則預設執行在主程序中,主程序名字為包名。 andr

【Linux】Linux程序通訊訊息佇列

1、訊息佇列概念引入    訊息佇列提供了一個從一個程序向另外一個程序傳送一塊資料的方法每個資料塊都被認為是有一個型別,接收者程序接收的資料塊可以有不同的型別值訊息佇列也有管道一樣的不足,就是每個訊息的最大長度是有上限的(MSG