Android的IPC机制(七)—— Socket的原理简析与使用

来源:互联网 发布:windows原版aero主题 编辑:程序博客网 时间:2024/05/21 10:49

综述

  在前面的几篇文章中,我们介绍了许多在Android中有关进程间通信的方式,但都是在一个设备上进行的进程间通信,而这时候我们两个应用在不同的设备上的时候,在这个时候我们就不能通过前方介绍的那些方法来解决了。但是我们通过网络进行通信来处理这个问题。今天就来介绍一下Android中网络通信的其中一种方式——Socket。Socket翻译为中文为套接字,而现在套接字也成为了操作系统中的一部分。下面我们就来看一下如何使用这个套接字的。

TCP/IP介绍

  这里有一点需要说明一下,在Internet所使用的各种协议中,最重要的和最著名的就是TCP和IP两个协议。而我们现在经常提到的TCP/IP并不一定单指TCP和IP这两个具体的协议,而往往表示Internet所使用的整个TCP/IP协议族。

TCP/IP的体系结构

  TCP/IP协议可以为各式各样的应用提供服务,同时TCP/IP协议也允许IP协议在各式各样的网络构成的互联网上运行。TCP/IP协议是一个四层的体系结构,它包含应用层、传输层、网络层、网络接口层。在传输层中主要使用的有两种协议:TCP(Transmission Control Protocol,传输控制协议)和UDP(User Datagram Protocol,用户数据报协议)。下面是TCP/IP体系结构示意图。 
这里写图片描述

传输控制协议TCP

  TCP是TCP/IP体系中非常复杂的一个协议,在这里我们简单看一下TCP协议的特点,对于TCP协议的详细内容,可以查看一些计算机网络的相关书籍。 
1. TCP是面向连接传输协议,也就是说在我们的应用程序在使用TCP协议之前,必须先建立起TCP连接。在传送数据完毕后,必须释放已经建立的TCP连接。就像我们打电话一样,打电话之前首先需要拨号进行建立连接,等通话结束后再挂断释放连接。 
2. 每一条TCP连接只能有两个端点,每一条TCP连接只能是点对点的。 
3. TCP提供了一个可靠交付的服务,也就是说通过TCP连接传送的数据,无差错,不丢失,不重复,并且按序到达。 
4. TCP提供全双工通信,它允许通信双方的应用进程在任何时候都能够发送数据。 
5. TCP通信中是面向字节流的,其中的“流”指的是流入到进程或从进程流出的字节序列。

用户数据报协议UDP

  用户数据报协议UDP只是在IP的数据服务上增加了很少的一点功能(复用、分用的功能以及差错检测的功能)。在这里简单说一下UDP的特点。 
1. UDP是无连接,也就是在发送数据之前是不需要建立连接的,也就减少了开销和发送数据之间的延时。 
2. UDP它只能是尽最大努力地交付,也就是不能够保证可靠交付。 
3. UDP它是面向报文的。发送方的UDP对应用程序交下来的报文,再添加首部后就向下交付给IP层。 
4. UDP它没有拥塞控制,也就是说在网络出现拥塞的情况下不会使源主机的发送速率降低。 
5. UDP支持一对一,一对多,多对一和多对多的交互通信。

Socket在TCP/IP中的作用

  Socket是工作于TCP/IP协议中应用层和传输层之间的一种抽象(不属于应用层也不属于传输层)。在Android系统中,它可以分为流套接字(streamsocket)和数据报套接字(datagramsocket)。而Socket中的流套接字将TCP协议作为其端对端协议,提供了一个可信赖的字节流服务;数据报套接字使用UDP协议,提供数据打包发送服务。 
  在网络编程的时候,我们经常把Socket作为应用进程和传输层协议之间的接口。在下面图中表示了这样一个概念。在图中我们假定了运输层使用的是TCP协议(如果使用的是UDP协议,情况也是类似的,只是UDP是无连接的通信的两端依然可以用两个套接字来标志)。并且现在套接字已经成为操作系统内核的一部分。 
这里写图片描述
  不过有一点我们要注意,在套接字以上的进程是受应用程序控制的,而在套接字以下的的传输层协议软件则是由计算机操作系统控制。因此,只要我们的应用程序使用TCP/IP协议进行通信,它就必须通过Socket与操作系统交互并请求服务。从这里可以看出来,我们对Socket以上的应用进程具有完全的控制,但对Socket以下的传输层却只有很少的控制。例如,我们可以选择传输层协议(TCP或UDP)和一些传输层的参数(如最大缓存空间和最大报文长度)。

Socket使用案例

  在这里我们选择传输层协议为TCP协议,也就是我们将使用流套接字作为例子进行举例说明。现在我们现在做一个聊天室功能。在这里我们创建两个应用程序,分别运行在两个不同的设备上。首先看一下效果图。

演示

     

客户端代码

package com.example.ljd.socketclient;import android.annotation.SuppressLint;import android.os.Handler;import android.os.Message;import android.os.SystemClock;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.text.TextUtils;import android.view.View;import android.widget.EditText;import android.widget.LinearLayout;import android.widget.TextView;import android.widget.Toast;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.Socket;import java.sql.Date;import java.text.SimpleDateFormat;import butterknife.Bind;import butterknife.ButterKnife;import butterknife.OnClick;public class MainActivity extends AppCompatActivity{    private static final int RECEIVE_NEW_MESSAGE = 1;    private static final int SOCKET_CONNECT_SUCCESS = 2;    private static final int SOCKET_CONNECT_FAIL = 3;    @Bind(R.id.msg_edit_text)    EditText mMessageEditText;    @Bind(R.id.show_linear)    LinearLayout mShowLinear;    private PrintWriter mPrintWriter;    private Socket mClientSocket;    private boolean mIsConnectServer = false;    @SuppressLint("HandlerLeak")    private Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case RECEIVE_NEW_MESSAGE:                    TextView textView = new TextView(MainActivity.this);                    textView.setText((String)msg.obj);                    mShowLinear.addView(textView);                    break;                case SOCKET_CONNECT_SUCCESS:                    Toast.makeText(MainActivity.this,"连接服务端成功",Toast.LENGTH_SHORT).show();                    break;                case SOCKET_CONNECT_FAIL:                    Toast.makeText(MainActivity.this,"连接服务端失败,请重新尝试",Toast.LENGTH_SHORT).show();                    break;                default:                    break;            }        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ButterKnife.bind(this);    }    @Override    protected void onDestroy() {        ButterKnife.unbind(this);        disConnectServer();        super.onDestroy();    }    @OnClick({R.id.send_btn,R.id.connect_btn,R.id.disconnect_btn})    public void onClickButton(View v) {        switch (v.getId()){            case R.id.send_btn:                sendMessageToServer();                break;            case R.id.connect_btn:                new Thread() {                    @Override                    public void run() {                        connectServer();                    }                }.start();                break;            case R.id.disconnect_btn:                Toast.makeText(MainActivity.this,"已经断开连接",Toast.LENGTH_SHORT).show();                disConnectServer();                break;        }    }    private String getTime(long time) {        return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));    }    private void connectServer() {        if (mIsConnectServer)            return;        int count = 0;        while (mClientSocket == null) {            try {                mClientSocket = new Socket("10.10.14.160", 8088);                mPrintWriter = new PrintWriter(new BufferedWriter(                        new OutputStreamWriter(mClientSocket.getOutputStream())), true);                mIsConnectServer = true;                mHandler.obtainMessage(SOCKET_CONNECT_SUCCESS).sendToTarget();            } catch (IOException e) {                SystemClock.sleep(1000);                count++;                if (count == 5){                    mHandler.obtainMessage(SOCKET_CONNECT_FAIL).sendToTarget();                    return;                }            }        }        try {            // 接收服务器端的消息            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(                    mClientSocket.getInputStream()));            while (!MainActivity.this.isFinishing()) {                String msg = bufferedReader.readLine();                if (msg != null) {                    String time = getTime(System.currentTimeMillis());                    final String showedMsg = "server " + time + ":" + msg;                    mHandler.obtainMessage(RECEIVE_NEW_MESSAGE, showedMsg)                            .sendToTarget();                }            }            mPrintWriter.close();            bufferedReader.close();            mClientSocket.close();        } catch (IOException e) {            e.printStackTrace();        }    }    private void disConnectServer(){        mIsConnectServer = false;        if (mClientSocket != null) {            try {                mClientSocket.shutdownInput();                mClientSocket.close();                mClientSocket = null;            } catch (IOException e) {                e.printStackTrace();            }        }    }    private void sendMessageToServer(){        if (!mIsConnectServer){            Toast.makeText(this,"没有连接上服务端,请重新连接",Toast.LENGTH_SHORT).show();            return;        }        final String msg = mMessageEditText.getText().toString();        if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {            mPrintWriter.println(msg);            mMessageEditText.setText("");            String time = getTime(System.currentTimeMillis());            final String showedMsg = "client " + time + ":" + msg;            TextView textView = new TextView(this);            textView.setText(showedMsg);            mShowLinear.addView(textView);        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187

客户端布局代码

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    android:padding="5dp"    >    <ScrollView        android:layout_width="match_parent"        android:layout_height="0dp"        android:layout_weight="1" >        <LinearLayout            android:id="@+id/show_linear"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:orientation="vertical" >        </LinearLayout>    </ScrollView>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal">        <EditText            android:id="@+id/msg_edit_text"            android:layout_width="0dp"            android:layout_gravity="bottom"            android:layout_height="wrap_content"            android:layout_weight="1"            />        <Button            android:id="@+id/send_btn"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="发送" />    </LinearLayout>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal">        <Button            android:id="@+id/connect_btn"            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="wrap_content"            android:text="连接"/>        <Button            android:id="@+id/disconnect_btn"            android:layout_width="0dp"            android:layout_weight="1"            android:layout_height="wrap_content"            android:text="断开连接"/>    </LinearLayout></LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

服务端代码 

package com.example.ljd.socketserver;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.text.TextUtils;import android.widget.EditText;import android.widget.LinearLayout;import android.widget.TextView;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;import java.sql.Date;import java.text.SimpleDateFormat;import butterknife.Bind;import butterknife.ButterKnife;import butterknife.OnClick;public class MainActivity extends AppCompatActivity{    @Bind(R.id.show_linear)    LinearLayout mShowLinear;    @Bind(R.id.msg_edit_text)    EditText mMessageEditText;    private ServerSocket mServerSocket;    private PrintWriter mPrintWriter;    private Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            if (msg.what == 0){                TextView textView = new TextView(MainActivity.this);                textView.setText((String)msg.obj);                mShowLinear.addView(textView);            }            super.handleMessage(msg);        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ButterKnife.bind(this);        try {            mServerSocket = new ServerSocket(8088);        } catch (IOException e) {            e.printStackTrace();        }        new Thread(new AcceptClient()).start();    }    @Override    public void onDestroy() {        ButterKnife.unbind(this);        if (mServerSocket != null){            try {                mServerSocket.close();                mServerSocket = null;            } catch (IOException e) {                e.printStackTrace();            }        }        super.onDestroy();    }    @OnClick(R.id.send_btn)    public void onClickButton() {        final String msg = mMessageEditText.getText().toString();        if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {            //将消息写入到流中            mPrintWriter.println(msg);            mMessageEditText.setText("");            String time = getTime(System.currentTimeMillis());            final String showedMsg = "server " + time + ":" + msg;            TextView textView = new TextView(this);            textView.setText(showedMsg);            mShowLinear.addView(textView);        }    }    private String getTime(long time) {        return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));    }    class AcceptClient implements Runnable{        @Override        public void run() {            try {                Socket clientSocket = null;                while (clientSocket == null){                    clientSocket = mServerSocket.accept();                    mPrintWriter = new PrintWriter(new BufferedWriter(                            new OutputStreamWriter(clientSocket.getOutputStream())), true);                }                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(                        clientSocket.getInputStream()));                while (!MainActivity.this.isFinishing()) {                    //读取客户端发来的消息                    String msg = bufferedReader.readLine();                    if (msg != null) {                        String time = getTime(System.currentTimeMillis());                        final String showedMsg = "client " + time + ":" + msg;                        mHandler.obtainMessage(0, showedMsg)                                .sendToTarget();                    }                }                bufferedReader.close();                clientSocket.close();                mPrintWriter.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129

服务端布局代码

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    android:padding="5dp"    >    <ScrollView        android:layout_width="match_parent"        android:layout_height="0dp"        android:layout_weight="1" >        <LinearLayout            android:id="@+id/show_linear"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:orientation="vertical" >        </LinearLayout>    </ScrollView>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal">        <EditText            android:id="@+id/msg_edit_text"            android:layout_width="0dp"            android:layout_gravity="bottom"            android:layout_height="wrap_content"            android:layout_weight="1"            />        <Button            android:id="@+id/send_btn"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="发送" />    </LinearLayout></LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

添加权限

  在客户端与服务端应用中我们还需要添加一些权限

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

总结

  在网络通信中我们除了socket我们最常用的还有一种方式那就是http通信,而http连接采用的是”请求—响应方式“,也就是说只有当客户端发出请求时,服务端才能够向客户端返回数据。在这里我们就不在详细介绍。对于Android的IPC机制就说到这里了,当然还有其他方式可以进行跨进程通信,我们可以自行研究了。

源码下载

阅读全文
0 0
原创粉丝点击