客户端的连接需要准备的东西:gson-2.3.1.jar包用于json解析
先看整个工程目录,其次service需要在xml中注册
与服务器的连接类:
public class Connector {
//设置端口号和IP地址
protected static final StringdstName ="192.168.3.40";
protected static final int dstPort =7896;
private SocketmSocket;
public Connector(){
}
//-单例-----------------------------------------------
private static Connectorinstance;
public static Connector getInstance() {
if (instance ==null) {
synchronized (Connector.class) {
if (instance ==null) {
instance =new Connector();
}
}
}
return instance;
}
//-监听-----------------------------------
protected ConnectorListenermListener;
public void setConnectorListener(ConnectorListener listener) {
this.mListener = listener;
}
public interface ConnectorListener {
void pushData(String data);
}
public void connect(){
//在这里发送数据和接收数据是两个单独的线程
try {
if (mSocket ==null ||mSocket.isClosed()) {
//如果Socket对象为空则新建一个,并给定端口号和IP地址
mSocket =new Socket(dstName,dstPort);
}
//发送数据
new Thread(new RequestWorker()).start();
//接受数据
new Thread(new ReceiveWorker()).start();
}catch (UnknownHostException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
}
//-一般使用这个方法----------------------------------------------------
public void connect(AuthRequest auth){
connect();//调用连接线程
putRequest(auth);//发送认证信息
}
//消息队列
private ArrayBlockingQueuequeue =new ArrayBlockingQueue(8);
//发送的线程,只要消息队列中有数据就发送出去
private class RequestWorkerimplements Runnable{
@Override
public void run() {
OutputStream out =null;
try {
out =mSocket.getOutputStream();
while(true){
String content =queue.take();
out.write(content.getBytes());
}
}catch (IOException e) {
e.printStackTrace();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void putRequest(String content){
try {
queue.put(content);//将消息丢到消息队列里面
}catch (InterruptedException e) {
e.printStackTrace();
}
}
public void putRequest(Request request) {
//将需要发送的消息放到消息队列,发送的线程会自动监测,一旦有消息进来我们就将它发出去
putRequest(request.getData());
}
//接收数据的线程
private class ReceiveWorkerimplements Runnable{
@Override
public void run() {
try {
InputStream inputStream =mSocket.getInputStream();
byte[] buffer =new byte[1024];
int len = -1;
while((len = inputStream.read(buffer))!=-1){
//一旦有消息进来我们就把消息放到队列中,在广播出去
String text =new String(buffer,0, len);
if (mListener !=null) {
mListener.pushData(text);
}
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
//销毁连接
public void disConn(){
try {
if (mSocket !=null && !mSocket.isClosed()) {
mSocket.close();
mSocket =null;
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
然后是连接管理类:
public class ConnectorManager implements Connector.ConnectorListener {
//连接器
private Connectorconnector;
private ConnectorListenermListener;
private static ConnectorManagerinstance;
private ConnectorManager() { }
public static ConnectorManager getInstance() {
if (instance ==null) {
synchronized (ConnectorManager.class) {
if (instance ==null) {
instance =new ConnectorManager();
}
}
}
return instance;
}
/**
* 创建连接发送验证
* @param auth
*/
public void connnect(String auth) {
connector =new Connector();
connector.setConnectorListener(this);
connector.connect();
// connector.auth(auth);
}
public void connect(AuthRequest auth) {
connector =new Connector();
connector.setConnectorListener(this);
connector.connect();
// connector.auth(auth.getData());
}
public void putRequest(String request) {
connector.putRequest(request);
}
public void putRequest(Request request) {
connector.putRequest(request.getData());
}
@Override
public void pushData(String data) {
if (mListener !=null) {
mListener.pushData(data);
}
}
public void setConnectorListener(ConnectorListener listener) {
this.mListener = listener;
}
public interface ConnectorListener {
void pushData(String data);
}
服务类
/**
* 继承了Service,实现了Connector中的接口ConnectorListener
*
* 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的Android程序员如果连Service都没听说过的话,那确实也太逊了。
* Service作为Android四大组件之一,在每一个应用程序中都扮演着非常重要的角色。
* 它主要用于在后台处理一些耗时的逻辑,或者去执行某些需要长期运行的任务。
* 必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态。
* Created by Administrator on 2018/1/9.
*/
public class CoreService extends Service implements Connector.ConnectorListener {
private Connectorconnector;//连接器主要用来连接服务器
private ExecutorServicemPools;//线程池
@Override
public IBinder onBind(Intent intent) {
return null;//Client和Server之间的进程间通信通过Binder驱动程序间接实现.不懂的可以百度,这里不做过多讲解
}
@Override
public void onCreate() {
super.onCreate();
/*通过Connector.getInstance()我们可以的到一个连接器,单例模式,相信每个程序员都应该了解这个设计模式,
这样就可以每次得到同一个对象,在我们进行长连接时就避免了每次去新建一个实例化对象*/
connector = Connector.getInstance();
//监听器,主要用来监听从服务器接收到的数据
connector.setConnectorListener(this);
//设置线程池的初始化大小为3
mPools = Executors.newFixedThreadPool(3);
//用线程池去开启线程,这块不做详细解释,
mPools.execute(new Runnable() {
@Override
public void run() {
//每次将认证消息赋空,防止获取延迟导致的错误
AuthRequest request =null;
//在我们自己开发应用过程中,常常使用如下的代码形式判断运行新API还是旧的API:
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {
// 包含新API的代码块
request =new AuthRequest("B","B");//AuthRequest将信息以键值对的形式存起来
}else {
// 包含旧的API的代码块
request =new AuthRequest("A","A");
}
connector.connect(request);//重点:连接服务器在这里调用,这样解耦了客户端的连接
}
});
}
@Override
public void pushData(String data) {
Intent intent =new Intent();
intent.setAction(PushReceiver.ACTION_TEXT);
intent.putExtra(PushReceiver.DATA_KEY, data);
//使用广播的形式把数据发送到主界面进行处理
sendBroadcast(intent);
}
}
广播类------------------------------------------------------------------
//广播接收器,继承了BroadcastReceiver
/**
* 知识补充:一来为了自己能更深刻的理解广播的机制,二是为了方便新手学习
*Android中的广播使用了设计模式中的观察者模式:基于消息的发布 / 订阅事件模型
*因此,Android将广播的发送者 和 接收者 解耦,使得系统方便集成,更易扩展
*
* 自定义广播接收者BroadcastReceiver
*
* 继承BroadcastReceivre基类
必须复写抽象方法onReceive()方法
广播接收器接收到相应广播后,会自动回调 onReceive() 方法
一般情况下,onReceive方法会涉及 与 其他组件之间的交互,如发送Notification、启动Service等
默认情况下,广播接收器运行在 UI 线程,因此,onReceive()方法不能执行耗时操作,否则将导致ANR
*/
public abstract class PushReceiverextends BroadcastReceiver {
public static final StringACTION_TEXT ="com.android.action.text";
public static final StringDATA_KEY ="data";
}
消息接收与发送的类-------------------------------------------------------------------------------------
public interface Request {
String getData();
}
public class TextRequest implements Request {
private Mapmap =new HashMap();
public TextRequest(String sender, String token, String receiver,
String content) {
map.put("type","request");
map.put("sequence", UUID.randomUUID().toString());
map.put("action","text");
map.put("sender", sender);
map.put("token", token);
map.put("receiver", receiver);
map.put("content", content);
}
@Override
public String getData() {
return new Gson().toJson(map);
}
}
public class AuthRequest implements Request {
private Mapmap =new HashMap();
public AuthRequest(String sender, String token) {
map.put("type","request");
map.put("sequence", UUID.randomUUID().toString());
map.put("action","auth");
map.put("sender", sender);
map.put("token", token);
}
@Override
public String getData() {
return new Gson().toJson(map);//将map转成JSON数据
}
}
客户端Activity代码-----------------------------------------------------------------
public class ClientActivity extends Activity {
private EditTextmEtContent;//绑定控件主要为了获取输入框的内容
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEtContent = findViewById(R.id.edit_query);
//开启服务需要在app/src/main/AndroidManifest.xml中进行注册,启动Service的方法和启动Activity很类似,都需要借助Intent来实现
startService(new Intent(this, CoreService.class));
}
/**
* 发送消息:
* @param view
*/
public void sendMsg(View view){
String content =mEtContent.getText().toString();
if (TextUtils.isEmpty(content)) {
return;
}
String sender =null;
String token =null;
String receiver =null;
//在我们自己开发应用过程中,常常使用如下的代码形式判断运行新API还是旧的API:
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR2) {
// 包含新的API的代码块
sender ="B";
receiver ="A";
token ="B";
}else {
// 包含旧的API的代码块
sender ="A";
token ="A";
receiver ="B";
}
Request request =new TextRequest(sender, token, receiver, content);
Connector.getInstance().putRequest(request);
}
/* 这里多说两句查阅api文档后自己对于Intent的一些理解和实践
程序的3个核心组件——Activity、services、广播接收器——是通过intent传递消息的。
intent消息对于运行时绑定不同的组件是很方便的,这些组件可以是同一个程序也可以是不同的。
一个intent对象,是一个被动的数据结构,它保存了一个操作的抽象描述——或通常是一个广播的实例,一些发生的事情的描述,一个通知。
传递intent到不同组件的机制是互不相同的。
Android系统会寻找合适的Activity、service或设置广播接收器来响应intent,在需要的时候实例化它们。
在消息系统里没有交叠:广播intent仅仅分派给广播接收器,不会分派给Activity或service。一个intent分派给startActivity()仅仅分派给Activity,不会分派给service或广播接收器,等等。
在这里我们用Intent去告诉广播群我们接收到了数据,一旦对应的Intent接收到了数据,广播就会告诉绑定了广播的Activity去更新UI
*/
@Override
protected void onResume() {
super.onResume();
if (mReceiver !=null) {
//IntentFilter会告诉Android系统我要广播intent仅仅分派给广播接收器
// 设置接收广播的类型为PushReceiver.ACTION_TEXT
IntentFilter filter =new IntentFilter(PushReceiver.ACTION_TEXT);
//动态注册:调用Context的registerReceiver()方法
registerReceiver(mReceiver, filter);
}
}
private PushReceivermReceiver =new PushReceiver() {
/* 实例化了广播接收器
实现抽象类PushReceiver,重写了onReceive的方法
复写onReceive()方法
接收到广播后,则自动调用该方法
*/
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (PushReceiver.ACTION_TEXT.equals(action)) {
//接收到对应的广播后执行的操作
String result = intent.getStringExtra(PushReceiver.DATA_KEY);
System.out.println(result);
System.out.println(" ");
Toast.makeText(getApplicationContext(), result,
Toast.LENGTH_SHORT).show();
}
}
};
// 注册广播后,要在相应位置记得销毁广播
// 即在onDestroy()中unregisterReceiver(mBroadcastReceiver)
// 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中
// 当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。
@Override
protected void onDestroy() {
super.onDestroy();
if (mReceiver !=null) {
unregisterReceiver(mReceiver);
}
}
}
服务器端-----------------------------------------------------------------------------------
//服务器端
@SuppressWarnings("all")
public class TcpServer {
public static void main(String[] args) {
//final LinkedList list = new LinkedList();
final Map map =new HashMap();
int port =7896;
try {
ServerSocket server =new ServerSocket(port);
while (true) {
// 获得客户端连接
// 阻塞式方法
System.out.println("准备阻塞...");
final Socket client = server.accept();
System.out.println("阻塞完成...");
// 添加到集合里
//list.add(client);
new Thread(new Runnable() {
@Override
public void run() {
try {
// 输入流,为了获取客户端发送的数据
InputStream is =client.getInputStream();
// 得到输出流
OutputStream out =client.getOutputStream();
byte[] buffer =new byte[1024];
int len = -1;
System.out.println("准备read...");
while ((len = is.read(buffer)) != -1) {
String text =new String(buffer,0, len);
if (text.startsWith("#")) {
map.put(text,client);
// 回复认证信息
out.write("认证成功,over".getBytes());
}else {
out.write("发送成功,over".getBytes());
System.out.println(text);
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
结果