前面写过《WinCE数据通讯之Web Service篇》那篇对于数据量不是很大的情况下单包传输是可以了,但是对于大数据量的情况下WinCE终端的内存往往会在解包或者接受数据时产生内存溢出。具体在多大数据量下会溢出,得根据不同的终端设备内存而定。我做的一个项目测试,表列为47列,其中各种数据类型都可能存在,当下载单包数据在三千条左右时终端解压将报内存溢出错误,终端用的是32M的Flash。因此,所为商用项目,在不确定数据量情况下,分包下载是必须的。相应的上传,也应该根据实际情况进行分包上传,只不过分包的工作在终端设备上进行,性质是一样的。

分包传输要解决的一个关键问题,需要反馈包的大小给终端及接收终端请求的第几包数值。包的大小的确定原则上是根据终端能够处理的数据的字节数来确定,服务器端每次传输在终端最大能够接收的字节流量就可以了。实际在操作过程中,我开始也是这么做的,但是效率有些问题,每次终端请求时要对包字节流量进行统计,较费时间。后面想到一个“偷懒”的方法,用表记录的条数来决定字节流量,根据不同的表列字段设置不同的记录数,基本上能够跟字节流量接近,只要不超过上限就可以了,实际上也不用特别精确,因此还是可行的。记录数的确定可以另外作一个小程序针对不同的表事先计算出来写成配置文件就行了,针对一个项目只要配置好一次以后实际上传下载过程中读取这个记录数值就可以了,用表记录数来决定分包大小在计算包数时速度相比每次都要进行字节流统计省时高效。

配置文件格式如下:

  1. 配置文件
  2. <?xml version="1.0" encoding="utf-8"?>
  3. <Config>
  4. <add key="PRICE" value="SELECT [Customer_ID], [Goods_No], [UnitPrice], [BalancePrice],[ModifyDate] FROM [Price]"/>
  5. <add key="PRICEROW" value="6000"/>
  6. <add key="GOODS" value="SELECT [Goods_no],[Goods_name], [Size_class],[NewOld], [UnitPrice],[Input_Date],[QiPrice], [MaiPrice] FROM [Goods] WHERE [Input_Date]>'{0}'" />
  7. <add key="GOODSROW" value="2500"/>
  8. <add key="CSTOCK" value="INSERT INTO [Cstock]([StockCode], [Customer_Id], [Goods_No], [Grade], [Quantity])VALUES('{0}', '{1}','{2}', '{3}',CASE WHEN '{4}'=''THEN 0 ELSE CONVERT(INT,'{4}') END)"/>
  9. </Config>

这里对表操作的读取和插入都写在配置文件里了,这样方便对字段和表的修改。

下面获取数据表数据方法:

  1. 获取数据表数据
  2. public DataTable getTableData(ref int result, string tableName, params string[] para)
  3. {
  4. result = -;
  5. string repStr;
  6. try
  7. {
  8. string cmdTxt = commandTxt(tableName);
  9. for (int i = ; i < para.Length; i++)
  10. {
  11. repStr = "{" + i.ToString() + "}";
  12. cmdTxt = cmdTxt.Replace(repStr, para.GetValue(i).ToString());
  13. }
  14. DataAccess das = new DataAccess();
  15. DataTable dt = das.getTable(cmdTxt);
  16. result = ;
  17. return dt;
  18. }
  19. catch (Exception ex)
  20. {
  21. return null;
  22. }
  23. }
  24. public static string commandTxt(string tableName)
  25. {
  26. config conf = new config();
  27. return conf.getConfigValue(tableName.ToUpper());
  28. }

对获取到的数据表进行编码及压缩处理

  1. public static byte[] getStrZipData(ref int result, ref int pgTotal, int currentPg, string tableName, params string[] para)
  2. {
  3. config conf = new config();
  4. int rowLength = Convert.ToInt32(conf.getConfigValue(string.Format("{0}ROW", tableName.ToUpper())));
  5. dataTableClass dtcs = new dataTableClass();
  6. DataTable dt = dtcs.getTableData(ref result, tableName, para);
  7. if (result != )
  8. return null;
  9. int len = Convert.ToInt16(dt.Rows.Count / rowLength);
  10. pgTotal = dt.Rows.Count % rowLength == ? len : len + ;
  11. DataTable cdt = dt.Clone();
  12. StringBuilder builder = new StringBuilder();
  13. int startRow = (currentPg - ) * rowLength;
  14. int endRow = startRow + rowLength < dt.Rows.Count ? startRow + rowLength : dt.Rows.Count;
  15. for (int i = startRow; i < endRow; i++)
  16. {
  17. for (int j = ; j < dt.Columns.Count; j++)
  18. {
  19. switch (dt.Rows[i][j].GetType().Name)
  20. {
  21. case "String":
  22. case "DateTime":
  23. builder.Append("'");
  24. builder.Append(dt.Rows[i][j]);
  25. builder.Append("',");
  26. break;
  27. case "Int16":
  28. case "Int32":
  29. case "Decimal":
  30. builder.Append(dt.Rows[i][j]);
  31. builder.Append(",");
  32. break;
  33. case "Boolean":
  34. builder.Append(Convert.ToInt32(dt.Rows[i][j]));
  35. builder.Append(",");
  36. break;
  37. case "DBNull":
  38. builder.Append("null");
  39. builder.Append(",");
  40. break;
  41. default:
  42. break;
  43. }
  44. }
  45. builder.Remove(builder.Length - , );
  46. builder.Append(";");
  47. }
  48. builder.Remove(builder.Length - , );
  49. byte[] serial = Encoding.Unicode.GetBytes(builder.ToString());
  50. byte[] zipData = zip.BZipCompress(serial);
  51.  
  52. result = ;
  53. return zipData;
  54. }

其中代码中的压缩zip类在前文中已经有说明。这里对数据类型进行处理于方便终端直接接收到数据后进行插入,而无须对数据类型再进行转换,节省终端插入数据处理时间。另外把序列化方法抛弃改用字符串格式,在实际的测试过程中,用XML序列化的时间要远大于字符串格式转换时间,两者不是在一个数量级别上,终端调用后解字符串和反XML序列化所耗的时间对比更明显。

接下来调用下载数据

  1. WebService调用方法
  2. [WebMethod(Description = "获取Table表数据;传入参数(ref int 调用结果,ref int 总包数,int 当前请求包(初始值为1),string 调用表名,string[]参数),返回压缩字节流")]
  3. public byte[] getTable(ref int result, ref int totalPg, int currentPg, string tableName, params string[] para)
  4. {
  5. try
  6. {
  7. byte[] zipData = Business.zipData.getStrZipData(ref result, ref totalPg, currentPg, tableName, para);
  8. return zipData;
  9. }
  10. catch (Exception ex)
  11. {
  12. return null;
  13. }
  14. }

至此,整个下载调用过程就完成了,这个下载调用方法在页面中不能调用,因为有ref参数及params参数,若要进行测试,可以另写一个不含上述参数的调用方法即可。

下面是上传的流程

上传数据调用方法

  1. 上传数据调用方法
  2. [WebMethod(Description = "上传Table表数据;传入参数(string 调用表名,byte[]压缩数据流),返回结果值(1成功,<0失败)")]
  3. public int putTable(string tableName, byte[] zipData)
  4. {
  5. try
  6. {
  7. if (Business.zipData.putTableStr(tableName, zipData))
  8. return ;
  9. else
  10. return -;
  11. }
  12. catch (Exception ex)
  13. {
  14. return -;
  15. }
  16. }

对接收到的数据进行解码处理

  1. 接收数据处理
  2. public static bool putTableStr(string tableName, byte[] zipData)
  3. {
  4. try
  5. {
  6. string cmdTxt = dataTableClass.commandTxt(tableName);
  7. //byte[] serial = zip.BZipDeCompress(zipData);
  8. byte[] serial = zip.ZipDeCompress(zipData);
  9. //DataTable dt = (DataTable)zip.objXmlDeserialize(serial, typeof(DataTable));
  10. string strData = Encoding.Unicode.GetString(serial);
  11. DataAccess das = new DataAccess();
  12. bool insertSuccess = das.InsertTable(strData, cmdTxt);
  13. return insertSuccess;
  14. }
  15. catch (Exception ex)
  16. {
  17. return false;
  18. }
  19. }

插入数据表处理方法

  1. public bool InsertTable(string strData, string sqlStr)
  2. {
  3. SqlCommand cmd = new SqlCommand();
  4. cmd.Connection = conn;
  5. openDB();
  6. string[] insertStr = strData.Split(';');
  7. string cmdTxt = sqlStr;
  8. string repStr;
  9. SqlTransaction tra = conn.BeginTransaction();
  10. cmd.Transaction = tra;
  11. bool success = true;
  12. try
  13. {
  14. foreach (string str in insertStr)
  15. {
  16. string[] s = str.Split(',');
  17. for (int i = ; i < s.Length; i++)
  18. {
  19. repStr = "{" + i.ToString() + "}";
  20. cmdTxt = cmdTxt.Replace(repStr, s[i].ToString());
  21. }
  22. cmd.CommandText = cmdTxt;
  23. if (cmd.ExecuteNonQuery() <= )
  24. {
  25. success = false;
  26. throw new Exception("insert error");
  27. }
  28. cmdTxt = sqlStr;
  29. }
  30. tra.Commit();
  31. return success;
  32. }
  33. catch (Exception ex)
  34. {
  35. tra.Rollback();
  36. return false;
  37. }
  38. finally
  39. {
  40. tra.Dispose();
  41. cmd.Dispose();
  42. closeDB();
  43. }
  44. }

整个数据接收完成。

WinCE数据通讯之Web Service分包传输篇的更多相关文章

  1. WinCE数据通讯之Web Service篇

    准备写个WinCE平台与数据库服务器数据通讯交互方面的专题文章,今天先整理个Web Service通讯方式. 公司目前的硬件产品平台是WinCE5.0,数据通讯是连接服务器与终端的桥梁,关系着终端的数 ...

  2. 基于Web Service的客户端框架搭建一:C#使用Http Post方式传递Json数据字符串调用Web Service

    引言 前段时间一直在做一个ERP系统,随着系统功能的完善,客户端(CS模式)变得越来越臃肿.现在想将业务逻辑层以下部分和界面层分离,使用Web Service来做.由于C#中通过直接添加引用的方来调用 ...

  3. WinCE数据通讯之SqlCE数据同步篇

    上一篇总结了WinCE通过WebService进行数据通讯的交互方式,今天整理个SqlCE数据同步方式的内容.先说下软件环境:终端平台使用WinCE5.0+SqlCE2.0,服务器使用Windows ...

  4. 建立自己的Web service(SOAP篇)

    1.简介 这篇文章主要介绍采用SOAP来建立以及访问Web service接口. Web service是一个平台独立的,低耦合的,自包含的.基于可编程的web的应用程序,可使用开放的XML(标准通用 ...

  5. 从WEB SERVICE 上返回大数据量的DATASET

    前段时间在做一个项目的时候,遇到了要通过WEB SERVICE从服务器上返回数据量比较大的DATASET,当然,除了显示在页面上以外,有可能还要用这些数据在客户端进行其它操作.查遍了网站的文章,问了一 ...

  6. Web Service 的工作原理

    Web Service基本概念 Web Service也叫XML Web Service WebService是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求,轻量级的 ...

  7. Web Service工作原理

    Web Service基本概念 Web Service也叫XML Web Service WebService是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求,轻量级的 ...

  8. Web Service 的工作原理(转载)

    Web Service基本概念 Web Service也叫XML Web Service WebService是一种可以接收从Internet或者Intranet上的其它系统中传递过来的请求,轻量级的 ...

  9. 【转载】Web Service 的工作原理

    http://www.cnblogs.com/Jessy/p/3528341.html Web Service基本概念 Web Service也叫XML Web Service WebService是 ...

随机推荐

  1. 爬虫之UserAgent

    UserAgent简介 UserAgent中文名为用户代理,是Http协议中的一部分,属于头域的组成部分,UserAgent也简称UA.它是一个特殊字符串头,是一种向访问网站提供你所使用的浏览器类型及 ...

  2. Spark 2.0 PCA主成份分析

    PCA在Spark2.0中用法比较简单,只需要设置: .setInputCol(“features”)//保证输入是特征值向量 .setOutputCol(“pcaFeatures”)//输出 .se ...

  3. 使用cygwin移植Linux的项目到Windows下之总结(转)

    使用cygwin移植Linux的项目到Windows下之总结(转) 原文 http://my.oschina.net/michaelyuanyuan/blog/68615?p=1   一.why   ...

  4. 如何获知PHP程序占用多少内存(复制)

    想要知道编写的 PHP 脚本需要占用多少内存么?很简单,直接使用 PHP 查看当前分配给 PHP 脚本的内存的函数 memory_get_usage() 就可以了 下面是使用示例: 复制代码 代码如下 ...

  5. maven tomcat7-maven-plugin配置及背景

    背景: 在研发阶段,想让一个服务通过tomcat启动起来有很多的方法,常用的idea都有这样的支持,那么如果我们没有tomcat,能不能让服务通过tomcat启动起来呢?maven就提供了这样的支持. ...

  6. git 移除某个文件的版本管理

    1:最简单的,在项目刚创建的时候,在根目录的.gitignore,加入该文件的相对路径 2:已经被纳入到了版本控制,使用在当前目录下,打开cmd窗口 输入rm命令,加上文件的绝对路径(相对路径没试过) ...

  7. google GFS

    我们设计并实现了Google GFS文件系统,一个面向大规模数据密集型应用的.可伸缩的分布式文件系统.GFS虽然运行在廉价的普遍硬件设备上,但是它依然了提供灾难冗余的能力,为大量客户机提供了高性能的服 ...

  8. spark[源码]-Pool分析

    概述 这篇文章主要是分析一下Pool这个任务调度的队列.整体代码量也不是很大,正好可以详细的分析一下,前面在TaskSchedulerImpl提到大体的功能,这个点在丰富一下吧. DAGSchedul ...

  9. 对Java ConcurrentHashMap的一些了解

    ①引言(为什么要使用ConcurrentHashMap) 因为多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap. Has ...

  10. eclipse 创建jsp报错