随笔 - 36  文章 - 0  评论 - 126 

综合现在能查到的免费短信发送方式,在飞信今年未升级前,大多是利用分析出的飞信客户端协议来发送短信,

优点是速度快,使用简单!

后来由于飞信协议的升级,以及验证码问题,大多的此类发送方式已经挂掉了,

网上也有关于利用google日历来变相进行短信发送的方法,

还有就是利用139邮箱,实际上不仅仅是139邮箱,189,yahoo等很多邮箱服务商都提供短信通知功能!

 

今天在首页又看到一篇文章利用139邮箱来进行短消息发送,随后自己测试了下189邮箱,下面将封装一个类来实现此功能

 

首先,在用smtp 发信测试的时候,发现 139,189等邮箱的smtp服务器不兼容.net smtp类的邮件发送,但是outlook等一些软件可以正常使用

 

关于.net smtpclient类不能发送139,189邮件服务器的问题也有人抓包分析了,是因为这2个邮件服务器对于一个命令解析的不兼容,在此不多说了!有兴趣可以google查下!

 

言规正传,既然这种变相的短信发送方式,是利用邮件服务商提供的一个新邮件短信提示功能,那么就是说我的支持短信提示的邮箱只要收到信邮件,就会由邮件服务商向

 

我们事先设置的手机发送新邮件提示, 139,189邮箱非别是移动和电信的手机邮箱,所以默认是跟手机账号关联的,yahoo邮箱则可以自行设置手机账号,具体的邮箱接收短信

 

的设置需要根据邮箱支持的功能自行设定,这里不多解释了,贴下189的短信提示设置:

 

 

 清楚了上面这层原理,那我们就直接来写代码了,这里说明下:为了简化操作,这里使用。net smtpclient来发信,

 

所以配置smtp发送的时候不支持的是无法使用的,其实配置一般的支持smtp发送邮件的就行,我会给出用QQ邮箱发信的测试用例! 

 

以下简单的封装,通过SmtpClient来给能够进行短信提示的邮箱发送新邮件,已达到变相短信提示目的!

  public class SmsAPI : IDisposable

    {
        
#region fields
        
/// <summary>
        
/// System.Net.Mail.SmtpClient对象
        
/// </summary>
        static SmtpClient _smtp;
        SmtpConfig _config;
        
/// <summary>
        
/// 是否已释放过资源
        
/// </summary>
        bool disponsed = false;
        
/// <summary>
        
/// 异步发送完成事件
        
/// </summary>
        event EventHandler<SendInfo> _event;
        
/// <summary>
        
/// 
        
/// </summary>
        object objectLock = new Object();
        
#endregion
#region delegates/events
        
/// <summary>
        
/// 异步发送完后触发的事件
        
/// </summary>
        public event EventHandler<SendInfo> SendCompleted
        {
            add
            {
                
lock (objectLock)
                {
                    _event 
+= value;
                }
            }
            remove
            {
                
lock (objectLock)
                {
                    _event 
-= value;
                }
            }
        }
#endregion
        
#region methods
        
/// <summary>
        
/// 构造函数
        
/// </summary>
        
/// <param name="xmlpath">Smtp的xml配置文件</param>
        public SmsAPI(string xmlpath)
        {
            
if (!File.Exists(xmlpath))
                
throw new FileNotFoundException("error:not found configxml file");
            
using (FileStream fs = new FileStream(xmlpath, FileMode.Open, FileAccess.Read))
            {
                XmlSerializer xs 
= new XmlSerializer(typeof(SmtpConfig));
                _config 
= xs.Deserialize(fs) as SmtpConfig;
                InitSmtp(_config.Host, _config.Port, _config.Sll, _config.User, _config.Password); 
/*初始化SMTPCLIENT对象*/
                fs.Close();
            }

        }
        
/// <summary>
        
/// 构造函数
        
/// </summary>
        
/// <param name="host">SMTP服务器</param>
        
/// <param name="user">SMTP用户名</param>
        
/// <param name="password">SMTP密码</param>
        public SmsAPI(string host, string user, string password)
            : 
this(host, 25false, user, password)
        {

        }
        
/// <summary>
        
/// 构造函数
        
/// </summary>
        
/// <param name="host">SMTP服务器</param>
        
/// <param name="port">SMTP端口</param>
        
/// <param name="ssl">是否使用SLL</param>
        
/// <param name="user">SMTP用户名</param>
        
/// <param name="password">SMTP密码</param>
        public SmsAPI(string host, int port, bool ssl, string user, string password)
        {
            InitSmtp(host, port, ssl, user, password);   
/*初始化SMTPCLIENT对象*/

        }
        
/// <summary>
        
/// 初始化SMTP对象
        
/// </summary>
        
/// <param name="host">SMTP服务器</param>
        
/// <param name="port">SMTP端口</param>
        
/// <param name="ssl">是否使用SLL</param>
        
/// <param name="user">SMTP用户名</param>
        
/// <param name="password">SMTP密码</param>
        private void InitSmtp(string host, int port, bool ssl, string user, string password)
        {
            
if (host == null || host.Length == 0)
                
throw new ArgumentNullException("error:host is null");
            
if (port < 0 || port > 65534)
                
throw new ArgumentException("error:port (0~65534)");
            _smtp 
= new SmtpClient();
            _smtp.SendCompleted 
+= new SendCompletedEventHandler(SendCompletedProc);//设置SmtpClient对象的SendCompleted事件
            _smtp.Host = host;
            _smtp.Port 
= port;
            _smtp.EnableSsl 
= ssl;
            _smtp.Credentials 
= new NetworkCredential(user, password);

            _config 
= new SmtpConfig(host, port, ssl, user, password);//实例一个SmtpConfig对象
        }
        
/// <summary>
        
/// SmtpClient.SendCompleted 事件的绑定函数
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>
        void SendCompletedProc(object sender,AsyncCompletedEventArgs e)
        {
            SendInfo info 
= e.UserState as SendInfo;
            info.Ex 
= e.Error;//这里设置info对象的Ex属性
            if(_event!=null)
                _event(
null, info); //事件投递给上层调用
        }
        
/// <summary>
        
/// 实现IDisponse接口Disponse方法
        
/// </summary>
        public void Dispose()
        {
            Disponse(
true);
            GC.SuppressFinalize(
this);
        }
        
/// <summary>
        
/// 资源释放
        
/// </summary>
        
/// <param name="disponseing"></param>
        protected virtual void Disponse(bool disponseing)
        {
            
if (!disponsed)
            {
                
if (disponseing)
                {
                    
//释放受控资源
                    _smtp = null;
                    _config 
= null;
                }
                
//释放非受控资源
            }
            disponsed 
= true;
        }
        
/// <summary>
        
/// 发送邮件
        
/// </summary>
        
/// <param name="_mail">邮件对象</param>
        public void Send(MailMessage _mail)
        {
            
try
            {   
                _smtp.SendAsync(_mail, 
new SendInfo(_mail.To[0].ToString(), DateTime.Now,null));
            }
            
catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        
/// <summary>
        
/// 发送邮件
        
/// </summary>
        
/// <param name="from">发件人</param>
        
/// <param name="to"><收件人/param>
        
/// <param name="subject">主题</param>
        
/// <param name="body">正文</param>
        public void Send(string from, string to, string subject, string body)
        {
            
try
            {

                MailMessage _mail 
= new MailMessage();
                _mail.From 
= new MailAddress(from);
                _mail.To.Add(
new MailAddress(to));
                _mail.Subject 
= subject;
                _mail.IsBodyHtml 
= true;
                _mail.Body 
= body;
                _smtp.SendAsync(_mail, 
new SendInfo(to, DateTime.Now,null));
            }
            
catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

        }
        
/// <summary>
        
/// get appdomain base directory
        
/// </summary>
        
/// <returns></returns>
        public static string AppDirectory()
        {
            
return AppDomain.CurrentDomain.BaseDirectory;
        }
        
/// <summary>
        
/// XML序列化SmtpConfig对象
        
/// </summary>
        
/// <param name="fileName">文件名</param>
        public void SaveConfig(string fileName)
        {
            
if (fileName == null || fileName.Length == 0)
                fileName 
= "smsapi.xml";
            
string xmlpath = Path.Combine(AppDirectory(), fileName);
            
using(StreamWriter sw=new StreamWriter(xmlpath,false,System.Text.Encoding.Default))
            {
                XmlSerializer xs 
= new XmlSerializer(typeof(SmtpConfig));
                xs.Serialize(sw, _config);
                sw.Close();
            }
        }
        
#endregion

    }
    
/// <summary>
    
/// 发送信息
    
/// </summary>
    public class SendInfo:System.EventArgs
    {
        
public SendInfo(string _to, DateTime _time,Exception _ex)
        {
            
this.To = _to;
            
this.Time = _time;
            
this.Ex = _ex;
        }
        
/// <summary>
        
/// 接收用户
        
/// </summary>
        public string To
        {
            
get;
            
set;
        }
        
/// <summary>
        
/// 发送时间
        
/// </summary>
        public DateTime Time
        {
            
get;
            
set;
        }
        
/// <summary>
        
/// 发送产生的异常
        
/// </summary>
        public Exception Ex
        { 
           
get;
           
set;
        }
    }

 

 

 

SmtpConfig
 [Serializable]
    
public class SmtpConfig
    {
        
public SmtpConfig()
        { 
        }
        
/// <summary>
        
/// 构造函数
        
/// </summary>
        
/// <param name="host">SMTP服务器</param>
        
/// <param name="port">SMTP端口</param>
        
/// <param name="ssl">是否启用SSL</param>
        
/// <param name="user">用户名</param>
        
/// <param name="password">密码</param>
        public SmtpConfig(string host,int port,bool ssl,string user,string password)
        {
            
this.Host = host;
            
this.Port = port;
            
this.Sll = ssl;
            
this.User = user;
            
this.Password = password;
        }
        
string _host = "";
        
/// <summary>
        
/// SMTP服务器
        
/// </summary>
        public string Host
        {
            
get{return _host;}
            
set
            {
                
if (value == null || value.Length == 0)
                    
throw new ArgumentNullException("error:host is null");
                _host 
= value;
            }
        }
        
int _port = 25;
        
/// <summary>
        
/// SMTP端口
        
/// </summary>
        public int Port
        {
            
get { return _port; }
            
set
            {
                
if (value < 0 || value > 65534)
                    
throw new ArgumentException("error:port (0~65534)");
                _port 
= value;
            }
        }
        
/// <summary>
        
/// 是否启用SSL
        
/// </summary>
        public bool Sll
        {
            
get;
            
set;
        }
        
/// <summary>
        
/// 用户名
        
/// </summary>
        public string User
        {
            
get;
            
set;
        }
        
/// <summary>
        
/// 用户密码
        
/// </summary>
        public string Password
        {
            
get;
            
set;
        }
    }

 

 

 

上面是封装的SmsApi 代码,很简单,大家都能看的懂,下面贴下示例代码,然后我根据示例代码稍微解释下

 

SmsAPI api = new SmsAPI("smtp.qq.com""发送账号""发送账号的密码");
            {
                api.SendCompleted 
+= new EventHandler<SendInfo>((obj, e) =>
                {
                    Console.WriteLine(
"to:{0}  time={1}", e.To, e.Time);
                    
if (e.Ex != null)//如果发送出了异常,是在事件的绑定函数里可以体现出来的,
                    {
                        Console.WriteLine(
"exception:{0}", e.Ex);
                    }
                    
                });
                api.SaveConfig(
"sms.xml"); //xml序列化保存Smtp配置
                api.Send(
"****@qq.com""1533651****@189.cn""测试邮件""这是一封测试邮件");

            }

 

 

 需要解释的有3部分,

1,对象的创建:重载了3个构造函数,分别需要传递的参数在代码中写的很明白, 大家可以看下,

    在构造参数传递后进行 SmtpClient对象的初始化

2,发送邮件: 重载了2个函数,都是最简单的邮件发送,也是。net SmtpClient 最基本的操作,但是这里使用了异步操作,这样做的原因下一点说

3, 邮件发送完成的事件:这里是在SmsApi类自定义一个事件SendCompleted,当SmtpClient对象异步发送邮件后触发给上层调用的,这样我们上层调用的时候只管发送邮件,最后发    送成功还是发送异常都可以在事件绑定的方法里反映出来

 

 

以上代码经测试,完全OK, 只是189邮箱的短信提示稍有延迟! 接下来有时间,在试试其他邮箱,写的比较不自信,如有问题 希望大家多指教!

posted on 2010-09-01 13:29 LiangHu 阅读(6338) 评论(26) 编辑 收藏

 回复 引用 查看   
#1楼 2010-09-01 13:53 | Vseen[ Aloner ]      
弱弱的问。短信提示,有什么用?
 回复 引用 查看   
#2楼[楼主] 2010-09-01 13:57 | %admin      
引用Vseen[ Aloner ]:弱弱的问。短信提示,有什么用?

用处太大了,监控软件中,报警时候要给用户发送短信的,
以前我们项目中也是用 飞信的免费api,后来挂掉后,买了一个sp商的短信发送接口,但是一条1毛钱, 有时候如果监测数据异常太多,一晚上可能要发好几千条,这费用也不小的,
今天实现的这利用 邮件服务商对新邮件的短信提醒功能来发送短信,暂时还不会用在实际使用中,有待考验的!

 回复 引用 查看   
#3楼 2010-09-01 14:08 | 超级奶崽      
飞信的现在不能了?飞信的需要添加好友吧
 回复 引用 查看   
#4楼 2010-09-01 14:15 | 贪食蛇      
赞一个
 回复 引用 查看   
#5楼 2010-09-01 14:15 | 瓦尔登      
我就是支持你来的~~继续努力,哈哈
 回复 引用 查看   
#6楼 2010-09-01 14:24 | Vseen[ Aloner ]      
@%admin
邮箱的内容,他也会发送到手机上?

 回复 引用 查看   
#7楼 2010-09-01 14:28 | Vseen[ Aloner ]      
@%admin
嘿嘿。我看了一下。有个疑惑,那如果有10个客户,就要他们用手机注册139?

 回复 引用 查看   
#8楼 2010-09-01 14:34 | undefined      
太太太~~~~

无耻~了~

=。=

 回复 引用 查看   
#9楼 2010-09-01 14:57 | Kevin Zou      
買一個短信貓,應該還蠻簡單的
 回复 引用 查看   
#10楼[楼主] 2010-09-01 15:46 | %admin      
引用Vseen[ Aloner ]:
@%admin
嘿嘿。我看了一下。有个疑惑,那如果有10个客户,就要他们用手机注册139?


嗯,其实问题还是蛮多的,就像139邮箱只支持 移动
189邮箱只支持电信
yahoo短信不像新用户申请了,

对于.net smtpclient 还有写问题就是 同步发送要有发送时间, 异步发送的话,也必须一个发送完,再进行下一个异步

 回复 引用 查看   
#11楼[楼主] 2010-09-01 15:47 | %admin      
@Kevin Zou
短信猫 和 sp商接口 都属于收费的发短信,呵呵,暂不讨论,这方面做起来也不难,利用 西门子手机可以 32口com 口进行短信收发,

 回复 引用 查看   
#12楼[楼主] 2010-09-01 15:48 | %admin      
引用Vseen[ Aloner ]:
@%admin
邮箱的内容,他也会发送到手机上?



我只试了试 189邮箱,短信发送方式要设置的, 我设置的长短信
所以接收到的是所有信件内容,分2条接收的, 接收完毕有个 [结束] 这么一个标志

 回复 引用 查看   
#13楼 2010-09-01 16:32 | 马老虎      
免费毕竟是免费,正式的使用中还是使用短信猫划算点。
 回复 引用 查看   
#14楼 2010-09-01 19:59 | Allen Zhang      
我倒是对如何分析飞信协议的技术有兴趣,呵呵。
 回复 引用 查看   
#15楼 2010-09-01 20:51 | niaoren      
前段时间我做了一个发送注册激化码邮件的,是gmail的,我想知道163等邮箱的端口号是多少啊?gmail是587
 回复 引用 查看   
#16楼 2010-09-01 23:33 | 易木      
很好很强大,能否提供源码下载供学习研究啊。
或单独发一份给我,e-mail:watchdb@126.com,谢谢了!

 回复 引用 查看   
#17楼[楼主] 2010-09-01 23:45 | %admin      
引用易木:
很好很强大,能否提供源码下载供学习研究啊。
或单独发一份给我,e-mail:watchdb@126.com,谢谢了!


上文就是全部代码,包括测试代码, 代码根本就是很简单,简单的smtp 发信,这篇文章揭示的是利用130 189 yahoo等邮件服务提高的邮件短信提醒来变相达到免费发送短信的目地

 回复 引用 查看   
#18楼 2010-09-02 08:07 | Artech      
那么可以写一个发短信的手机软件来买,呵呵。
 回复 引用 查看   
#19楼 2010-09-02 08:37 | virus      
但是网站运营商也不是傻逼吧,时间长了,你的频率过高,肯定会封掉你的,不信你就试一试
 回复 引用 查看   
#20楼 2010-09-02 09:02 | Leon Weng      
我之前写过一个软件就是定时提醒到gmail,然后设置了转发到139的记事软件,呵呵,不过这个时间长了怕邮箱被封啊。而且QQ邮箱现在每个月有20条免费短信可以做提醒使用。
 回复 引用 查看   
#21楼 2010-09-02 12:00 | skyzhou      
很有创意 我就没想到
 回复 引用 查看   
#22楼 2010-09-02 16:01 | 陈晨      
之前用过139的,自己玩玩可以,别商用
 回复 引用 查看   
#23楼 2010-09-03 10:11 | P_Chou      
Mark!有想法!
 回复 引用 查看   
#24楼 2010-09-04 22:48 | 易木      
@%admin
嗯,知道了!

 回复 引用 查看   
#25楼 2010-09-21 15:32 | __器      
你做过旺旺协议的分析吗,想向你请教下这方面的问题,我的QQ是:1181265848
 回复 引用 查看   
#26楼 2010-10-10 18:33 | 阿斯兰      
楼主公司买的短信太贵了吧,我们用的也就几分钱一条啊!