需求:
描述:实时在客户端上获取到哪些款需要补货.
要求: 后台需要使用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中的更多相关文章
-
Asp.net SignalR 实现服务端消息实时推送到所有Web端
ASP .NET SignalR是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.实际上 Asp.net SignalR 2 实现 服务端消息推送到Web端, 更加 ...
-
spring集成webSocket实现服务端向前端推送消息
原文:https://blog.csdn.net/ya_nuo/article/details/79612158 spring集成webSocket实现服务端向前端推送消息 1.前端连接webso ...
-
服务端向客户端推送消息技术之websocket的介绍
websocket的介绍 在讲解WebSocket前,我们先来看看下面这种场景,在HTTP协议下,怎么实现. 需求: 在网站中,要实现简单的聊天,这种情况怎么实现呢?如下图: 当发送私信的时候,如果要 ...
-
WebService推送数据,数据结构应该怎样定义?
存放在Session有一些弊端,不能实时更新.server压力增大等... 要求:将从BO拿回来的数据存放在UI Cache里面,数据库更新了就通过RemoveCallback "告诉&qu ...
-
SSE推送数据
SSE(Server-Sent Event,服务端推送事件)是一种允许服务端向客户端推送新数据的HTML5技术.与由客户端每隔几秒从服务端轮询拉取新数据相比,这是一种更优的解决方案. WebSocke ...
-
Asp.net Core3.1+Vue 使用SignalR推送数据
本文就简单使用 往前端页面推送消息 SignalR 是什么 SignalR是一个.NET Core/.NET Framework的开源实时框架. SignalR的可使用Web Socket, Serv ...
-
使用SignalR ASP.NET Core来简单实现一个后台实时推送数据给Echarts展示图表的功能
什么是 SignalR ASP.NET Core ASP.NET Core SignalR 是一种开放源代码库,可简化将实时 web 功能添加到应用程序的功能. 实时 web 功能使服务器端代码可以立 ...
-
SQL Server 2000向SQL Server 2008 R2推送数据
[文章摘要]最近做的一个项目要获取存在于其他服务器的一些数据,为了安全起见,采用由其他“服务器”向我们服务器推送的方式实现.我们服务器使用的是SQL Server 2008 R2,其他“服务器”使用的 ...
-
wp8.1 Study16:网络之 使用Azure移动服务及利用Azure推送通知服务
一.WP8.1有关网络的API WP8.1与其它平台的对比如下图: 二.Azure移动服务 前提: Azure移动服务可以让使用者的数据存放在云空间,从而方便使用者的App在不同平台上的数据共享. 1 ...
随机推荐
-
【干货】JS版汉字与拼音互转终极方案,附简单的JS拼音输入法
前言 网上关于JS实现汉字和拼音互转的文章很多,但是比较杂乱,都是互相抄来抄去,而且有的不支持多音字,有的不支持声调,有的字典文件太大,还比如有时候我仅仅是需要获取汉字拼音首字母却要引入200kb的字 ...
-
mac os x 连不上android 手机
在系统关于中查询mac的usb的厂商ID 如id为0x2717 执行echo 0x2717 >> ~/.android/adb_usb.ini 即可
-
PowerMock与EasyMock的应用(转)
Leader请求在做Junit测试的时辰,Mock掉各个办法之间的依附.这两天进修了下PowerMock的应用. PowerMock是EasyMock的一个扩大,参加了static,final,pri ...
-
Ubuntu下VSFTPD(六)(常见FTP命令及其功能) (
常见FTP命令及其功能 FTP 命令 功能 FTP 命令 功能 ls 显示服务器上的目录 ls [remote-dir][local-file] 显示远程目录remote-dir,并存入本地文件 ...
-
uoj164. 【清华集训2015】V 统计
坑爹题面:http://uoj.ac/problem/164 正常题面: 对于一个序列支持下列5个操作: 1.区间加x 2.区间减x并与0取max 3.区间覆盖 4.单点查询 5.单点历史最大值查询 ...
-
阿里云服务器上架设apache php mysql 环境
由于朋友一公司要做企业站,于是就买了阿里云的服务器.买完进去发现iptables 和selinux默认就是关掉的,可能是因为阿里云有云盾就可以不用自带的防火墙吧,具体配置过程如下(我边配边记录的): ...
-
Dockerfile
http://blog.csdn.net/jiashiwen/article/details/48806243 一:如何使用: Dockerfile 用来创建一个自定义的image,包含了用户指定的软 ...
-
第四天学习内容 if switch for 的练习
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...
-
【洛谷P5020】货币系统 完全背包
题目大意:给定 N 个数,求在这 N 个数中至少选出几个数能表示出所有数字,输出最少的个数. 题解:由于只有小的数字可以表示大的数字,因此首先需要对这 N 个数字进行从小到大排序.排序之后就变成一道不 ...
-
不同的最小割(cqoi2016,bzoj4519)(最小割树)
学过图论的同学都知道最小割的概念:对于一个图,某个对图中结点的划分将图中所有结点分成 两个部分,如果结点\(s,t\)不在同一个部分中,则称这个划分是关于\(s,t\)的割.对于带权图来说,将 所有顶 ...