客戶端的連接需要準備的東西: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();
}
}
}
結果