一些系統,需要能夠發送短信,量很小,平均每日10條。
運營商平台太貴,白名單很嚴格,小額只能發省內;
各短信平台有各種限制,大事件前后會關閉;
飛信以前可以用WAP發,但是現在多了驗證碼,搞不定了;
短信貓買來還得寫控制程序,價錢和時間上……
手上有兩部廢棄的WM手機,沒有故障,只是老舊不好用!
有一個WM軟件(后面有下載),它在WM手機上開啟一個Http服務器,使得我們可以在PC上通過瀏覽器直接訪問!
有收發短信的功能!
把手機WiFi性能跳到最大,關閉每x分鍾限制自動關閉;軟件設置自動開啟服務,關閉“運行時防止休眠”(它設計上有BUG,這個會導致服務10分鍾就關閉),拉到啟動目錄設為自動啟動。
OK!就是它了,我們寫個程序包裝一下,偽造為Web操作收發短信!
主要代碼如下:

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