C#服务端通过Socket推送数据到Android端App中

需求:

  描述:实时在客户端上获取到哪些款需要补货.

     要求: 后台需要使用c#,并且哪些需要补货的逻辑写在公司框架内,客户端采用PDA(Android客户端 版本4.4) . 用户打开了补货通知页面时不需要在通知栏推送 如果不在时需要通知栏推送消息.

实现思路设计

思路图解释:

想法是启动服务时候创建TcpListener监听指定的Ip端口创建接收连接的线程和接收数据的回调由于 定义的存储方式(Dictionary<string, ClientSocketManager> Clients = new Dictionary<string, ClientSocketManager>();)所以采用了先由客户端建立连接然后服务端收到连接请求时立即发送一条数据给客户端要求客户端发送用户签名给服务端以便服务端可以根据用户Id之类的数据去发送给指定的用户 设置的数据格式为/*star|状态,user标识|end*/真实数据

客户端登录时候和服务端建立连接在任意界面都可以接收到通知打开通知时候进入展示数据的页面

服务端关键性代码

 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         }
OnStart
 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         }
发送数据到客户端

客户端关键性代码

先授权

1   <uses-permission android:name="android.permission.INTERNET" />
2 <uses-permission android:name="android.permission.GET_TASKS" />  
AndroidManifest.xml

在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 }
BaseApplication
 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个小时的问题就是后台返回的文本太多前台一次性收不完然后造成了页面显示数据不全而且通知栏有一大段的文本提示

最后解决方案就是提示时候只提示有一条消息然后打开时服务端继续推送到客户端 客户端采用追加的形式添加进去(可以优化为服务端分多次返回数据当前是服务端一次性取完数据)

2.使用log4net时配置正确显示不出的问题 解决方案

生成选项中选择如果较新则复制

运行效果

服务端运行

效果

点击登录

收到通知

跳转到显示数据界面并传入数据

测试环境:VS2015,Neon.3 Release (4.6.3),Android4.4,逍遥安卓模拟器4.4版本

本例运行需要更改的内容

中的

中的

 中的

本例demo下载

未加wcf版本demo

链接: http://pan.baidu.com/s/1pKOoMld 密码: e5pg

补充:demo中联入WCF

添加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     }
AndroidServiceFactory
 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         }
加入轮询wcf方法

然后在服务启动函数内启动线程

原文地址:https://www.cnblogs.com/nontracey/p/6623555.html