综合现在能查到的免费短信发送方式,在飞信今年未升级前,大多是利用分析出的飞信客户端协议来发送短信,
优点是速度快,使用简单!
后来由于飞信协议的升级,以及验证码问题,大多的此类发送方式已经挂掉了,
网上也有关于利用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, 25 , false , 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 ;
}
}
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 代码,很简单,大家都能看的懂,下面贴下示例代码,然后我根据示例代码稍微解释下
{
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邮箱的短信提示稍有延迟! 接下来有时间,在试试其他邮箱,写的比较不自信,如有问题 希望大家多指教!