Fourteen, Android in the way of IPC (6) --- using Socket

Disclaimer: This article is a blogger original article, shall not be reproduced without the bloggers allowed. https://blog.csdn.net/yz_cfm/article/details/90550691

    What is Socket?

    Two computers communicate in a network, we can locate a particular IP address to a computer, and then through the port number to locate a particular application this computer. A computer so that we can make the appropriate communication through the IP address and port number and another computer. Then we targeted to how a specific computer and its corresponding communication it? Communication between them is to use the programming interface Socket offer. Socket called "socket", popular understanding is that it provides a way of inter-process communication, and provides an inter-process corresponding programming interfaces for communication.

    In general, Socket communication is divided into stream sockets user datagram sockets and two kinds respectively corresponding to the transport network control layer TCP and UDP protocols .

    TCP is a connection-oriented protocol agreement provides a stable two-way communication function, TCP connection establishment need to go through "three-way handshake" to complete, in order to provide a stable data transmission, which itself provides a timeout retransmission mechanism, and therefore has a high stability; and the UDP is a connectionless, there is provided a communication unstable single function, of course, also be realized UDP bi-directional communication function. In performance, UDP has a better efficiency, the drawback is no guarantee of transmission will be able to correct, especially in the case of network congestion.

    Briefly about the UDP and TCP:

    1. UDP

    FIG UDP communication model:

    Illustration: UDP Client (client) located by ip address and port corresponding to the port UDP Server (server), and then package the information of DatagramPacket send method DatagramSocket by transmitting to the server class, received by the server after parsing the same and then to return to the client is transmitted by way of DatagramSocket.

    UDP write mode of communication like manner, the sender information is written letter, and the letter before being passed to the addressee, the addressee opens the envelope and read the letter on receipt, and then the same way to decide whether to reply to the sender. Since we can not determine whether the other party after the receipt of the letter sent, it is usually defined as a communication protocol UDP insecure.

Related to the API:

- InetAddress (ip, port wrapper class)

- DatagramSocket( receive, send)

- Datagram Packet

    2. TCP

    FIG TCP communication model:

    TCP is similar to the way phone calls. Once turned on, we can continuously communicate, in fact it is a more secure protocol.

Related to the API:

- ServerSocket (Server端)

- Socket (Client side)

- and the associated IO class

    

    Combat: Use TCP Socket communication mode is completed interprocess communication

    1. First, due to the use of network communication, so to declare the permissions in AndroidManifest.xml file:

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

    2. The server code (using the ServerSocket), TCPServerService.java:

package com.cfm.sockettest;

public class TCPServerService extends Service {

    private static final String TAG = "cfmtest";
    // 当服务销毁时,结束服务端与客户端之间通信的线程
    private boolean mIsServiceDestoryed = false;
    private String[] mDefinedMessages = new String[]{
            "坚决的信心,能使平凡的人们,做出惊人的事业。——马尔顿",
            "人生是个圆,有的人走了一辈子也没有走出命运画出的圆圈,其实,圆上的每一个点都有一条腾飞的切线。",
            "要有自信,然后全力以赴——假如具有这种观念,任何事情十之八九都能成功。——威尔逊",
            "行动是治愈恐惧的良药,而犹豫、拖延将不断滋养恐惧。",
            "天道酬勤。也许你付出了不一定得到回报,但不付出一定得不到回报。",
            "一个人是否有成就只有看他是否具有自尊心和自信心两个条件。——苏格拉底",
            "宝剑锋从磨砺出,梅花香自苦寒来。"
    };

    @Override
    public void onCreate() {
        super.onCreate();
        new Thread(new TcpServer()).start();
    }

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

    private class TcpServer implements Runnable{

        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
                // 创建服务端 Socket
                serverSocket = new ServerSocket(8868);
            } catch (IOException e) {
                e.printStackTrace();
            }

            // 接收客户端的请求
            while (!mIsServiceDestoryed){
                try {
                    if (serverSocket != null) {

                        // 连接到服务端的客户端 Socket 对象
                        final Socket socket = serverSocket.accept();
                        Log.d(TAG, "服务端接收到客户端的连接请求!");
                        new Thread(new Runnable() {

                            @Override
                            public void run() {
                                try {
                                    responseClient(socket);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }).start();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void responseClient(Socket clientSocket) throws IOException{

        // 用于接收客户端消息
        BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

        // 用于向客户端发送消息
        PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())), true);

        while (!mIsServiceDestoryed){
            String str = in.readLine();
            Log.d(TAG, "服务端接收到客户端发送的消息: " + str);
            if(str == null)break;
            // 随机回复一句箴言
            int i = new Random().nextInt(mDefinedMessages.length);
            String msg = mDefinedMessages[i];
            out.println(msg); // 服务端向客户端回复消息
            Log.d(TAG, "服务端发送给客户端的箴言: " + msg);
        }

        Log.d(TAG, "服务端连接断开!");
        in.close();
        out.close();
        clientSocket.close();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mIsServiceDestoryed = true;
    }
}

    3. The client-side code, TCPClientActivity.java:

package com.cfm.sockettest;

public class TCPClientActivity extends AppCompatActivity {

    private static final String TAG = "cfmtest";
    private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
    private static final int MESSAGE_SOCKET_CONNECTED = 2;
    private Button mSendButton;
    private TextView mMessageTextView;
    private EditText mMessageEditText;

    // 向服务端发送数据
    private PrintWriter mPrintWriter;

    // 客户端 Socket
    private Socket mClientSocket;

    // 通过 Handler 从子线程切换到主线程以方便更新 UI
    private Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_RECEIVE_NEW_MSG:
                    mMessageTextView.setText(mMessageTextView.getText() + (String) msg.obj);
                    break;

                case MESSAGE_SOCKET_CONNECTED:
                    // 与服务端 Socket 连接成功,使能发送按钮
                    mSendButton.setEnabled(true);
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tcpclient);
        mMessageEditText = findViewById(R.id.msg);
        mMessageTextView = findViewById(R.id.msg_container);
        mSendButton = findViewById(R.id.send);

        // 开始远程 Service
        Intent intent = new Intent(this, TCPServerService.class);
        startService(intent);

        // 连接服务端 Socket
        new Thread(new Runnable() {

            @Override
            public void run() {
                connectTCPServer();
            }
        }).start();

        mSendButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                final String msg = mMessageEditText.getText().toString();
                Log.d(TAG,"客户端向服务端发送的消息: " + msg);
                if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            // 发送消息给客户端,注意这里要在线程中发送,否则会 crash
                            mPrintWriter.println(msg);
                        }
                    }).start();
                    mMessageEditText.setText("");  // 清空输入框
                    String time = formatDateTime(System.currentTimeMillis());
                    String showedMsg = "self " + time + ":" + msg + "\n";
                    // 将客户端的发送消息的时间和内容显示出来
                    mMessageTextView.setText(mMessageTextView.getText() + showedMsg);
                }
            }
        });
    }

    private String formatDateTime(long time) {
        return new SimpleDateFormat("(HH:mm:ss)", Locale.CHINA).format(new Date(time));
    }

    private void connectTCPServer(){
        Socket socket = null;
        while (socket == null){
            // 如果没连接成功就一直尝试连接
            try {
                socket = new Socket("localhost", 8868);
                mClientSocket = socket;
                mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);

                // 连接成功
                mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
                Log.d(TAG, "客户端连接服务端成功!");
            } catch (IOException e) {
                SystemClock.sleep(1000);
                e.printStackTrace();
            }
        }

        // 接收服务端消息
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            while (!TCPClientActivity.this.isFinishing()){
                String msg = in.readLine();
                Log.d(TAG, "客户端接收到的服务端消息: " + msg);
                if(msg != null){
                    String time = formatDateTime(System.currentTimeMillis());
                    String showedMsg = "server " + time + ":" + msg + "\n";
                    mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showedMsg).sendToTarget();
                }
            }
            Log.d(TAG, "客户端连接中断!");
            in.close();
            mPrintWriter.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

activity_tcpclient.xml:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="8dp"
    android:background="#ffffff"
    tools:context=".TCPClientActivity">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:id="@+id/msg_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
        </LinearLayout>

    </ScrollView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <EditText
            android:id="@+id/msg"
            android:layout_width="0dp"
            android:layout_height="35dp"
            android:layout_weight="1"
            android:ems="10"
            android:padding="8dp"/>

        <Button
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            android:enabled="false"
            android:text="发送"/>
    </LinearLayout>

</LinearLayout>

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.cfm.sockettest">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <service
            android:name=".TCPServerService"
            android:enabled="true"
            android:exported="true"
            android:process=":remote">
        </service>

        <activity android:name=".TCPClientActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

Print Log Information:

// 服务端
2019-05-25 19:29:40.546 31371-31395/com.cfm.sockettest:remote D/cfmtest: 服务端接收到客户端的连接请求!
2019-05-25 19:29:44.447 31371-31417/com.cfm.sockettest:remote D/cfmtest: 服务端接收到客户端发送的消息: 123
2019-05-25 19:29:44.449 31371-31417/com.cfm.sockettest:remote D/cfmtest: 服务端发送给客户端的箴言: 天道酬勤。也许你付出了不一定得到回报,但不付出一定得不到回报。
2019-05-25 19:29:47.760 31371-31417/com.cfm.sockettest:remote D/cfmtest: 服务端接收到客户端发送的消息: 456
2019-05-25 19:29:47.761 31371-31417/com.cfm.sockettest:remote D/cfmtest: 服务端发送给客户端的箴言: 坚决的信心,能使平凡的人们,做出惊人的事业。——马尔顿
2019-05-25 19:29:50.301 31371-31417/com.cfm.sockettest:remote D/cfmtest: 服务端接收到客户端发送的消息: 789
2019-05-25 19:29:50.302 31371-31417/com.cfm.sockettest:remote D/cfmtest: 服务端发送给客户端的箴言: 一个人是否有成就只有看他是否具有自尊心和自信心两个条件。——苏格拉底
2019-05-25 19:29:52.963 31371-31417/com.cfm.sockettest:remote D/cfmtest: 服务端接收到客户端发送的消息: 147
2019-05-25 19:29:52.964 31371-31417/com.cfm.sockettest:remote D/cfmtest: 服务端发送给客户端的箴言: 行动是治愈恐惧的良药,而犹豫、拖延将不断滋养恐惧。
2019-05-25 19:31:17.807 31371-31417/com.cfm.sockettest:remote D/cfmtest: 服务端接收到客户端发送的消息: null
2019-05-25 19:31:17.807 31371-31417/com.cfm.sockettest:remote D/cfmtest: 服务端连接断开!

// 客户端
2019-05-25 19:29:40.546 31335-31376/com.cfm.sockettest D/cfmtest: 客户端连接服务端成功!
2019-05-25 19:29:44.442 31335-31335/com.cfm.sockettest D/cfmtest: 客户端向服务端发送的消息: 123
2019-05-25 19:29:44.450 31335-31376/com.cfm.sockettest D/cfmtest: 客户端接收到的服务端消息: 天道酬勤。也许你付出了不一定得到回报,但不付出一定得不到回报。
2019-05-25 19:29:47.755 31335-31335/com.cfm.sockettest D/cfmtest: 客户端向服务端发送的消息: 456
2019-05-25 19:29:47.762 31335-31376/com.cfm.sockettest D/cfmtest: 客户端接收到的服务端消息: 坚决的信心,能使平凡的人们,做出惊人的事业。——马尔顿
2019-05-25 19:29:50.293 31335-31335/com.cfm.sockettest D/cfmtest: 客户端向服务端发送的消息: 789
2019-05-25 19:29:50.302 31335-31376/com.cfm.sockettest D/cfmtest: 客户端接收到的服务端消息: 一个人是否有成就只有看他是否具有自尊心和自信心两个条件。——苏格拉底
2019-05-25 19:29:52.960 31335-31335/com.cfm.sockettest D/cfmtest: 客户端向服务端发送的消息: 147
2019-05-25 19:29:52.965 31335-31376/com.cfm.sockettest D/cfmtest: 客户端接收到的服务端消息: 行动是治愈恐惧的良药,而犹豫、拖延将不断滋养恐惧。
2019-05-25 19:31:54.944 31335-31996/com.cfm.sockettest D/cfmtest: 客户端接收到的服务端消息: null
2019-05-25 19:31:54.944 31335-31996/com.cfm.sockettest D/cfmtest: 客户端连接中断!

Renderings:

 

Guess you like

Origin blog.csdn.net/yz_cfm/article/details/90550691
IPC