描述:實時在客戶端上獲取到哪些款須要補貨.java
要求: 後臺須要使用c#,而且哪些須要補貨的邏輯寫在公司框架內,客戶端採用PDA(即Android客戶端 版本4.4) . 用戶打開了補貨通知頁面時不須要在通知欄推送 若是不在時須要通知欄推送消息.android
思路圖解釋:正則表達式
想法是啓動服務時候建立TcpListener監聽指定的Ip端口建立接收鏈接的線程和接收數據的回調因爲 定義的存儲方式(Dictionary<string, ClientSocketManager> Clients = new Dictionary<string, ClientSocketManager>();)因此採用了先由客戶端創建鏈接而後服務端收到鏈接請求時當即發送一條數據給客戶端要求客戶端發送用戶簽名給服務端以便服務端能夠根據用戶Id之類的數據去發送給指定的用戶 設置的數據格式爲/*star|狀態,user標識|end*/真實數據c#
客戶端登陸時候和服務端創建鏈接在任意界面均可以接收到通知打開通知時候進入展現數據的頁面服務器
1 protected override void OnStart(string[] args) 2 { 3 if (IsListened) return; 4 //啓動監聽 5 IPAddress ip = IPAddress.Parse(ServiceSetting._IPAddress); 6 IPEndPoint point = new IPEndPoint(ip, ServiceSetting._IPEndPoint); 7 listener = new TcpListener(point); 8 try 9 { 10 listener.Start(100); 11 } 12 catch (Exception ex) 13 { 14 LogHelper.Log(ex); 15 return; 16 } 17 IsListened = true; 18 //接受鏈接請求的異步調用 19 AsyncCallback callback = new AsyncCallback(AcceptCallBack); 20 listener.BeginAcceptSocket(callback, listener); 21 LogHelper.Write("服務已啓動……", LogLev.Info); 22 }
1 private void AcceptCallBack(IAsyncResult ar) 2 { 3 try 4 { 5 //完成異步接收鏈接請求的異步調用 6 Socket handle = listener.EndAcceptSocket(ar); 7 ClientSocketManager manager = new ClientSocketManager(handle); 8 9 AsyncCallback callback; 10 //繼續調用異步方法接收鏈接請求 11 if (IsListened) 12 { 13 callback = new AsyncCallback(AcceptCallBack); 14 listener.BeginAcceptSocket(callback, listener); 15 } 16 //開始在鏈接上進行異步的數據接收 17 manager.ClearBuffer(); 18 callback = new AsyncCallback(ReceiveCallback); 19 manager.socket.BeginReceive(manager.Rcvbuffer, 0, manager.Rcvbuffer.Length, SocketFlags.None, callback, manager); 20 } 21 catch (Exception ex) 22 { 23 //在調用EndAcceptSocket方法時可能引起異常 24 //套接字Listener被關閉,則設置爲未啓動偵聽狀態 25 IsListened = false; 26 LogHelper.Log(ex); 27 return; 28 } 29 }
1 private void ReceiveCallback(IAsyncResult ar) 2 { 3 ClientSocketManager manager = (ClientSocketManager)ar.AsyncState; 4 try 5 { 6 int i = manager.socket.EndReceive(ar); 7 if (i == 0) 8 { 9 RemoveOneClientbyManager(manager); 10 return; 11 } 12 else 13 { 14 string data = Encoding.UTF8.GetString(manager.Rcvbuffer, 0, i); 15 //manager.socket.RemoteEndPoint.ToString() 獲取客戶端IP 16 manager.ClearBuffer(); 17 //根據傳入數據處理用戶數據 設置數據格式爲/*star|狀態,user標識|end*/真實數據 18 //匹配中間的狀態正則表達式 ^(/[*]star\|).*(?=\|end[*]/) 19 //首次傳入保存鏈接信息 20 //打開通知時接收到回調 21 string packgeHead = Regex.Match(data, @"^(/[*]star\|).*(?=\|end[*]/)").ToString().Replace("/*star|", ""); 22 if (string.IsNullOrEmpty(packgeHead)) 23 { 24 LogHelper.Write("客戶端沒有發送指定的數據頭", LogLev.Warn); 25 return; 26 } 27 string[] heads = packgeHead.Split(','); 28 switch (heads[0]) 29 { 30 case "first":// 首次傳入保存鏈接信息 31 lock (lockObj) 32 { 33 Clients.Add(heads[1], manager); 34 } 35 //ToRemove 36 SendToClient(new List<string>() { "aaa" }, "您有一條新的通知"); 37 //endRemove 38 break; 39 case "data"://用戶打開了頁面能夠發送數據 40 ReceiveThenSend(manager); 41 break; 42 default: 43 LogHelper.Write("客戶端發送的數據頭有問題", LogLev.Warn); 44 break; 45 } 46 AsyncCallback callback = new AsyncCallback(ReceiveCallback); 47 manager.socket.BeginReceive(manager.Rcvbuffer, 0, manager.Rcvbuffer.Length, SocketFlags.None, callback, manager); 48 } 49 } 50 catch (Exception ex) 51 { 52 RemoveOneClientbyManager(manager); 53 LogHelper.Log(ex); 54 return; 55 } 56 }
1 private void SendData(ClientSocketManager manager, string data) 2 { 3 try 4 { 5 byte[] msg = Encoding.UTF8.GetBytes(data); 6 AsyncCallback callback = new AsyncCallback(new Action<IAsyncResult>(ar => 7 { 8 ClientSocketManager frd = (ClientSocketManager)ar.AsyncState; 9 try 10 { 11 frd.socket.EndSend(ar); 12 } 13 catch (Exception ex) 14 { 15 RemoveOneClientbyManager(manager); 16 LogHelper.Log(ex); 17 return; 18 } 19 })); 20 manager.socket.BeginSend(msg, 0, msg.Length, SocketFlags.None, callback, manager); 21 } 22 catch (Exception ex) 23 { 24 RemoveOneClientbyManager(manager); 25 LogHelper.Log(ex); 26 return; 27 } 28 }
先受權app
1 <uses-permission android:name="android.permission.INTERNET" /> 2 <uses-permission android:name="android.permission.GET_TASKS" />
在Application中插入代碼 (ps:文件名因爲開始打算建的是父級的Activity後來實驗不行就沒有更換名稱)框架
1 package com.example.sockettestclient; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.OutputStream; 6 import java.io.UnsupportedEncodingException; 7 import java.net.Socket; 8 import java.net.UnknownHostException; 9 10 import android.app.ActivityManager; 11 import android.app.Application; 12 import android.app.NotificationManager; 13 import android.app.PendingIntent; 14 import android.content.Context; 15 import android.content.Intent; 16 import android.net.NetworkInfo.State; 17 import android.os.Bundle; 18 import android.os.Handler; 19 import android.os.Message; 20 import android.support.v4.app.NotificationCompat; 21 import android.widget.EditText; 22 import android.widget.TextView; 23 24 public class BaseActivity extends Application{ 25 @Override 26 public void onCreate(){ 27 super.onCreate(); 28 } 29 //NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 30 //發送通知 31 protected void InitNotificationManager(String Title,String Content) { 32 NotificationCompat.Builder builder = new NotificationCompat.Builder(this); 33 Intent intent = new Intent(this, TestGetSocketRes.class);//將要跳轉的界面 34 builder.setAutoCancel(true);//點擊後消失 35 builder.setSmallIcon(R.drawable.info);//設置通知欄消息標題的頭像 36 builder.setDefaults(NotificationCompat.DEFAULT_SOUND);//設置通知鈴聲 37 builder.setTicker(Title); 38 builder.setContentText(Content);//通知內容 39 builder.setContentTitle(Title); 40 //添加參數 41 Bundle bundle = new Bundle(); 42 bundle.putString("title", Title); 43 bundle.putString("Content", Content); 44 intent.putExtras(bundle); 45 //利用PendingIntent來包裝咱們的intent對象,使其延遲跳轉 46 PendingIntent intentPend = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); 47 builder.setContentIntent(intentPend); 48 NotificationManager manager = (NotificationManager) this.getSystemService(this.NOTIFICATION_SERVICE); 49 manager.notify(0, builder.build()); 50 } 51 protected boolean isConnect = false; 52 protected static final String ServerIP = "192.168.47.102"; 53 protected static final int ServerPort = 4567; 54 protected Socket socket = null; 55 protected ReceiveThread receiveThread = null; 56 protected boolean isReceive = false; 57 protected Handler SocketUIHandler = null; 58 protected OutputStream outStream; 59 protected String strMessage; 60 protected TextView textReceive = null; 61 protected EditText textSend = null; 62 private boolean IsInPage=false; 63 private String UserID="aaa"; 64 public enum PostState { 65 first, data 66 } 67 public void SetUserInfo() { 68 UserID="aaa"; 69 } 70 public String GetSocketPostDataHeadInfo(PostState state) { 71 return "/*star|"+state.toString()+","+UserID+"|end*/"; 72 } 73 Runnable connectThread = new Runnable() { 74 75 @Override 76 public void run() { 77 // TODO Auto-generated method stub 78 try { 79 //初始化Scoket,鏈接到服務器 80 socket = new Socket(ServerIP, ServerPort); 81 isConnect = true; 82 //啓動接收線程 83 isReceive = true; 84 receiveThread = new ReceiveThread(socket); 85 receiveThread.start(); 86 strMessage = GetSocketPostDataHeadInfo(PostState.first); 87 new Thread(sendThread).start(); 88 System.out.println("----connected success----"); 89 } catch (UnknownHostException e) { 90 // TODO Auto-generated catch block 91 e.printStackTrace(); 92 System.out.println("UnknownHostException-->" + e.toString()); 93 } catch (IOException e) { 94 // TODO Auto-generated catch block 95 e.printStackTrace(); 96 System.out.println("IOException" + e.toString()); 97 } 98 } 99 }; 100 //接收線程 101 protected class ReceiveThread extends Thread{ 102 private InputStream inStream = null; 103 104 private byte[] buffer; 105 private String str = null; 106 107 ReceiveThread(Socket socket){ 108 try { 109 inStream = socket.getInputStream(); 110 } catch (IOException e) { 111 // TODO Auto-generated catch block 112 e.printStackTrace(); 113 } 114 } 115 @Override 116 public void run(){ 117 while(isReceive){ 118 buffer = new byte[512]; 119 try { 120 inStream.read(buffer); 121 } catch (IOException e) { 122 // TODO Auto-generated catch block 123 e.printStackTrace(); 124 } 125 try { 126 str = new String(buffer,"UTF-8").trim(); 127 } catch (UnsupportedEncodingException e) { 128 // TODO Auto-generated catch block 129 e.printStackTrace(); 130 } 131 if(str!=""||str!=null){ 132 if(getRunningActivityName().equals("com.example.sockettestclient.TestGetSocketRes")){ 133 IsInPage=true; 134 } 135 if(IsInPage){ 136 Message msg = new Message(); 137 msg.obj = "/*star|data,aaa|end*/"+str; 138 SocketUIHandler.sendMessage(msg); 139 }else { 140 InitNotificationManager("通知",str); 141 } 142 if(str.indexOf("_pageend")>=0){ 143 IsInPage=false; 144 } 145 } 146 } 147 } 148 } 149 private String getRunningActivityName(){ 150 ActivityManager activityManager=(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); 151 String runningActivity=activityManager.getRunningTasks(1).get(0).topActivity.getClassName(); 152 return runningActivity; 153 } 154 //發送消息的接口 155 Runnable sendThread = new Runnable() { 156 157 @Override 158 public void run() { 159 // TODO Auto-generated method stub 160 byte[] sendBuffer = null; 161 try { 162 sendBuffer = strMessage.getBytes("UTF-8"); 163 } catch (UnsupportedEncodingException e1) { 164 // TODO Auto-generated catch block 165 e1.printStackTrace(); 166 } 167 try { 168 outStream = socket.getOutputStream(); 169 } catch (IOException e) { 170 // TODO Auto-generated catch block 171 e.printStackTrace(); 172 } 173 try { 174 outStream.write(sendBuffer); 175 } catch (IOException e) { 176 // TODO Auto-generated catch block 177 e.printStackTrace(); 178 } 179 } 180 }; 181 182 183 }
1 protected void onCreate(Bundle savedInstanceState) { 2 final BaseActivity app = (BaseActivity)getApplication(); 3 super.onCreate(savedInstanceState); 4 setContentView(R.layout.activity_test_login); 5 Button btnConnect = (Button)findViewById(R.id.button1); 6 //鏈接 7 btnConnect.setOnClickListener(new View.OnClickListener() { 8 9 @Override 10 public void onClick(View v) { 11 // TODO Auto-generated method stub 12 if (!app.isConnect){ 13 new Thread(app.connectThread).start(); 14 } 15 Intent intent = new Intent(); 16 intent.setClass(TestLogin.this, UserLookPage.class); 17 startActivity(intent); 18 } 19 }); 20 21 }
1 protected void onCreate(Bundle savedInstanceState) { 2 super.onCreate(savedInstanceState); 3 setContentView(R.layout.activity_test_get_socket_res); 4 app = (BaseActivity)getApplication(); 5 TextView textReceive = (TextView)findViewById(R.id.textView1); 6 textReceive.setMovementMethod(ScrollingMovementMethod.getInstance()); 7 textReceive.setText(""); 8 Intent intent=getIntent(); 9 String title=intent.getStringExtra("title"); 10 String Content=intent.getStringExtra("Content"); 11 if(app.isConnect){ 12 app.strMessage = app.GetSocketPostDataHeadInfo(PostState.data)+Content; 13 new Thread(app.sendThread).start(); 14 } 15 final TextView resval=(TextView)findViewById(R.id.textView1); 16 app.SocketUIHandler= new Handler(){ 17 @Override 18 public void handleMessage(Message msg){ 19 String removestr=app.GetSocketPostDataHeadInfo(PostState.data); 20 resval.append((msg.obj).toString().replace(removestr, "")); 21 } 22 }; 23 //new Thread(app.sendThread).start(); 24 }
1.最初設計思路是直接後臺將數據返回而後直接顯示在通知欄用戶打開通知欄將存在客戶端的數據顯示到頁面上結果折騰了5個小時的問題就是後臺返回的文本太多前臺一次性收不完而後形成了頁面顯示數據不全並且通知欄有一大段的文本提示異步
最後解決方案就是提示時候只提示有一條消息而後打開時服務端繼續推送到客戶端 客戶端採用追加的形式添加進去(能夠優化爲服務端分屢次返回數據當前是服務端一次性取完數據)socket
2.使用log4net時配置正確顯示不出的問題 解決方案ide
生成選項中選擇若是較新則複製
服務端運行
效果
點擊登陸
收到通知
跳轉到顯示數據界面並傳入數據
測試環境:VS2015,Neon.3 Release (4.6.3),Android4.4,逍遙安卓模擬器4.4版本
中的
中的
中的
未加wcf版本demo
連接: http://pan.baidu.com/s/1pKOoMld 密碼: e5pg
添加WCF服務工廠
1 public class AndroidServiceFactory : IAndroidServiceFactory 2 { 3 private readonly string serviceUri = Common.Constants.WCFAndroidBindAddressUrl; 4 5 public IAndroidService CreateService() 6 { 7 return this.CreateService<IAndroidService>(serviceUri); 8 } 9 10 private T CreateService<T>(string uri) 11 { 12 var key = string.Format("{0} - {1}", typeof(T), uri); 13 14 if (Caching.Get(key) == null) 15 { 16 var binding = new WSHttpBinding(); 17 binding.MaxReceivedMessageSize = maxReceivedMessageSize; 18 binding.SendTimeout = new TimeSpan(0, 10, 0); 19 binding.CloseTimeout = new TimeSpan(0, 10, 0); 20 binding.ReceiveTimeout = new TimeSpan(0, 10, 0); 21 binding.OpenTimeout = new TimeSpan(0, 10, 0); 22 binding.ReaderQuotas = new XmlDictionaryReaderQuotas(); 23 binding.ReaderQuotas.MaxStringContentLength = maxReceivedMessageSize; 24 binding.ReaderQuotas.MaxArrayLength = maxReceivedMessageSize; 25 binding.ReaderQuotas.MaxBytesPerRead = maxReceivedMessageSize; 26 27 ChannelFactory<T> chan = new ChannelFactory<T>(binding, new EndpointAddress(uri)); 28 chan.Open(); 29 var service = chan.CreateChannel(); 30 Caching.Set(key, service); 31 return service; 32 } 33 else 34 { 35 return (T)Caching.Get(key); 36 } 37 } 38 39 private const int maxReceivedMessageSize = int.MaxValue; 40 }
1 /// <summary> 2 /// 輪詢庫存 3 /// </summary> 4 /// <param name="users"></param> 5 /// <param name="AlertTitle"></param> 6 private void WcfGetInventory() 7 { 8 try 9 { 10 while (true) 11 { 12 string str = wcf.AndroidService.Replenishmentnotice(); 13 //記錄缺貨的庫存信息 14 if (!string.IsNullOrEmpty(str)) 15 { 16 lock (lockObj) 17 { 18 SendClientData = str; 19 } 20 } 21 //發送客戶端通知 22 SendToAllClient(ServiceSetting.AlertTitle); 23 //等待30分鐘後從新查詢1800000 24 Thread.Sleep(10000);//10秒 25 if (Thread.CurrentThread.IsAlive) 26 { 27 WcfReTry = 0; 28 } 29 else { 30 if (WcfReTry <= ServiceSetting.WcfReTryCount) 31 { 32 //重啓線程 33 Thread th = new Thread(WcfGetInventory); 34 th.Start(); 35 WcfReTry++; 36 } 37 LogHelper.Write("WcfGetInventory方法線程死掉而且沒法重啓", LogLev.Error); 38 } 39 } 40 } 41 catch (Exception ex) 42 { 43 if (WcfReTry <= ServiceSetting.WcfReTryCount) { 44 //重啓線程 45 Thread th = new Thread(WcfGetInventory); 46 th.Start(); 47 WcfReTry++; 48 } 49 LogHelper.Log(ex); 50 } 51 }
而後在服務啓動函數內啓動線程