简单的Socket 编程 服务端和客户端 (封装)

时间:2021-08-19 22:16:54


 由于项目需求有推送的功能,技术总监又不想用第三方极光推送,所以在同事的帮助下搭建了简单的socket通讯。


 首先服务端  java搭建


AndriodService.java


package com.android.net;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class AndroidService {

// // 定义保存所有的Socket
// public static List<Socket> socketList = new ArrayList<Socket>();
//
// public static void main(String[] args) throws IOException {
// ServerSocket server = new ServerSocket(3000);
// while (true) {
// Socket s = server.accept();
// socketList.add(s);
// // 每当客户端连接之后启动一条ServerThread线程为该客户端服务
// new Thread(new ServiceThreada(s)).start();
//
// }
// }

public static void main(String[] args) throws IOException {
ServerSocket serivce = new ServerSocket(3000);
while (true) {
// 等待客户端连接
Socket socket = serivce.accept();
String ip = socket.getInetAddress().toString();
System.out.println(System.currentTimeMillis() + "用户IP:" + ip
+ " 链接了 ");
new Thread(new AndroidRunable(socket)).start();
}
}

}


AndroidRunable .java


package com.android.net;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

public class AndroidRunable implements Runnable {

Socket socket = null;

public AndroidRunable(Socket socket) {
this.socket = socket;
}

public void run() {
// 向android客户端输出hello worild
String line = null;
InputStream input;
OutputStream output;
String str = "hello world!";
try {
// 向客户端发送信息
output = socket.getOutputStream();
input = socket.getInputStream();
// BufferedReader bff = new BufferedReader(
// new InputStreamReader(input));
// output.write(str.getBytes("gbk"));
// output.flush();
//半关闭socket
// socket.shutdownOutput();
// 获取客户端的信息
System.out.println(Thread.class.getName());
ObjectOutputStream oos=new ObjectOutputStream(output);
ObjectInputStream ois = new ObjectInputStream(input);
while (true) {
Object object = ois.readObject();
System.out.println(object);
if (object instanceof byte[]) {
byte[] buff = (byte[]) object;
System.out.print(new String(buff, "gbk"));
}
oos.writeObject("收到消息");
}

// 关闭输入输出流
// output.close();
// bff.close();
// input.close();
// socket.close();

} catch (Exception e) {
e.printStackTrace();
}
}
}


   因为推送打开服务端要一直保持在线,所以就不关了,反正项目中的服务端不是你搭,关键在客户端是否正常。



 客户端 Andriod项目


首先我要知道客户端收到服务端信息是什么类型,其次要怎么办,怎么执行。


我这个项目是 服务端发送过来一个json  客户端解析得到要到数据,以广播的形式发送通知。


上代码



import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Application;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
/**
* socket 封装类
* 客户端读流线程
* ClientReadThead clientReadThead;
* 客户端流线程
* ClientWriteThread clientWriteThread;
* @author onebot
*
*/
public class ClientSocket {
ClientReadThead clientReadThead;
ClientWriteThread clientWriteThread;
private static ClientSocket reClient;

private ClientSocket() {
// TODO Auto-generated constructor stub
}

private ClientSocket(Application context) {
// TODO Auto-generated constructor stub
new MyThread(context).start();
}

public static ClientSocket reSocket(Application context) {
if (reClient != null) {
reClient.close();
reClient = null;
}
if (reClient == null) {
synchronized (ClientSocket.class.getName()) {
if (reClient == null) {
reClient = new ClientSocket(context);
}
}
}
return reClient;
}

class MyThread extends Thread {
Application context;

public MyThread(Application context) {
this.context = context;
}

@Override
public void run() {
// 定义消息
Message msg = new Message();
msg.what = 0x11;
Bundle bundle = new Bundle();
bundle.clear();
try {
// 连接服务器 并设置连接超时为5秒
Socket socket = new Socket();
socket.connect(new InetSocketAddress("服务端地址",端口号),
5000);
clientWriteThread = new ClientWriteThread(socket);
clientWriteThread.start();
clientReadThead = new ClientReadThead(socket, context);
clientReadThead.start();
timers();

} catch (Exception e) {
bundle.putString("msg", "连接异常,再次请求连接");
msg.setData(bundle);
e.printStackTrace();
// 发送消息 修改UI线程中的组件
try {
sleep(5000);
reSocket(context);
} catch (Exception e2) {
// TODO: handle exception
}
}
}
}

private void timers() {
Timer timer = new Timer();
timer.schedule(new TimerTasks(), 1000 * 60, 5 * 1000 * 60);

// 获得闹钟管理器
// AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
// 设置任务执行计划
// am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000 * 60, 5 *
// 1000 * 60, sender);//从firstTime才开始执行,每隔5秒再执行
}

class TimerTasks extends TimerTask {

@Override
public void run() {
// TODO Auto-generated method stub
// 位置

}

}

//客户端写消息给服务器(位置消息)
public void writeMessage(Object ob) {
if (clientWriteThread != null) {
clientWriteThread.writeMessage(ob);
} else {
throw new RuntimeException("you need transfer method reSocket");
}
}


private void close() {
// TODO Auto-generated method stub
if (clientWriteThread != null) {
clientWriteThread.close();
clientWriteThread = null;
}
if (clientReadThead != null) {
clientReadThead.close();
clientReadThead = null;
}
System.gc();
}

//
public static void closeGc() {
reClient.close();
reClient = null;
System.gc();
}
}


ClientReadThead.java




import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.net.Socket;

import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;

/**
* 客户端读流线程
* @author onebot
*
*/
public class ClientReadThead extends Thread {
private Socket socket;
private ObjectInputStream ois;
private boolean tag = true;
private int len = -1;
private byte[] b;
public Application context;

public ClientReadThead(Socket socket, Application context) {
// TODO Auto-generated constructor stub
try {
this.context = context;
this.socket = socket;
this.ois = new ObjectInputStream(socket.getInputStream());
b = new byte[1024 * 1024 * 5];
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
tag = false;
}
}

@Override
public void run() {
// TODO Auto-generated method stub
super.run();
try {
while (tag) {
synchronized (this) {
Object ob = ois.readObject();

dealMessage(ob);
}

}
close();
} catch (Exception e) {
// TODO: handle exception
tag = false;
ClientSocket.reSocket(context);

e.printStackTrace();
}
}

private void dealMessage(Object ob) {

// TODO Auto-generated method stub
// 广播
Intent intent = new Intent();
intent.setAction("com.box");
Log.e("=========", "走没走");
if (ob instanceof String) {
intent.putExtra("msg", ob.toString());
} else if (ob instanceof Serializable) {
Serializable s = (Serializable) ob;
intent.putExtra("msg", s);
}
context.sendOrderedBroadcast(intent, null); // 有序广播发送
// Toast.makeText(context, "发送广播成功", Toast.LENGTH_SHORT).show();

System.out.println(ob);
}

public void close() {
try {
ois.close();
ois = null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


ClientWriteThread .java



import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;

/**
* 客户端写流线程
*/
public class ClientWriteThread extends Thread {
private boolean tag = true;
private final static String TAG = "com.xiao.hei.Thread.WriteThread";
private Object o;
private Socket socket;
private ObjectOutputStream os;

public ClientWriteThread(Socket socket) {
// TODO Auto-generated constructor stub
try {
this.socket = socket;
this.os = new ObjectOutputStream(socket.getOutputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
tag = false;
}
}

@Override
public void run() {
// TODO Auto-generated method stub
super.run();
try {
while (tag) {
synchronized (this) {
if (o == null)
wait();
else {
os.writeObject(o);
o = null;
}
}

}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}

synchronized public void writeMessage(Object o) {
System.out.println(o);
this.o = o;
notify();
}

public synchronized void close() {
try {
notify();
tag = false;
os.close();
socket.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}


广播 SocketReceiver.java




import java.io.Serializable;

import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Builder;
import android.util.Log;
import android.widget.RemoteViews;

public class ClientReceiver extends BroadcastReceiver {
public static final String LOGIN_BOX = "com.box";
public static final String LOGIN_USER = "";
public static final String LOGIN_LOACTION = "";
public static final String MSG_SERVICE = "";
/** Notification构造器 */
NotificationCompat.Builder mBuilder;

/** Notification的ID */
int notifyId = 100;

/** Notification管理 */
public NotificationManager mNotificationManager;
public Context context;
public PushBean push;
public String MsgContent;
public int MsgType;
public String MsgImgpath;

@SuppressLint("NewApi")
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
// String action = intent.getAction();
System.out.println("接受到广播");
this.context = context;
initNotify();
mNotificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);// 获取系统通知的服务

push = new PushBean();
Object msg = intent.getExtras().get("msg");
Log.e(ClientReceiver.class.getName(), msg.toString());
if (msg instanceof String) {
deal_string(msg.toString());
} else if (msg instanceof Serializable) {
deal_serializable((Serializable) msg);
}
}

private void deal_serializable(Serializable serializable) {
// showIntentActivityNotify(serializable.toString());
shwoNotify(serializable.toString());
System.out.println("11111");
}

// / {"type":1,"msg":{}} type 类型 0 1 2 3 4 5 6
private void deal_string(String string) {
//try {
//JSONObject jo = new JSONObject(string);
//PushBean push = new PushBean();
//push.setContent(jo.getString("content"));
//
//push.setType(jo.getInt("type"));
//push.setImg_path(jo.getString("img"));
//
//
//MsgContent = push.getContent();
//MsgType=push.getType();
//MsgImgpath=push.getImg_path();
//
//if(MsgType==1){
////类型1的通知
//
//}
//if(MsgType==2){
////发送登录后的类型2的通知
//
//}
//if(MsgType==3){
////发送附件有网吧的类型3的通知
//
//{
//

shwoNotify(string);
}


//} catch (JSONException e) {
//// TODO Auto-generated catch block
//e.printStackTrace();
//}
//

// showIntentActivityNotify(string);
//}

public PendingIntent getDefalutIntent(int flags) {
PendingIntent pendingIntent = PendingIntent.getActivity(context, 1,
new Intent(), flags);
return pendingIntent;
}

public void showIntentActivityNotify(String text) {
// Notification.FLAG_ONGOING_EVENT --设置常驻
// Flag;Notification.FLAG_AUTO_CANCEL 通知栏上点击此通知后自动清除此通知
// notification.flags = Notification.FLAG_AUTO_CANCEL;
// //在通知栏上点击此通知后自动清除此通知
try {
// mBuilder = new NotificationCompat.Builder(context);
mBuilder.setAutoCancel(true)
// 点击后让通知将消失
.setContentTitle("奇葩葩").setContentText(text)
.setSmallIcon(R.drawable.ico_qpp).setTicker("有新的更新");
// 点击的意图ACTION是跳转到Intent
Intent resultIntent = new Intent(context, MainActivity.class);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pendingIntent);
mNotificationManager.notify(notifyId, mBuilder.build());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/**
* 初始化通知栏
* */
private void initNotify() {
mBuilder = new NotificationCompat.Builder(context);
mBuilder.setContentTitle("<span style="font-family: Arial, Helvetica, sans-serif;">QPP</span>")
.setContentText("<span style="font-family: Arial, Helvetica, sans-serif;">QPP</span>有新的更新")
.setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL))// .setNumber(number)//显示数量.setTicker("测试通知来啦")// 通知首次出现在通知栏,带上升动画效果的.setWhen(System.currentTimeMillis())// 通知产生的时间,会在通知信息里显示.setPriority(Notification.PRIORITY_DEFAULT)// 设置该通知优先级.setAutoCancel(true)// 设置这个标志当用户单击面板就可以让通知将自动取消.setOngoing(false)// ture,设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接).setDefaults(Notification.DEFAULT_VIBRATE)// 向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合:// Notification.DEFAULT_ALL Notification.DEFAULT_SOUND 添加声音 //// requires VIBRATE permission.setSmallIcon(R.drawable.ic_launcher);}public void shwoNotify(Object ob) {// 先设定RemoteViewsRemoteViews view_custom = new RemoteViews(context.getPackageName(),R.layout.view_custom);// 设置对应IMAGEVIEW的ID的资源图片view_custom.setImageViewResource(R.id.custom_icon, R.drawable.ico_qpp);// view_custom.setInt(R.id.custom_icon,"setBackgroundResource",R.drawable.icon);view_custom.setTextViewText(R.id.tv_custom_title, "QPP");view_custom.setTextViewText(R.id.tv_custom_content,"附近的网咖" + ob.toString());// view_custom.setTextViewText(R.id.tv_custom_time,// String.valueOf(System.currentTimeMillis()));// 设置显示// view_custom.setViewVisibility(R.id.tv_custom_time, View.VISIBLE);// view_custom.setLong(R.id.tv_custom_time,"setTime",// System.currentTimeMillis());//不知道为啥会报错,过会看看日志// 设置number// NumberFormat num = NumberFormat.getIntegerInstance();// view_custom.setTextViewText(R.id.tv_custom_num,// num.format(this.number));mBuilder = new Builder(context);mBuilder.setContent(view_custom).setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL)).setWhen(System.currentTimeMillis())// 通知产生的时间,会在通知信息里显示.setTicker("有新的消息 ").setPriority(Notification.PRIORITY_DEFAULT)// 设置该通知优先级.setAutoCancel(true).setOngoing(false)// 不是正在进行的 true为正在进行// 效果和.flag一样.setSmallIcon(R.drawable.ico_qpp);// 点击的意图ACTION是跳转到IntentIntent resultIntent = new Intent(context, MainActivity.class);resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);mBuilder.setContentIntent(pendingIntent);// mNotificationManager.notify(notifyId, mBuilder.build());Notification notify = mBuilder.build();notify.contentView = view_custom;// Notification notify = new Notification();// notify.icon = R.drawable.icon;// notify.contentView = view_custom;// notify.contentIntent =// getDefalutIntent(Notification.FLAG_AUTO_CANCEL);mNotificationManager.notify(notifyId, notify);}}


 初始化 BaseApplication 



import android.app.Application;

public class BaseApplication extends Application{

public static ClientSocket clientSocket;
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
<span style="white-space:pre"></span>//初始化clientSocket = ClientSocket.reSocket(this);}}




  清单文件的配置  注册广播  打开网络权限


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.umeng.message.example"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="22" />

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

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:name="com.umeng.message.example.BaseApplication"
android:theme="@style/AppTheme" >
<activity
android:name="com.umeng.message.example.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>




<receiver
android:name="com.umeng.message.example.ClientReceiver"
>
<intent-filter android:priority="1000"
>
<action android:name="com.box"/>
</intent-filter>
</receiver>

</application>

</manifest>


 在app中使用调用   


 如果需要客户端登录后发送消息给服务端 服务端才开启对用户的推送 那么


BaseActivity



import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;

public class BaseActivity extends Activity {
/** Notification管理 */
public NotificationManager mNotificationManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
initService();
}

/**
* 初始化要用到的系统服务
*/
private void initService() {
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}

/**
* 清除当前创建的通知栏
*/
public void clearNotify(int notifyId){
mNotificationManager.cancel(notifyId);//删除一个特定的通知ID对应的通知
//mNotification.cancel(getResources().getString(R.string.app_name));
}

/**
* 清除所有通知栏
* */
public void clearAllNotify() {
mNotificationManager.cancelAll();// 删除你发的所有通知
}

/**
* @获取默认的pendingIntent,为了防止2.3及以下版本报错
* @flags属性:
* 在顶部常驻:Notification.FLAG_ONGOING_EVENT
* 点击去除: Notification.FLAG_AUTO_CANCEL
*/
public PendingIntent getDefalutIntent(int flags){
PendingIntent pendingIntent= PendingIntent.getActivity(this, 1, new Intent(), flags);
return pendingIntent;
}
}


MainActivity 



import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import android.app.AlarmManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Builder;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RemoteViews;
import android.widget.TextView;
import encryption.stringUtil.Util;
import encryption.util.NumUtil;

public class MainActivity extends BaseActivity {
/** Notification构造器 */
NotificationCompat.Builder mBuilder;

/** Notification的ID */
int notifyId = 100;

String buffer = "";
TextView txt1;
Button send;
EditText ed1;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initNotify();
txt1 = (TextView) findViewById(R.id.txt1);
send = (Button) findViewById(R.id.send);
ed1 = (EditText) findViewById(R.id.ed1);
send.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//if (clientWriteThread != null)
//clientWriteThread.writeMessage("================");
//else
//System.err.println("null============ ");//发送消息到服务端
BaseApplication.clientSocket.writeMessage("================");


}
});

}
<span style="white-space:pre"></span>//调用的方法
public void ClientWriteMsg(Object ob){
BaseApplication.clientSocket.writeMessage(ob);
}


}

 

 友情提示代码中的通知 请初始化再用。


 代码都在上面 就不发demo了   客户端是封装好了的,有断线重连的过程。