Android's IPC communication - SOCKET method

1. Familiar with Socket

(1) Understand from its original meaning

  The English meaning of socket is "hole" or "socket". As the process communication mechanism of 4BDS UNIX, take the latter meaning. Also commonly referred to as a "socket" to describe an IP address and port, it is a handle to a communication chain. Applications typically make and respond to network requests through "sockets". A host on the Internet generally runs multiple service software and provides several services at the same time. Each service opens a Socket and binds it to a port, and different ports correspond to different services. Socket is like a multi-hole socket, just like its original meaning in English. A host is like a room full of sockets, each socket has a number, some sockets provide 220V AC, some provide 110V AC, and some provide cable TV programs. The client software inserts the plugs into different numbered sockets.

(2) From the perspective of java

 Applications typically make and respond to network requests through "sockets". Take J2SDK-1.3 as an example, the Socket and ServerSocket class libraries are located in the java.net package. ServerSocket is used on the server side, and Socket is used when establishing a network connection. When the connection is successful, both ends of the application will generate a Socket instance, operate this instance, and complete the required session. For a network connection, sockets are equal, there is no difference, and there is no difference between the server side and the client side. Whether it is Socket or ServerSocket, their work is done through the SocketImpl class and its subclasses.

(3) Simply put, it is an abstraction layer through which applications send and receive data. Using Socket, applications can be added to the network and communicate with other applications in the same network. Simply put, Socket provides a port for communication between the program and the outside world and provides a data transmission channel for both parties.

Two, socket classification

 Socket is divided into stream socket (streamsocket) and datagram socket (datagramsocket). Corresponding to the TCP and UDP protocols in network transmission control respectively. The TCP protocol is a connection-oriented protocol that provides stable two-way communication. UDP is connectionless and provides unstable one-way communication. Let's take a look at the basic implementation models of these two Socket types.

(1) TCP communication model


(2) UDP communication model

Third, the highlight: android realizes socket communication

(1) To use sockets to communicate, you first need to declare permissions

<!--Allow the application to change the network state-->    
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<!--Allow the application to fully use the network-->    
<uses-permission android:name="android.permission.INTERNET"/>    

(2) Design of the server

When the service starts, a TCP service will be established in the thread, which is listening on port 8688, and then it can wait for the client's connection request. When a client is connected, a new socket will be generated. The socket can communicate with different clients respectively.

public class TCPServiceService extends Service {
   private boolean mIsServiceDestroyed=false;
    private String[] mDefinedMessages=new String[]{
            "hello",
            "Are you ok",
            "haha",
            "yeah"
    };

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

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

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

    private class TcpServer implements Runnable{

            @SuppressWarnings("resource")
            @Override
            public void run() {
                ServerSocket serverSocket = null;
                try {
                    serverSocket = new ServerSocket(8688);
                } catch (IOException e) {
                    System.err.println("establish tcp server failed, port:8688");
                    e.printStackTrace();
                    return;
                }

                while (!mIsServiceDestroyed){
                try {
                    //接受客户端的请求
                    final Socket client=serverSocket.accept();
                    Log.d("test","accept");
                    new Thread(){
                        @Override
                        public void run() {
                            try {
                                responseClient(client);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        };
                    }.start();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void responseClient(Socket client) throws IOException {
        //接受客户端消息
        BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream()));
        //发送服务端消息
        PrintWriter out=new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())),true);
        out.println("欢迎来到聊天室!");
        while (!mIsServiceDestroyed) {
            String str = in.readLine();
            Log.d("test", "msg from client:" + str);
            if (str == null) {
                break;
            }
            int i = new Random().nextInt(mDefinedMessages.length);
            String msg = mDefinedMessages[i];
            out.println(msg);
            Log.d("test:", "send" + msg);
        }
            Log.d("test:","client quit");
            client.close();
}
(2)客户端的设计
为了确认能够连接成功,这里采用了超市重连的策略,每次连接失败后都会重新建立尝试建立连接。等然为了降低重新机制的开销,我们加入休眠机制,即加上了每次重试的时间间隔。

服务端连接成功以后,就可以和服务端进行通信了。

注意:Activity退出时,要关闭当前的socket。

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    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;
    private Socket mClientSocket;
    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:
                {
                    mSendButton.setEnabled(true);
                    break;
                }
                default:
                    break;
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mMessageTextView=(TextView) findViewById(R.id.tv_contents);
        mSendButton=(Button) findViewById(R.id.send);
        mSendButton.setOnClickListener(this);
        mMessageEditText=(EditText) findViewById(R.id.ed_news);
        Intent service=new Intent(this,TCPServiceService.class);
        startService(service);
        new Thread() {
            @Override
            public void run() {
                connectTCPService();
            }
        }.start();
    }

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

    private void connectTCPService() {
        Socket socket = null;
        while (socket == null) {
            try {
                socket = new Socket("localhost", 8688);
                mClientSocket = socket;
                mPrintWriter = new PrintWriter(new BufferedWriter(
                        new OutputStreamWriter(socket.getOutputStream())), true);
                mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
                System.out.println("connect server success");
            } catch (IOException e) {
                SystemClock.sleep(1000);
                System.out.println("connect tcp server failed, retry...");
            }
        }
        try {
            BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while (!MainActivity.this.isFinishing()) {
                String msg = br.readLine();
                if (msg != null) {
                    String time = formatDateTime(System.currentTimeMillis());
                    final String showedMsg = "server" + time + ":" + msg + "\n";
                    mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showedMsg).sendToTarget();
                }
            }
            Log.d("text","quit...");
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //把当前时间转化成自定义格式
private String formatDateTime(long time){
    return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));
}
    @Override
    public void onClick(View view) {
        if(view==mSendButton){
            final String msg=mMessageEditText.getText().toString();
            if(!TextUtils.isEmpty(msg)&&mPrintWriter!=null){
                mPrintWriter.println(msg);
                mMessageEditText.setText("");
                String time=formatDateTime(System.currentTimeMillis());
                final String showedMsg="self"+time+":"+msg+"\n";
                mMessageTextView.setText(mMessageTextView.getText()+showedMsg);
            }
        }
    }
}
四、小结一下

上述就是通过socket来进行进程间通信的实例,除了采用TCP套接字,还可以采用UDP套接字。实际上通过socket不仅仅能实现进程间通信,还可以实现设备间的通信,当然前提是这些设备之间的IP地址互相可见。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325569234&siteId=291194637