老瓶装新酒 - C#调用WM手机发送短信(源码)
一些系统,需要能够发送短信,量很小,平均每日10条。
运营商平台太贵,白名单很严格,小额只能发省内;
各短信平台有各种限制,大事件前后会关闭;
飞信以前可以用WAP发,但是现在多了验证码,搞不定了;
短信猫买来还得写控制程序,价钱和时间上……
手上有两部废弃的WM手机,没有故障,只是老旧不好用!
有一个WM软件(后面有下载),它在WM手机上开启一个Http服务器,使得我们可以在PC上通过浏览器直接访问!
有收发短信的功能!
把手机WiFi性能跳到最大,关闭每x分钟限制自动关闭;软件设置自动开启服务,关闭“运行时防止休眠”(它设计上有BUG,这个会导致服务10分钟就关闭),拉到启动目录设为自动启动。
OK!就是它了,我们写个程序包装一下,伪造为Web操作收发短信!
主要代码如下:
View Code
using System; using System.Collections.Generic; using System.Globalization; using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Web; using NewLife; using NewLife.Linq; using NewLife.Log; using NewLife.Web; namespace WMSms { /// <summary>手机类</summary> class Mobile : DisposeBase { #region 属性 private String _Address; /// <summary>地址</summary> public String Address { get { return _Address; } set { _Address = value; } } private Boolean _Logined; /// <summary>是否已登录</summary> public Boolean Logined { get { return _Logined; } set { _Logined = value; } } #endregion #region 构造 public Mobile(String address) { Address = address; } protected override void OnDispose(bool disposing) { base.OnDispose(disposing); if (client != null) client.Dispose(); } #endregion #region 通讯基础 private WebClientX client; void EnsureClient() { if (client != null) return; client = new WebClientX(true, true); client.Encoding = Encoding.UTF8; ServicePointManager.Expect100Continue = false; } String Get(String url) { EnsureClient(); //return client.DownloadString(url); var bts = client.DownloadData(url); return Encoding.UTF8.GetString(bts); } String Post(String url, String data) { EnsureClient(); client.Headers["Referer"] = url; client.Headers["Content-Type"] = "application/x-www-form-urlencoded"; //return client.UploadString(url, data); var bts = Encoding.UTF8.GetBytes(data); bts = client.UploadData(url, bts); return Encoding.UTF8.GetString(bts); } String Post(String url, IDictionary<String, Object> data) { if (data == null) return Post(url, ""); var sb = new StringBuilder(); foreach (var item in data) { if (sb.Length > 0) sb.Append("&"); if (item.Value != null) sb.AppendFormat("{0}={1}", item.Key, HttpUtility.UrlEncode("" + item.Value)); else sb.AppendFormat("{0}=", item.Key); } return Post(url, sb.ToString()); } #endregion #region 方法 public Boolean Login(String pass) { if (Logined) return true; var url = String.Format("http://{0}/authent/?returnurl=%2Ffs%2F", Address); var html = Get(url); url = String.Format("http://{0}/authent/?returnurl=%2Ffs%2F", Address); var dic = new Dictionary<String, Object>(); dic.Add("user", "Admin"); dic.Add("pwd", pass); html = Post(url, dic); var rs = !html.IsNullOrWhiteSpace() && html.Contains("form"); Logined = rs; return rs; } public Boolean SendMessage(String number, String content) { var url = String.Format("http://{0}/messaging/?page=newmessage", Address); var dic = new Dictionary<String, Object>(); dic.Add("RequestDeliveryReport", "chk"); dic.Add("recipient", number); dic.Add("recipientName", null); dic.Add("smsbody", content); var html = Post(url, dic); return !html.IsNullOrWhiteSpace() && html.Contains("已发信息"); } public Boolean Ping() { if (!Logined) return false; try { var url = String.Format("http://{0}/fs/", Address); var html = Get(url); if (html.IsNullOrWhiteSpace()) return false; // 如果回到登录页面,则标记为未登录 if (html.Contains("authent")) { Logined = false; return false; } return html.Contains("My Device"); } catch (Exception ex) { XTrace.WriteException(ex); return false; } } #endregion #region 接收短信 public List<Message> ReceiveUnReaded() { var list = Receive(0); if (list == null) return null; list = list.Where(m => !m.Readed).ToList(); var i = 1; var ls = list; while (ls.Count >= 10 && !ls[ls.Count - 1].Readed) { ls = Receive(i++); if (ls == null || ls.Count < 1) break; ls = ls.Where(m => !m.Readed).ToList(); list.AddRange(ls); if (list.Count >= 100) break; } return list; } public List<Message> ReceiveFromTime(DateTime start) { var list = Receive(0); if (list == null) return null; list = list.Where(m => m.Time > start).ToList(); var i = 1; var ls = list; while (ls.Count >= 10 && ls[ls.Count - 1].Time > start) { ls = Receive(i++); if (ls == null || ls.Count < 1) break; ls = ls.Where(m => m.Time > start).ToList(); list.AddRange(ls); if (list.Count >= 100) break; } return list; } static Regex _regMsg = new Regex("<div class=\"([^\"]*)\"><span class=\"msgDesc\">从 :</span>\\s*([^<]*?)\\s*-\\s*<a href=\"\\?page=newmessage&recipient=([^&\"]*)[^\"]*\">答复</a><br/><span class=\"msgDesc\">已收到 :</span>\\s*([^<]*?)\\s*<br/>\\s*([^<]*?)\\s*</div>", RegexOptions.IgnoreCase | RegexOptions.Compiled); public List<Message> Receive(Int32 page) { if (!Logined) return null; var url = String.Format("http://{0}/messaging/", Address); if (page > 1) url += "?page=" + page; var html = Get(url); if (html.IsNullOrWhiteSpace()) return null; var list = new List<Message>(); foreach (Match m in _regMsg.Matches(html)) { var msg = new Message(); msg.Readed = "readMsg".EqualIgnoreCase(m.Groups[1].Value); // 名字 var name = m.Groups[2].Value.Trim(); if (!name.IsNullOrWhiteSpace()) { name = HttpUtility.HtmlDecode(name); var p = name.IndexOf("("); if (p > 0) name = name.Substring(0, p).Trim(); msg.Name = name; } // 号码 var num = m.Groups[3].Value.Trim(); if (!num.IsNullOrWhiteSpace()) { num = HttpUtility.UrlDecode(num); if (num.StartsWith("+86")) num = num.Substring(3); msg.Number = num; } // 时间 var dt = m.Groups[4].Value.Trim(); if (!dt.IsNullOrWhiteSpace()) { dt = HttpUtility.HtmlDecode(dt); var p = dt.IndexOf("("); if (p > 0) dt = dt.Substring(0, p).Trim(); DateTime d; if (DateTime.TryParse(dt, out d)) msg.Time = d; else if (DateTime.TryParseExact(dt, "MM/dd/yy dddd HH:mm:ss", null, DateTimeStyles.None, out d)) msg.Time = d; } // 内容 var txt = m.Groups[5].Value.Trim(); if (!txt.IsNullOrWhiteSpace()) { msg.Content = HttpUtility.HtmlDecode(txt); } list.Add(msg); } return list; } public class Message { private String _Number; /// <summary>号码</summary> public String Number { get { return _Number; } set { _Number = value; } } private String _Name; /// <summary>名称</summary> public String Name { get { return _Name; } set { _Name = value; } } private String _Content; /// <summary>内容</summary> public String Content { get { return _Content; } set { _Content = value; } } private DateTime _Time; /// <summary>时间</summary> public DateTime Time { get { return _Time; } set { _Time = value; } } private Boolean _Readed; /// <summary>是否已读</summary> public Boolean Readed { get { return _Readed; } set { _Readed = value; } } } #endregion } }
刚下去小店买了一张移动动感地带的卡,50块办卡返50,分5月返还每月10元,套餐每月20元包400条短信,实际上,后面每个月只需要交10元即可!
配置一下路由器,把WM手机的Http端口映射到公网,可供远程应用系统直接调用!
现在很多可用的二手WM手机只需要一百多块钱即可买到。
这个方案跟短信猫不相上下,(短信猫成品三百多块),也算是多一种选择吧!
WM软件:WiFi.rar
源代码:WMSmsSrc.rar
我不相信神话,我只相信汗水!我不相信命运,我只相信双手!