進程間通信的方式之Scoket——實現與AI鬼才聊天

轉載請以鏈接形式標明出處:
本文出自:103style的博客

《Android開發藝術探索》 學習記錄

base on AndroidStudio 3.5.1


目錄

  • 前言
  • 實現跨進程與AI鬼才聊天
  • 小結

簡介

前面我們介紹了:
進程間通信基礎介紹
通過AIDL介紹Binder的工作機制
通過 Bundle、文件共享、Messenger實現進程間通信
進程間通信的方式之AIDL
進程間通信的方式之ContentProvider

本文主要介紹進程間通信的方式之 Scoket。

Socket 也稱之爲 “套接字”,是網絡通信中的概念,分 流式套接字 和 用戶數據套接字,分別對應網絡的傳輸控制層中的 TCP 和 UDP 協議。
TCP是面向連接的協議,提供雙向通信功能,連接建立需要 “三次握手”,爲了傳輸的穩定性,其本身提供了超時機制。
UDP是無連接的,提供不穩定的單向通信功能,也可以提供雙向。
在性能上UDP有更好的效率,缺點就是不能保證數據正確傳輸,尤其是網絡擁塞的情況下。

下面我們來演示一個通過Socket實現跨進程聊天的程序,Socket本身可以支持傳輸任意的字節流,我們這裏僅傳輸文本信息。


實現跨進程與AI鬼才聊天

主要由 服務端 和 客戶端組成,在服務端創建一個 TCP服務, 然後在客戶端連接這個服務,然後開始相互聊天,來模仿一下“AI鬼才” 的對話:

在嗎?
在!
你好
你好!
能聽懂漢語嗎?
能聽懂漢語!
真的嗎?
真的!

1.首先申明網絡權限

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

然後注意一點在Android4.0 以上不能直接在UI線程進行網絡訪問。

2.實現服務端代碼

步驟大致爲:

  • 創建一個ServerSocket,響應客戶端的連接,注意服務端和客戶端爲一對多關係。(TCPServer)
  • 處理“AI鬼才”聊天的邏輯. (responseClient(...))
public class TCPServerService extends Service {
    public static int PORT = 10024;
    private static final String TAG = "TCPServerService";
    private boolean destroyed = false;
    public void onCreate() {
        new Thread(new TCPServer()).start();
        super.onCreate();
    }
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public void onDestroy() {
        destroyed = true;
        super.onDestroy();
    }
    private void responseClient(Socket client) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
        PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
        pw.println("welcome to chat room!");
        while (!destroyed) {
            String s = br.readLine();
            if (TextUtils.isEmpty(s)) {
                SystemClock.sleep(1000);
                continue;
            }
            Log.e(TAG, "client send msg = " + s);
            //AI鬼才邏輯
            if (s.contains("嗎?")) {
                s = s.replaceAll("嗎?", "!");
            } else {
                char end = s.charAt(s.length() - 1);
                if (end < '0' || (end > '9' && end < 'A')) {
                    s = s.substring(0, s.length() - 1) + "!";
                } else {
                    s += "!";
                }
            }
            if (s.contains("你")) {
                s = s.replaceAll("你", "我");
            }
            pw.println(s);
        }
        pw.close();
        br.close();
        client.close();
    }
    private class TCPServer implements Runnable {
        @Override
        public void run() {
            ServerSocket serverSocket = null;
            while (serverSocket == null) {
                try {
                    serverSocket = new ServerSocket(PORT);
                } catch (IOException e) {
                    Log.e(TAG, "establish tcp server failed, port =" + PORT);
                    PORT++;
                    SystemClock.sleep(1000);
                    Log.e(TAG, "retry...");
                }
            }
            while (!destroyed) {
                try {
                    Socket client = serverSocket.accept();
                    Log.e(TAG, "erverSocket accepted");
                    new Thread(() -> {
                        try {
                            responseClient(client);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }).start();
                } catch (IOException e) {
                    Log.e(TAG, "serverSocket.accept error");
                    SystemClock.sleep(1000);
                }
            }
        }
    }
}

AndroidManifest.xml 中聲明,配置爲 :remote 進程。

<manifest ...>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application ... >
        <service
            android:name="socket.TCPServerService"
            android:process=":remote" />
    </application>
</manifest>

3.實現客戶端代碼

步驟爲:

  • 啓動服務端服務 startService(new Intent(this, TCPServerService.class))
  • 初始化Socket,然後定時去讀服務端發的消息,注意要在非UI線程中初始化Socketinit()
  • 然後再界面上添加一個文本編輯和發送,注意發送要在非UI線程中執行sendMsg()
public class SocketTestActivity extends AppCompatActivity {
    private static final String TAG = "SocketTestActivity";
    private Socket mClientSocket;
    private PrintWriter mPrintWriter;
    private EditText editText;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_socket);
        editText = findViewById(R.id.input);
        startService(new Intent(this, TCPServerService.class));
        init();
        findViewById(R.id.send).setOnClickListener(v -> new Thread(() -> sendMsg()).start());
    }
    private void init() {
        new Thread(() -> {
            try {
                initSocket();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }
    private void initSocket() throws IOException {
        Socket socket = null;
        while (socket == null) {
            try {
                socket = new Socket("localhost", TCPServerService.PORT);
                mClientSocket = socket;
                mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(mClientSocket.getOutputStream())), true);
                Log.e(TAG, "connect server success");
            } catch (IOException e) {
                SystemClock.sleep(1000);
                Log.e(TAG, "initSocket: connect fail, retry....");
            }
        }
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        while (!SocketTestActivity.this.isFinishing()) {
            String msg = bufferedReader.readLine();
            if (TextUtils.isEmpty(msg)) {
                SystemClock.sleep(1000);
                continue;
            }
            Log.e(TAG, "receive msg from server = " + msg);
        }
        Log.e(TAG, "chat quit");
        mPrintWriter.close();
        bufferedReader.close();
    }
    private void sendMsg() {
        String temp = editText.getText().toString();
        if (TextUtils.isEmpty(temp) || mPrintWriter == null) {
            return;
        }
        mPrintWriter.println(temp);
        editText.setText("");
    }
    @Override
    protected void onDestroy() {
        if (mClientSocket != null) {
            try {
                mClientSocket.shutdownInput();
                mClientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (mPrintWriter != null) {
            mPrintWriter.close();
        }
        super.onDestroy();
    }
}

4.啓動程序,開始和 AI鬼才聊天

receive msg from server = welcome to chat room!
client send msg = 123
receive msg from server = 123!
client send msg = Hi
receive msg from server = Hi!
client send msg = understand?
receive msg from server = understand?!
client send msg = Really?
receive msg from server = Really?!
client send msg = NICE
receive msg from server = NICE!

如果覺得不錯的話,請幫忙點個讚唄。

以上


掃描下面的二維碼,關注我的公衆號 Android1024, 點關注,不迷路。
Android1024

`

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章