需求:

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

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

实现思路设计

思路图解释:

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

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

服务端关键性代码

    protected override void OnStart(string[] args)
{
if (IsListened) return;
//启动监听
IPAddress ip = IPAddress.Parse(ServiceSetting._IPAddress);
IPEndPoint point = new IPEndPoint(ip, ServiceSetting._IPEndPoint);
listener = new TcpListener(point);
try
{
listener.Start();
}
catch (Exception ex)
{
LogHelper.Log(ex);
return;
}
IsListened = true;
//接受连接请求的异步调用
AsyncCallback callback = new AsyncCallback(AcceptCallBack);
listener.BeginAcceptSocket(callback, listener);
LogHelper.Write("服务已启动……", LogLev.Info);
}

OnStart

   private void AcceptCallBack(IAsyncResult ar)
{
try
{
//完成异步接收连接请求的异步调用
Socket handle = listener.EndAcceptSocket(ar);
ClientSocketManager manager = new ClientSocketManager(handle); AsyncCallback callback;
//继续调用异步方法接收连接请求
if (IsListened)
{
callback = new AsyncCallback(AcceptCallBack);
listener.BeginAcceptSocket(callback, listener);
}
//开始在连接上进行异步的数据接收
manager.ClearBuffer();
callback = new AsyncCallback(ReceiveCallback);
manager.socket.BeginReceive(manager.Rcvbuffer, , manager.Rcvbuffer.Length, SocketFlags.None, callback, manager);
}
catch (Exception ex)
{
//在调用EndAcceptSocket方法时可能引发异常
//套接字Listener被关闭,则设置为未启动侦听状态
IsListened = false;
LogHelper.Log(ex);
return;
}
}

接收连接回调

   private void ReceiveCallback(IAsyncResult ar)
{
ClientSocketManager manager = (ClientSocketManager)ar.AsyncState;
try
{
int i = manager.socket.EndReceive(ar);
if (i == )
{
RemoveOneClientbyManager(manager);
return;
}
else
{
string data = Encoding.UTF8.GetString(manager.Rcvbuffer, , i);
//manager.socket.RemoteEndPoint.ToString() 获取客户端IP
manager.ClearBuffer();
//根据传入数据处理用户数据 设置数据格式为/*star|状态,user标识|end*/真实数据
//匹配中间的状态正则表达式 ^(/[*]star\|).*(?=\|end[*]/)
//首次传入保存连接信息
//打开通知时接收到回调
string packgeHead = Regex.Match(data, @"^(/[*]star\|).*(?=\|end[*]/)").ToString().Replace("/*star|", "");
if (string.IsNullOrEmpty(packgeHead))
{
LogHelper.Write("客户端没有发送指定的数据头", LogLev.Warn);
return;
}
string[] heads = packgeHead.Split(',');
switch (heads[])
{
case "first":// 首次传入保存连接信息
lock (lockObj)
{
Clients.Add(heads[], manager);
}
//ToRemove
SendToClient(new List<string>() { "aaa" }, "您有一条新的通知");
//endRemove
break;
case "data"://用户打开了页面可以发送数据
ReceiveThenSend(manager);
break;
default:
LogHelper.Write("客户端发送的数据头有问题", LogLev.Warn);
break;
}
AsyncCallback callback = new AsyncCallback(ReceiveCallback);
manager.socket.BeginReceive(manager.Rcvbuffer, , manager.Rcvbuffer.Length, SocketFlags.None, callback, manager);
}
}
catch (Exception ex)
{
RemoveOneClientbyManager(manager);
LogHelper.Log(ex);
return;
}
}

接收数据回调

   private void SendData(ClientSocketManager manager, string data)
{
try
{
byte[] msg = Encoding.UTF8.GetBytes(data);
AsyncCallback callback = new AsyncCallback(new Action<IAsyncResult>(ar =>
{
ClientSocketManager frd = (ClientSocketManager)ar.AsyncState;
try
{
frd.socket.EndSend(ar);
}
catch (Exception ex)
{
RemoveOneClientbyManager(manager);
LogHelper.Log(ex);
return;
}
}));
manager.socket.BeginSend(msg, , msg.Length, SocketFlags.None, callback, manager);
}
catch (Exception ex)
{
RemoveOneClientbyManager(manager);
LogHelper.Log(ex);
return;
}
}

发送数据到客户端

客户端关键性代码

先授权

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

AndroidManifest.xml

在Application中插入代码 (ps:文件名由于开始打算建的是父级的Activity后来实验不行就没有更换名称)

 package com.example.sockettestclient;

 import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException; import android.app.ActivityManager;
import android.app.Application;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.NetworkInfo.State;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.NotificationCompat;
import android.widget.EditText;
import android.widget.TextView; public class BaseActivity extends Application{
@Override
public void onCreate(){
super.onCreate();
}
//NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
//发送通知
protected void InitNotificationManager(String Title,String Content) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
Intent intent = new Intent(this, TestGetSocketRes.class);//将要跳转的界面
builder.setAutoCancel(true);//点击后消失
builder.setSmallIcon(R.drawable.info);//设置通知栏消息标题的头像
builder.setDefaults(NotificationCompat.DEFAULT_SOUND);//设置通知铃声
builder.setTicker(Title);
builder.setContentText(Content);//通知内容
builder.setContentTitle(Title);
//添加参数
Bundle bundle = new Bundle();
bundle.putString("title", Title);
bundle.putString("Content", Content);
intent.putExtras(bundle);
//利用PendingIntent来包装我们的intent对象,使其延迟跳转
PendingIntent intentPend = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(intentPend);
NotificationManager manager = (NotificationManager) this.getSystemService(this.NOTIFICATION_SERVICE);
manager.notify(0, builder.build());
}
protected boolean isConnect = false;
protected static final String ServerIP = "192.168.47.102";
protected static final int ServerPort = 4567;
protected Socket socket = null;
protected ReceiveThread receiveThread = null;
protected boolean isReceive = false;
protected Handler SocketUIHandler = null;
protected OutputStream outStream;
protected String strMessage;
protected TextView textReceive = null;
protected EditText textSend = null;
private boolean IsInPage=false;
private String UserID="aaa";
public enum PostState {
first, data
}
public void SetUserInfo() {
UserID="aaa";
}
public String GetSocketPostDataHeadInfo(PostState state) {
return "/*star|"+state.toString()+","+UserID+"|end*/";
}
Runnable connectThread = new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
try {
//初始化Scoket,连接到服务器
socket = new Socket(ServerIP, ServerPort);
isConnect = true;
//启动接收线程
isReceive = true;
receiveThread = new ReceiveThread(socket);
receiveThread.start();
strMessage = GetSocketPostDataHeadInfo(PostState.first);
new Thread(sendThread).start();
System.out.println("----connected success----");
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("UnknownHostException-->" + e.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("IOException" + e.toString());
}
}
};
//接收线程
protected class ReceiveThread extends Thread{
private InputStream inStream = null; private byte[] buffer;
private String str = null; ReceiveThread(Socket socket){
try {
inStream = socket.getInputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run(){
while(isReceive){
buffer = new byte[512];
try {
inStream.read(buffer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
str = new String(buffer,"UTF-8").trim();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(str!=""||str!=null){
if(getRunningActivityName().equals("com.example.sockettestclient.TestGetSocketRes")){
IsInPage=true;
}
if(IsInPage){
Message msg = new Message();
msg.obj = "/*star|data,aaa|end*/"+str;
SocketUIHandler.sendMessage(msg);
}else {
InitNotificationManager("通知",str);
}
if(str.indexOf("_pageend")>=0){
IsInPage=false;
}
}
}
}
}
private String getRunningActivityName(){
ActivityManager activityManager=(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
String runningActivity=activityManager.getRunningTasks(1).get(0).topActivity.getClassName();
return runningActivity;
}
//发送消息的接口
Runnable sendThread = new Runnable() { @Override
public void run() {
// TODO Auto-generated method stub
byte[] sendBuffer = null;
try {
sendBuffer = strMessage.getBytes("UTF-8");
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
outStream = socket.getOutputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
outStream.write(sendBuffer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}; }

BaseApplication

 protected void onCreate(Bundle savedInstanceState) {
final BaseActivity app = (BaseActivity)getApplication();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_login);
Button btnConnect = (Button)findViewById(R.id.button1);
//连接
btnConnect.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (!app.isConnect){
new Thread(app.connectThread).start();
}
Intent intent = new Intent();
intent.setClass(TestLogin.this, UserLookPage.class);
startActivity(intent);
}
}); }

登录页

 protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_get_socket_res);
app = (BaseActivity)getApplication();
TextView textReceive = (TextView)findViewById(R.id.textView1);
textReceive.setMovementMethod(ScrollingMovementMethod.getInstance());
textReceive.setText("");
Intent intent=getIntent();
String title=intent.getStringExtra("title");
String Content=intent.getStringExtra("Content");
if(app.isConnect){
app.strMessage = app.GetSocketPostDataHeadInfo(PostState.data)+Content;
new Thread(app.sendThread).start();
}
final TextView resval=(TextView)findViewById(R.id.textView1);
app.SocketUIHandler= new Handler(){
@Override
public void handleMessage(Message msg){
String removestr=app.GetSocketPostDataHeadInfo(PostState.data);
resval.append((msg.obj).toString().replace(removestr, ""));
}
};
//new Thread(app.sendThread).start();
}

接收数据页

开发中遇到的问题

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服务工厂

  public class AndroidServiceFactory : IAndroidServiceFactory
{
private readonly string serviceUri = Common.Constants.WCFAndroidBindAddressUrl; public IAndroidService CreateService()
{
return this.CreateService<IAndroidService>(serviceUri);
} private T CreateService<T>(string uri)
{
var key = string.Format("{0} - {1}", typeof(T), uri); if (Caching.Get(key) == null)
{
var binding = new WSHttpBinding();
binding.MaxReceivedMessageSize = maxReceivedMessageSize;
binding.SendTimeout = new TimeSpan(, , );
binding.CloseTimeout = new TimeSpan(, , );
binding.ReceiveTimeout = new TimeSpan(, , );
binding.OpenTimeout = new TimeSpan(, , );
binding.ReaderQuotas = new XmlDictionaryReaderQuotas();
binding.ReaderQuotas.MaxStringContentLength = maxReceivedMessageSize;
binding.ReaderQuotas.MaxArrayLength = maxReceivedMessageSize;
binding.ReaderQuotas.MaxBytesPerRead = maxReceivedMessageSize; ChannelFactory<T> chan = new ChannelFactory<T>(binding, new EndpointAddress(uri));
chan.Open();
var service = chan.CreateChannel();
Caching.Set(key, service);
return service;
}
else
{
return (T)Caching.Get(key);
}
} private const int maxReceivedMessageSize = int.MaxValue;
}

AndroidServiceFactory

   /// <summary>
/// 轮询库存
/// </summary>
/// <param name="users"></param>
/// <param name="AlertTitle"></param>
private void WcfGetInventory()
{
try
{
while (true)
{
string str = wcf.AndroidService.Replenishmentnotice();
//记录缺货的库存信息
if (!string.IsNullOrEmpty(str))
{
lock (lockObj)
{
SendClientData = str;
}
}
//发送客户端通知
SendToAllClient(ServiceSetting.AlertTitle);
//等待30分钟后重新查询1800000
Thread.Sleep();//10秒
if (Thread.CurrentThread.IsAlive)
{
WcfReTry = ;
}
else {
if (WcfReTry <= ServiceSetting.WcfReTryCount)
{
//重启线程
Thread th = new Thread(WcfGetInventory);
th.Start();
WcfReTry++;
}
LogHelper.Write("WcfGetInventory方法线程死掉并且无法重启", LogLev.Error);
}
}
}
catch (Exception ex)
{
if (WcfReTry <= ServiceSetting.WcfReTryCount) {
//重启线程
Thread th = new Thread(WcfGetInventory);
th.Start();
WcfReTry++;
}
LogHelper.Log(ex);
}
}

加入轮询wcf方法

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

C#服务端通过Socket推送数据到Android端App中的更多相关文章

  1. Asp.net SignalR 实现服务端消息实时推送到所有Web端

    ASP .NET SignalR是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.实际上 Asp.net SignalR 2 实现 服务端消息推送到Web端, 更加 ...

  2. spring集成webSocket实现服务端向前端推送消息

    原文:https://blog.csdn.net/ya_nuo/article/details/79612158 spring集成webSocket实现服务端向前端推送消息   1.前端连接webso ...

  3. 服务端向客户端推送消息技术之websocket的介绍

    websocket的介绍 在讲解WebSocket前,我们先来看看下面这种场景,在HTTP协议下,怎么实现. 需求: 在网站中,要实现简单的聊天,这种情况怎么实现呢?如下图: 当发送私信的时候,如果要 ...

  4. WebService推送数据,数据结构应该怎样定义?

    存放在Session有一些弊端,不能实时更新.server压力增大等... 要求:将从BO拿回来的数据存放在UI Cache里面,数据库更新了就通过RemoveCallback "告诉&qu ...

  5. SSE推送数据

    SSE(Server-Sent Event,服务端推送事件)是一种允许服务端向客户端推送新数据的HTML5技术.与由客户端每隔几秒从服务端轮询拉取新数据相比,这是一种更优的解决方案. WebSocke ...

  6. Asp.net Core3.1+Vue 使用SignalR推送数据

    本文就简单使用 往前端页面推送消息 SignalR 是什么 SignalR是一个.NET Core/.NET Framework的开源实时框架. SignalR的可使用Web Socket, Serv ...

  7. 使用SignalR ASP.NET Core来简单实现一个后台实时推送数据给Echarts展示图表的功能

    什么是 SignalR ASP.NET Core ASP.NET Core SignalR 是一种开放源代码库,可简化将实时 web 功能添加到应用程序的功能. 实时 web 功能使服务器端代码可以立 ...

  8. SQL Server 2000向SQL Server 2008 R2推送数据

    [文章摘要]最近做的一个项目要获取存在于其他服务器的一些数据,为了安全起见,采用由其他“服务器”向我们服务器推送的方式实现.我们服务器使用的是SQL Server 2008 R2,其他“服务器”使用的 ...

  9. wp8.1 Study16:网络之 使用Azure移动服务及利用Azure推送通知服务

    一.WP8.1有关网络的API WP8.1与其它平台的对比如下图: 二.Azure移动服务 前提: Azure移动服务可以让使用者的数据存放在云空间,从而方便使用者的App在不同平台上的数据共享. 1 ...

随机推荐

  1. Python爬虫开发【第1篇】【代理】

    1.简单的自定义opener() import urllib2 # 构建一个HTTPHandler 处理器对象,支持处理HTTP请求 http_handler = urllib2.HTTPHandle ...

  2. 【Codevs 2630】宝库通道

    http://codevs.cn/problem/2630/ Solution 预处理f[i][j],代表第j列前i行的代价 枚举上下界,然后做最大子段和,g[i]代表选到第i列的代价, g[k]=( ...

  3. 理解dropout——本质是通过阻止特征检测器的共同作用来防止过拟合 Dropout是指在模型训练时随机让网络某些隐含层节点的权重不工作,不工作的那些节点可以暂时认为不是网络结构的一部分,但是它的权重得保留下来(只是暂时不更新而已),因为下次样本输入时它可能又得工作了

    理解dropout from:http://blog.csdn.net/stdcoutzyx/article/details/49022443 http://www.cnblogs.com/torna ...

  4. 网络流之最大流算法(EK算法和Dinc算法)

    最大流 网络流的定义: 在一个网络(有流量)中有两个特殊的点,一个是网络的源点(s),流量只出不进,一个是网络的汇点(t),流量只进不出. 最大流:就是求s-->t的最大流量 假设 u,v 两个 ...

  5. python-----tuple用法

    有一种有序列表叫元组:tuple.tuple和list非常类似,但是tuple一旦初始化就不能修改,比如同样是列出同学的名字: >>> classmates = ('Michael' ...

  6. xargs 主要用于不支持管道的shell命令*****

    变量置换,主要用于不支持管道的shell命令,如:rm.sed等,但有些命令需要占位符“{}”需要注意.比如:删除文件- ls|xargs -i rm -rf {} 文件改名-   ls|xargs ...

  7. gitlab https

    https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#using-https https:// ...

  8. sql server数据库占用cpu太大,使用sys.dm_exec_query_stats查询优化

    查询sql语句占用 CPU详细信息: SELECT (SELECT TOP 1 SUBSTRING(s2.text,statement_start_offset / 2+1 , ( (CASE WHE ...

  9. bzoj 3396: [Usaco2009 Jan]Total flow 水流【最大流】

    最大流生动形象的板子,注意数组开大点 #include<iostream> #include<cstdio> #include<queue> #include< ...

  10. 1617: [Usaco2008 Mar]River Crossing渡河问题(dp)

    1617: [Usaco2008 Mar]River Crossing渡河问题 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1219  Solved:  ...