Android 开发艺术探索笔记(7)

使用Socket进行IPC通信

Socket又称为套接字,分为流式套接字和用户数据包套接字两种,分别对应网络的传输控制层的TCP和UDP协议。
接下来来设计一个聊天室实现Socket进行IPC通信。用远程Service来建立一个TCP服务,在主界面来连接这个TCP,然后在客户端发消息给服务端,服务端都会随机回复一句话。我们在服务端做一些处理来实现其能和多个客户端同时建立连接并响应。
先要声明权限:

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

这里是服务端的设计。当Service启动的时候,会在线程中建立TCP服务,这里监听的是8688端口,当有客户连接的时候就建立一个Socket,然后每次创建新的Socket就可以分别和不同的客户通话了。当客户端断开连接时,服务端也会关闭对应的Socket,通过输入流来关闭,客户端关闭的时候输入流会返回null

package com.rikka.socket1;

import android.app.Service;
import android.content.Intent;
import android.os.BatteryManager;
import android.os.IBinder;
import android.support.annotation.Nullable;

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.util.Random;

public class TCPServerService extends Service {

    private boolean mIsServiceDestoryed = false;
    private String[] mDefinedMessage = new String[]{  //随即回复的信息
            "给次机会我啊",
            "怎么给你机会?",
            "我以前没得选,现在我选回做好人",
            "好啊,你去跟法官说啊,看他给不给你当",
            "你这是让我死?",
            "对唔住,我是差人"
    };

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

    private class TcpServer implements Runnable{
        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
                //监听本地8688接口
                serverSocket = new ServerSocket(8868);
            } catch (IOException e) {
                System.err.println("establish tcp server failed,port:8868");
                e.printStackTrace();
                return ;
            }

            while (!mIsServiceDestoryed){
                try {
                    //接收客户请求
                    final Socket client = serverSocket.accept();
                    System.out.println("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 (!mIsServiceDestoryed){
            String str = in.readLine();
            System.out.println("msg from client:" + str);
            if(str == null){
                //客户端断开连接
                break;
            }
            int i = new Random().nextInt(mDefinedMessage.length);
            String msg = mDefinedMessage[i];
            out.println(msg);
            System.out.println("send :" + msg);
        }
        //客户端退出的操作
        System.out.println("client quit");
        //关闭流
        out.close();
        in.close();
        client.close();
    }

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

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

接下来看客户端,当activity启动的时候会在onCreate中开启一个线程去连接服务器Socket,这里采用超时重连的策略,每次连接失败后都会重新尝试连接,为了降低重试机制的开销,我们加入了休眠模式,即每次重试间隔为1s

package com.rikka.socket1;

import android.app.Activity;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

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.net.UnknownHostException;
import com.rikka.socket1.TCPServerService;

public class MainActivity extends Activity {

    private Socket mClientSocket,socket = null;
    private Handler mHandler = new Handler();
    private int MESSAGE_SOCKET_CONNECTED = 1;
    private int MESSAGE_RECEIVE_NEW_MSG = 2;

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

        //连接socket
        while(socket == null){
            try {
                socket = new Socket("localhost",8688);
                mClientSocket = socket;
                PrintWriter mPrintWrite = 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);
                e.printStackTrace();
            }
        }

        //接收服务端的消息
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while(!MainActivity.this.isFinishing()){
                String msg = br.readLine();
                System.out.println("receive :" + msg);
                if(msg != null){
                    long time = System.currentTimeMillis();
                    final String showMsg = "server " + time +":" + msg + "\n";
                    mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG,showMsg).sendToTarget();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //当Activity退出时则关闭当前Socket
    @Override
    protected void onDestroy() {
        if(mClientSocket != null){
            try {
                mClientSocket.shutdownInput();
                mClientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        super.onDestroy();
    }
}

接下来就是发送消息的过程,将上述的端口修改为53635(自己的端口),完善Activity

package com.rikka.socket1;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
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.Socket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.rikka.socket1.TCPServerService;

public class MainActivity extends Activity implements View.OnClickListener {

    private Socket mClientSocket;
    @SuppressLint("HandlerLeak")
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_SOCKET_CONNECTED:
                    btn.setEnabled(true);
                    break;
                case MESSAGE_RECEIVE_NEW_MSG:
                    tv.setText(tv.getText() + (String)msg.obj);
                    break;
                default: break;
            }
        }
    };
    private static final int MESSAGE_SOCKET_CONNECTED = 1;
    private static final int MESSAGE_RECEIVE_NEW_MSG = 2;

    private TextView tv;
    private Button btn;
    private EditText et;

    private  PrintWriter mPrintWrite;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
        //绑定服务
        Intent intent = new Intent(this,TCPServerService.class);
        startService(intent);
        new Thread(){
            @Override
            public void run() {
                connectTCPServer();
            }
        }.start();

    }


    private void init() {
        tv = findViewById(R.id.tv);
        btn = findViewById(R.id.btn);
        et = findViewById(R.id.et);

        btn.setOnClickListener(this);
    }

    /**
     * 开启线程来连接Socket
     */
    private void connectTCPServer() {
        Socket socket = null;
        while(socket == null){
            try {
                socket = new Socket("localhost",53635);
                mClientSocket = socket;
                mPrintWrite = 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);
                e.printStackTrace();
            }
        }

        //接收服务端的消息
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while(!MainActivity.this.isFinishing()){
                String msg = br.readLine();
                System.out.println("receive :" + msg);
                if(msg != null){
                    String time = formatDateTime(System.currentTimeMillis());
                    final String showMsg = "server " + time +":" + msg + "\n";
                    mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG,showMsg).sendToTarget();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //当Activity退出时则关闭当前Socket
    @Override
    protected void onDestroy() {
        if(mClientSocket != null){
            try {
                mClientSocket.shutdownInput();
                mClientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        super.onDestroy();
    }

    /**
     * 点击Btn发送信息
     * @param v
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn:
                final String msg = et.getText().toString();
                if(!msg.isEmpty() && mPrintWrite != null){
                    new Thread(){
                        @Override
                        public void run() {
                            mPrintWrite.println(msg);
                        }
                    }.start();
                    et.setText("");
                    String time = formatDateTime(System.currentTimeMillis());
                    final String showMsg = "self " + time + ":" + msg + "\n";
                    tv.setText(tv.getText() + showMsg);
                }
        }
    }

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

Socket连接成功,显示效果如下:
这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值