原理
最近用socket写了一个消息推送的demo,在这里和大家分享一下。
主要实现了:一台手机向另外一台手机发送消息,这两台手机可以随时自由发送文本消息进行通信,类似我们常用的QQ。
效果图:
原理:手机通过socket发送消息到服务器,服务器每接收到一条消息之后,都会把这条消息放进一个messageList里面,服务器会不停地检测messageList是否含有消息,如果有的话就会根据messageList里面item的数据,推送到相应的另一端手机上面。
下面简单画了一个图来说明这个原理:
演示:手机客户端client1发送消息msg1到手机客户端client2,client2收到消息后回复msg2给client1
1.手机客户端client1发送一条“msg1”的文本消息到服务器;
2.服务器收到来自client1的“msg1”消息后,把它add进messageList里面;
3.服务器检测到messageList里面含有消息(开启服务器时就新建里一个检测messageList的线程,线程里面有一个死循环,用于不停检测messageList是否含有消息);
4.服务器读取消息数据,如读取到来自client1发给client2的消息“msg1”,那么服务器就把“msg1”推送到client2上;
5.client2检测到服务器推送的消息,做出相应的操作(如:震动、铃声、显示消息等);
6.client2接收到来自服务器推送的“msg1”消息后,client2也回复一条文本消息“msg2”给client1,此过程和client1发送消息给client2一样。
7.最后,client2就可以显示来自client1发送的消息“msg1”,而client1则可以显示来自client2的回复消息“msg2”。
实现过程
根据消息推送的原理图,我们的实现过程主要分为Server端和Client端,Server端采用Java的编程,而Client端则用Android编程。
所以在这里也分别创建了两个工程SocketServer和SocketClient
我们先来看一下SocketMessage.java类:
public class SocketMessage {
public int to;//socketID,指发送给谁
public int from;//socketID,指谁发送过来的
public String msg;//消息内容
public String time;//接收时间
public SocketThread thread;//socketThread下面有介绍
}
该类是一个消息类,用于表示消息是由谁发给谁的、消息内容是什么、接收时间是多少,只有几个属性,比较简单。
而MyServer.java类就相对比较多一些代码:
package com.jimstin.server;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import org.json.JSONObject;
import com.jimstin.msg.SocketMessage;
public class MyServer {
private boolean isStartServer;
private ServerSocket mServer;
/**
* 消息队列,用于保存SocketServer接收来自于客户机(手机端)的消息
*/
private ArrayList<SocketMessage> mMsgList = new ArrayList<SocketMessage>();
/**
* 线程队列,用于接收消息。每个客户机拥有一个线程,每个线程只接收发送给自己的消息
*/
private ArrayList<SocketThread> mThreadList = new ArrayList<SocketThread>();
/**
* 开启SocketServer
*/
private void startSocket() {
try {
isStartServer = true;
int prot = 2000;//端口可以自己设置,但要和Client端的端口保持一致
mServer = new ServerSocket(prot);//创建一个ServerSocket
System.out.println(\"启动server,端口:\"+prot);
Socket socket = null;
int socketID = 0;//Android(SocketClient)客户机的唯一标志,每个socketID表示一个Android客户机
//开启发送消息线程
startSendMessageThread();
//用一个循环来检测是否有新的客户机加入
while(isStartServer) {
//accept()方法是一个阻塞的方法,调用该方法后,
//该线程会一直阻塞,直到有新的客户机加入,代码才会继续往下走
socket = mServer.accept();
//有新的客户机加入后,则创建一个新的SocketThread线程对象
SocketThread thread = new SocketThread(socket, socketID++);
thread.start();
//将该线程添加到线程队列
mThreadList.add(thread);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 开启推送消息线程,如果mMsgList中有SocketMessage,则把该消息推送到Android客户机
*/
public void startSendMessageThread() {
new Thread(){
@Override
public void run() {
super.run();
try {
/*如果isStartServer=true,则说明SocketServer已启动,
用一个循环来检测消息队列中是否有消息,如果有,则推送消息到相应的客户机*/
while(isStartServer) {
//判断消息队列中的长度是否大于0,大于0则说明消息队列不为空
if(mMsgList.size() > 0) {
//读取消息队列中的第一个消息
SocketMessage from = mMsgList.get(0);
for(SocketThread to : mThreadList) {
if(to.socketID == from.to) {
BufferedWriter writer = to.writer;
JSONObject json = new JSONObject();
json.put(\"from\", from.from);
json.put(\"msg\", from.msg);
json.put(\"time\", from.time);
//writer写进json中的字符串数据,末尾记得加换行符:\"\\n\",否则在客户机端无法识别
//因为BufferedReader.readLine()方法是根据换行符来读取一行的
writer.write(json.toString()+\"\\n\");
//调用flush()方法,刷新流缓冲,把消息推送到手机端
writer.flush();
System.out.println(\"推送消息成功:\"+from.msg+\">> to socketID:\"+from.to);
break;
}
}
//每推送一条消息之后,就要在消息队列中移除该消息
mMsgList.remove(0);
}
Thread.sleep(200);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
/**
* 定义一个SocketThread类,用于接收消息
*
*/
public class SocketThread extends Thread {
public int socketID;
public Socket socket;//Socket用于获取输入流、输出流
public BufferedWriter writer;//BufferedWriter 用于推送消息
public BufferedReader reader;//BufferedReader 用于接收消息
public SocketThread(Socket socket, int count) {
socketID = count;
this.socket = socket;
System.out.println(\"新增一台客户机,socketID:\"+socketID);
}
@Override
public void run() {
super.run();
try {
//初始化BufferedReader
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), \"utf-8\"));
//初始化BufferedWriter
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), \"utf-8\"));
//如果isStartServer=true,则说明SocketServer已经启动,
//现在需要用一个循环来不断接收来自客户机的消息,并作其他处理
while(isStartServer) {
//先判断reader是否已经准备好
if(reader.ready()) {
/*读取一行字符串,读取的内容来自于客户机
reader.readLine()方法是一个阻塞方法,
从调用这个方法开始,该线程会一直处于阻塞状态,
直到接收到新的消息,代码才会往下走*/
String data = reader.readLine();
//讲data作为json对象的内容,创建一个json对象
JSONObject json = new JSONObject(data);
//创建一个SocketMessage对象,用于接收json中的数据
SocketMessage msg = new SocketMessage();
msg.to = json.getInt(\"to\");
msg.msg = json.getString(\"msg\");
msg.from = socketID;
msg.time = getTime(System.currentTimeMillis());
//接收到一条消息后,将该消息添加到消息队列mMsgList
mMsgList.add(msg);
System.out.println(\"收到一条消息:\"+json.getString(\"msg\")+\" >>>> to socketID:\"+json.getInt(\"to\"));
}
//睡眠100ms,每100ms检测一次是否有接收到消息
Thread.sleep(100);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 获取指定格式的时间字符串,通过毫秒转换日期
* @param millTime
*/
private String getTime(long millTime) {
Date d = new Date(millTime);
SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");
return sdf.format(d);
}
public static void main(String[] args) {
MyServer server = new MyServer();
server.startSocket();
}
}
2.SocketClient工程
该工程是一个Android的工程,只有一个MainActivity.java和activity_main.xml文件,
先看一下activity_main.xml布局文件:
<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\"
tools:context=\".MainActivity\"
android:orientation=\"vertical\" >
<LinearLayout
android:layout_width=\"match_parent\"
android:layout_height=\"wrap_content\"
android:orientation=\"horizontal\">
<EditText
android:id=\"@+id/ip_edt\"
android:layout_width=\"0dp\"
android:layout_height=\"wrap_content\"
android:layout_weight=\"1\"
android:hint=\"ip\"
android:text=\"172.16.1.200\"/>
<EditText
android:id=\"@+id/port_edt\"
android:layout_width=\"0dp\"
android:layout_height=\"wrap_content\"
android:layout_weight=\"1\"
android:hint=\"port\"
android:text=\"2000\"/>
</LinearLayout>
<Button
android:id=\"@+id/start_btn\"
android:layout_width=\"match_parent\"
android:layout_height=\"wrap_content\"
android:text=\"start\"/>
<EditText
android:id=\"@+id/socket_id_edt\"
android:layout_width=\"match_parent\"
android:layout_height=\"wrap_content\"
android:hint=\"socketID\"/>
<EditText
android:id=\"@+id/msg_edt\"
android:layout_width=\"match_parent\"
android:layout_height=\"wrap_content\"
android:minLines=\"5\"
android:hint=\"content\"
android:gravity=\"top\"
/>
<Button
android:id=\"@+id/send_btn\"
android:layout_width=\"match_parent\"
android:layout_height=\"wrap_content\"
android:text=\"send\"/>
<TextView
android:id=\"@+id/console_txt\"
android:layout_width=\"match_parent\"
android:layout_height=\"0dp\"
android:layout_weight=\"1\"/>
<Button
android:id=\"@+id/clear_btn\"
android:layout_width=\"match_parent\"
android:layout_height=\"wrap_content\"
android:text=\"clear\"/>
</LinearLayout>
效果图:
MainActivity.java类:
package com.jimstin.socketclient;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.json.JSONObject;
import com.tencent.stat.MtaSDkException;
import com.tencent.stat.StatConfig;
import com.tencent.stat.StatService;
import android.R.integer;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.app.Activity;
public class MainActivity extends Activity implements OnClickListener {
private EditText mIPEdt, mPortEdt, mSocketIDEdt, mMessageEdt;
private static TextView mConsoleTxt;
private static StringBuffer mConsoleStr = new StringBuffer();
private Socket mSocket;
private boolean isStartRecieveMsg;
private SocketHandler mHandler;
protected BufferedReader mReader;//BufferedWriter 用于推送消息
protected BufferedWriter mWriter;//BufferedReader 用于接收消息
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mIPEdt = (EditText) findViewById(R.id.ip_edt);
mPortEdt = (EditText) findViewById(R.id.port_edt);
mSocketIDEdt = (EditText) findViewById(R.id.socket_id_edt);
mMessageEdt = (EditText) findViewById(R.id.msg_edt);
mConsoleTxt = (TextView) findViewById(R.id.console_txt);
findViewById(R.id.start_btn).setOnClickListener(this);
findViewById(R.id.send_btn).setOnClickListener(this);
findViewById(R.id.clear_btn).setOnClickListener(this);
mHandler = new SocketHandler();
}
/**
* 初始化socket
*/
private void initSocket() {
//新建一个线程,用于初始化socket和检测是否有接收到新的消息
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
String ip = mIPEdt.getText().toString();//IP
int port = Integer.parseInt(mPortEdt.getText().toString());//Socket
try {
isStartRecieveMsg = true;
mSocket = new Socket(ip, port);
mReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream(), \"utf-8\"));
mWriter = new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream(), \"utf-8\"));
while(isStartRecieveMsg) {
if(mReader.ready()) {
/*读取一行字符串,读取的内容来自于客户机
reader.readLine()方法是一个阻塞方法,
从调用这个方法开始,该线程会一直处于阻塞状态,
直到接收到新的消息,代码才会往下走*/
String data = mReader.readLine();
//handler发送消息,在handleMessage()方法中接收
mHandler.obtainMessage(0, data).sendToTarget();
}
Thread.sleep(200);
}
mWriter.close();
mReader.close();
mSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.send_btn:
send();
break;
case R.id.clear_btn:
mConsoleStr.delete(0, mConsoleStr.length());
mConsoleTxt.setText(mConsoleStr.toString());
break;
case R.id.start_btn:
if(!isStartRecieveMsg) {
initSocket();
}
break;
default:
break;
}
}
/**
* 发送
*/
private void send() {
new AsyncTask<String, Integer, String>() {
@Override
protected String doInBackground(String... params) {
sendMsg();
return null;
}
}.execute();
}
/**
* 发送消息
*/
protected void sendMsg() {
try {
String socketID = mSocketIDEdt.getText().toString().trim();
String msg = mMessageEdt.getText().toString().trim();
JSONObject json = new JSONObject();
json.put(\"to\", socketID);
json.put(\"msg\", msg);
mWriter.write(json.toString()+\"\\n\");
mWriter.flush();
mConsoleStr.append(\"我:\" +msg+\" \"+getTime(System.currentTimeMillis())+\"\\n\");
mConsoleTxt.setText(mConsoleStr);
} catch (Exception e) {
e.printStackTrace();
}
}
static class SocketHandler extends Handler {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
switch (msg.what) {
case 0:
try {
//将handler中发送过来的消息创建json对象
JSONObject json = new JSONObject((String)msg.obj);
mConsoleStr.append(json.getString(\"from\")+\":\" +json.getString(\"msg\")+\" \"+getTime(System.currentTimeMillis())+\"\\n\");
//将json数据显示在TextView中
mConsoleTxt.setText(mConsoleStr);
} catch (Exception e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}
@Override
public void onBackPressed() {
super.onBackPressed();
isStartRecieveMsg = false;
}
private static String getTime(long millTime) {
Date d = new Date(millTime);
SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");
return sdf.format(d);
}
}
以上代码的注释都比较详细,就不再多说了。
本文地址:https://www.stayed.cn/item/24915
转载请注明出处。
本站部分内容来源于网络,如侵犯到您的权益,请 联系我