Android的socket通信的长连接,有心跳检测

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zh724738989/article/details/42007099

        在Android开发中,我们可能需要和服务器保持连接不断开,这时需要用到socket通信的长连接,并且定时发送消息检测是否是连接状态——心跳检测。

        我们需要一个客户端和一个服务器端的demo,现在我就贴出重要代码,主要是Android客户端的,服务器端的demo供大家下载。

首先我们需要新建一个BackService类来继承Service:

package com.example.sockettest;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Arrays;

import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class BackService extends Service {
	private static final String TAG = "BackService";
	/** 心跳检测时间  */
	private static final long HEART_BEAT_RATE = 3 * 1000;
	/** 主机IP地址  */
	private static final String HOST = "192.168.1.104";
	/** 端口号  */
	public static final int PORT = 9800;
	/** 消息广播  */
	public static final String MESSAGE_ACTION = "org.feng.message_ACTION";
	/** 心跳广播  */
	public static final String HEART_BEAT_ACTION = "org.feng.heart_beat_ACTION";

	private long sendTime = 0L;

	/** 弱引用 在引用对象的同时允许对垃圾对象进行回收  */
	private WeakReference<Socket> mSocket;

	private ReadThread mReadThread;

	private IBackService.Stub iBackService = new IBackService.Stub() {
		@Override
		public boolean sendMessage(String message) throws RemoteException {
			return sendMsg(message);
		}
	};

	@Override
	public IBinder onBind(Intent arg0) {
		return (IBinder) iBackService;
	}

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

	// 发送心跳包
	private Handler mHandler = new Handler();
	private Runnable heartBeatRunnable = new Runnable() {
		@Override
		public void run() {
			if (System.currentTimeMillis() - sendTime >= HEART_BEAT_RATE) {
				boolean isSuccess = sendMsg("");// 就发送一个\r\n过去, 如果发送失败,就重新初始化一个socket
				if (!isSuccess) {
					mHandler.removeCallbacks(heartBeatRunnable);
					mReadThread.release();
					releaseLastSocket(mSocket);
					new InitSocketThread().start();
				}
			}
			mHandler.postDelayed(this, HEART_BEAT_RATE);
		}
	};

	public boolean sendMsg(String msg) {
		if (null == mSocket || null == mSocket.get()) {
			return false;
		}
		Socket soc = mSocket.get();
		try {
			if (!soc.isClosed() && !soc.isOutputShutdown()) {
				OutputStream os = soc.getOutputStream();
				String message = msg + "\r\n";
				os.write(message.getBytes());
				os.flush();
				sendTime = System.currentTimeMillis();// 每次发送成功数据,就改一下最后成功发送的时间,节省心跳间隔时间
				Log.i(TAG, "发送成功的时间:" + sendTime);
			} else {
				return false;
			}
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}

	// 初始化socket
	private void initSocket() throws UnknownHostException, IOException {
		Socket socket = new Socket(HOST, PORT);
		mSocket = new WeakReference<Socket>(socket);
		mReadThread = new ReadThread(socket);
		mReadThread.start();
		mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);// 初始化成功后,就准备发送心跳包
	}

	// 释放socket
	private void releaseLastSocket(WeakReference<Socket> mSocket) {
		try {
			if (null != mSocket) {
				Socket sk = mSocket.get();
				if (!sk.isClosed()) {
					sk.close();
				}
				sk = null;
				mSocket = null;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	class InitSocketThread extends Thread {
		@Override
		public void run() {
			super.run();
			try {
				initSocket();
			} catch (UnknownHostException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public class ReadThread extends Thread {
		private WeakReference<Socket> mWeakSocket;
		private boolean isStart = true;

		public ReadThread(Socket socket) {
			mWeakSocket = new WeakReference<Socket>(socket);
		}

		public void release() {
			isStart = false;
			releaseLastSocket(mWeakSocket);
		}

		@SuppressLint("NewApi")
		@Override
		public void run() {
			super.run();
			Socket socket = mWeakSocket.get();
			if (null != socket) {
				try {
					InputStream is = socket.getInputStream();
					byte[] buffer = new byte[1024 * 4];
					int length = 0;
					while (!socket.isClosed() && !socket.isInputShutdown()
							&& isStart && ((length = is.read(buffer)) != -1)) {
						if (length > 0) {
							String message = new String(Arrays.copyOf(buffer,
									length)).trim(); 
							Log.i(TAG, "收到服务器发送来的消息:"+message); 
							// 收到服务器过来的消息,就通过Broadcast发送出去  
							if (message.equals("ok")) {// 处理心跳回复
								Intent intent = new Intent(HEART_BEAT_ACTION);
								sendBroadcast(intent);
							} else {
								// 其他消息回复
								Intent intent = new Intent(MESSAGE_ACTION);
								intent.putExtra("message", message);
								sendBroadcast(intent);
							}
						} 
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

}

关键代码已经注释了,相信大家应该可以看懂。在这个类中关联了一个IBackService的类,新建一个IBackService.aidl。对,没错,就是新建一个IBackService.aidl,关于aidl的知识请查阅相关文档。代码如下:

package com.example.sockettest;
interface IBackService{
	boolean sendMessage(String message);
}

现在就是MainActivity了,这就是一个activity,接收广播,改变UI,就不多说了:

package com.example.sockettest;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {
	private static final String TAG = "MainActivity";

	private Intent mServiceIntent;
	private IBackService iBackService;
	private TextView tv;
	private EditText et;
	private Button btn;

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

		initViews();
		initData();
	}

	private void initViews() {
		tv = (TextView) findViewById(R.id.tv);
		et = (EditText) findViewById(R.id.editText1);
		btn = (Button) findViewById(R.id.button1);
	}

	private void initData() {
		mServiceIntent = new Intent(this, BackService.class);
		btn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				String string = et.getText().toString().trim();
				Log.i(TAG, string);
				try {
					Log.i(TAG, "是否为空:" + iBackService);
					if (iBackService == null) {
						Toast.makeText(getApplicationContext(),
								"没有连接,可能是服务器已断开", Toast.LENGTH_SHORT).show();
					} else {
						boolean isSend = iBackService.sendMessage(string);
						Toast.makeText(MainActivity.this,
								isSend ? "success" : "fail", Toast.LENGTH_SHORT)
								.show();
						et.setText("");
					}
				} catch (RemoteException e) {
					e.printStackTrace();
				}
			}
		});
	}

	@Override
	protected void onStart() {
		super.onStart();
		bindService(mServiceIntent, conn, BIND_AUTO_CREATE);
		// 开始服务
		registerReceiver();
	}

	@Override
	protected void onResume() {
		super.onResume();
		// 注册广播 最好在onResume中注册
		// registerReceiver();
	}

	@Override
	protected void onPause() {
		super.onPause();
		// 注销广播 最好在onPause上注销
		unregisterReceiver(mReceiver);
		// 注销服务
		unbindService(conn);
	}

	// 注册广播
	private void registerReceiver() {
		IntentFilter intentFilter = new IntentFilter();
		intentFilter.addAction(BackService.HEART_BEAT_ACTION);
		intentFilter.addAction(BackService.MESSAGE_ACTION);
		registerReceiver(mReceiver, intentFilter);
	}

	private BroadcastReceiver mReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			// 消息广播
			if (action.equals(BackService.MESSAGE_ACTION)) {
				String stringExtra = intent.getStringExtra("message");
				tv.setText(stringExtra);
			} else if (action.equals(BackService.HEART_BEAT_ACTION)) {// 心跳广播
				tv.setText("正常心跳");
			}
		}
	};

	private ServiceConnection conn = new ServiceConnection() {
		@Override
		public void onServiceDisconnected(ComponentName name) {
			// 未连接为空
			iBackService = null;
		}

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// 已连接
			iBackService = IBackService.Stub.asInterface(service);
		}
	};
}
</pre><p></p><pre>

好了,我们可以运行项目了。


这是前台显示的界面,正在发送心跳检测,发送给服务器的是空字符,返回的是ok字符,前台显示心跳正常,说明连接正常。

这是后台接收到的空字符,发送ok字符给前台。

现在我在前台界面输入内容发送给后台

后台收到前台消息,并返回一个数据:

前台收到后台的数据并改变UI

并且他们的连接是一直保持的,客户端会一直发送心跳判断是否还是连接中的,亲测demo是没有问题的。

demo下载地址:http://download.csdn.net/download/zh724738989/8274647

猜你喜欢

转载自blog.csdn.net/zh724738989/article/details/42007099