簡單的Socket 編程 服務端和客戶端 (封裝)


 由於項目需求有推送的功能,技術總監又不想用第三方極光推送,所以在同事的幫助下搭建了簡單的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) {
		// 先設定RemoteViews
		RemoteViews 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是跳轉到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());
		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了   客戶端是封裝好了的,有斷線重連的過程。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章