2023年2月

在前面的一些文章中,有介绍过DotNet内置SMTP类的邮件发送功能,附件、嵌入图片的模式都有介绍,本文继续介绍Lumisoft.NET这个非常优秀的开源组件,用该组件来设计开发邮件工具,将变得更加方便,功能更加强大。网上很多文章基本介绍如何使用该组件来收取邮件较多,较少介绍使用该组件做邮件发送功能的。本文主要探寻使用该组件实现邮件的发送功能,邮件发送有两种方式,一种是不用发件人即可发送邮件,一种是使用发件人账户密码和SMTP服务器来实现邮件发送的,本文分别对这两种方式进行介绍。

组件下载地址:
http://www.lumisoft.ee/lswww/download/downloads/

组件论坛地址:
http://www.lumisoft.ee/Forum/default.aspx?g=forum

秉承一贯的做法,先贴出相关的实现图形,感官认识下,在进入详细的介绍说明,以求达到最好的理解深度。

1、 首先是发件人的设置,可以从文本文件的导出,以及新建等操作,以方便用户操作。



2、 内容也支持导入导出,并且保存到数据库,方便进行记录及操作等,另外可以对内容进行随机混淆,混淆的内容在HTML邮件中式隐藏的,方便糊弄一下服务器的识别。


3、 邮件发送可以选择两种方式,下面将分别介绍这两种方式的实现,一种采用该控件封装非常好的邮件直投技术,不需要SMTP账号发送的;一种是普通的SMTP发送方式。当然我们还可以设置更多的参数,例如邮件尾部信息、HTML内容提示、 以及一些发送期间自动拨号的设置操作等。


4、 邮件直投技术,通过模拟账户来附加用户的邮件地址(或者可以成为伪装)。其中我填写了一些常用的SMTP服务器的域名,方便在其中构造合乎要求的邮件格式,还可以设置邮件回执通知,如下图所示。



5、 如果是采用普通发送方式,那么就需要制定用户的账号密码等信息,发送的时候,自动从启动获取发件人信息进行批量发送操作。


6、 最后体验一下少量邮件的发送效果,发送采用多线程发送,多线程采用比较有名的SmartThreadPool组件,并且发送过程总详细记录其中的日志,供参考。

介绍完毕相关的功能效果图,下面我们来分析下主要功能实现的代码:


代码


private
TimerHelper timer
=

null
;


private

void
btnSend_Click(
object
sender, EventArgs e)
{

//
重置计数变量


failedItems
=

0
;
successItems

=

0
;

workItemsCompleted

=

0
;
workItemsGenerated

=

0
;

Portal.gc.FailedCount

=

0
;
//
重置失败次数



STPStartInfo stpStartInfo

=

new
STPStartInfo();
stpStartInfo.IdleTimeout

=

10
;
stpStartInfo.MaxWorkerThreads

=

100
;
stpStartInfo.MinWorkerThreads

=

0
;

//
stpStartInfo.StartSuspended = true;


_smartThreadPool
=

new
SmartThreadPool(stpStartInfo);
_workItemsGroup

=
_smartThreadPool;

workItemsProducerThread

=

new
Thread(
new
ThreadStart(
this
.WorkItemsProducer));
workItemsProducerThread.IsBackground

=

true
;
workItemsProducerThread.Start();

RefreshStatusCount();


int
intervalRedial
=
SystemConfig.Default.IntervalRedial
*

1000

*

60
;

if
(intervalRedial
>

0
)
{

if
(timer
!=

null
)
{
timer.Stop();
timer.Dispose();
}
timer

=

new
TimerHelper(intervalRedial,
false
);
timer.Execute

+=

new
TimerHelper.TimerExecution(timer_Execute);
timer.Start();
}
}


private

static

object
locker
=

new

object
();

private

void
timer_Execute()
{

if
(Monitor.TryEnter(locker))
{

string
message
=

string
.Format(
"
在时间 {0} 时刻执行了一次重拨号操作!
"
, DateTime.Now);
ShowSendStatus(message);


string
RasName
=
SystemConfig.Default.RasName;

if
(
!
string
.IsNullOrEmpty(RasName))
{
message

=

string
.Format(
"
正在准备重新拨号({0})
"
, RasName);
ShowSendStatus(message);

Portal.gc.ReConnect(RasName);
Portal.gc.FailedCount

=

0
;
//
重新归零


}

Monitor.Exit(locker);
}

else

{
Monitor.Enter(locker);
Monitor.Exit(locker);
}
}

上面是主要的任务生成操作以及相关的拨号操作,其中任务详细的生成代码如下所示。


private

void
WorkItemsProducer()

{
CallCtrlWithThreadSafetyEx.SetText(

this
.txtSendDetail,
""
);

EnableControl(

false
,
true
,
true
);

string
message
=

string
.Format(
"
任务开始
"
);
RecordMessage(message);


#region
生成任务


IWorkItemsGroup workItemsGroup

=
_workItemsGroup;

if
(
null

==
workItemsGroup)
{

return
;
}

List

<
string
>
addressList
=
GetAddressList();
List

<
MyMailInfo
>
mailInfoList
=
GetMailInfo();

for
(
int
i
=

0
; i
<
addressList.Count; i
++
)
{

try

{
SendJobInfo jobInfo

=

new
SendJobInfo();
jobInfo.domainList

=
mailDomainList;
jobInfo.mailTo

=
addressList[i];
jobInfo.mailInfo

=
GetOneMail(mailInfoList, i);
jobInfo.ShowSendStatus

=
ShowSendStatus;
jobInfo.currentDomain

=
(i
%
mailDomainList.Count);
//
设置一个标志,默认那个账户开始发送


jobInfo.UseDirectSendType
=
SystemConfig.Default.EmailDirectSend;


//
如果用户未指定发送账号,那么采用默认的显示名称

//
如果为空,发送的时候,会自动采用邮件地址作为显示名称



if
(
string
.IsNullOrEmpty(SystemConfig.Default.UserEmailFrom))
{
jobInfo.mailFromDisplay

=
SystemConfig.Default.DefaultFromDisplayName;
}

workItemCallback

=

new
WorkItemCallback(
this
.DoWork);
workItemsGroup.QueueWorkItem(workItemCallback, jobInfo);
Thread.Sleep(

100
);
}

catch
(ObjectDisposedException ex)
{
LogTextHelper.WriteLine(ex.ToString());

continue
;
}

Interlocked.Increment(

ref
workItemsGenerated);
}


#endregion


RefreshStatusCount();
message

=

string
.Format(
"
共有 {0} 个任务,还剩下 {1} 个
"
,
workItemsGenerated, workItemsGenerated

-
workItemsCompleted);
CallCtrlWithThreadSafetyEx.SetText(

this
, message);
RecordMessage(message);


try

{

//
workItemsGroup.Start();


workItemsGroup.WaitForIdle();
_smartThreadPool.Shutdown();
}

catch
(Exception ex)
{
LogTextHelper.WriteLine(ex.ToString());
}

UpdateFinishStatus();
}

由于采用了多线程来处理,所以停止发送的时候,需要把相关的线程对象进行释放,如下代码所示。


代码


private

void
btnStop_Click(
object
sender, EventArgs e)
{

try

{
_smartThreadPool.Shutdown();
_smartThreadPool.Dispose();
_smartThreadPool

=

null
;


if
(timer
!=

null
)
{
timer.Stop();
timer.Dispose();
}
}

catch
(Exception ex)
{
LogTextHelper.WriteLine(ex.ToString());
}

UpdateFinishStatus();
}

其中具体的邮件发送功能封装在SendJobInfo中,通过判断不同的类型,进行不同的发送操作。

其中最为关键的发送代码,就是如何利用LumiSoft.NET组件来构造相应的邮件对象,下面先先介绍下邮件直投的发送方式,由于该组件封装比较好,直投发送方式很简单:

Mail_Message message
=
Create_PlainText_Html_Attachment_Image(mailTo, mailFrom, mailFromDisplay);

SMTP_Client.QuickSend(message);

其中
Create_PlainText_Html_Attachment_Image的封装函数详细内容如下所示:



代码


private
Mail_Message Create_PlainText_Html_Attachment_Image(
string
mailTo,
string
mailFrom,
string
mailFromDisplay)
{
Mail_Message msg

=

new
Mail_Message();
msg.MimeVersion

=

"
1.0
"
;
msg.MessageID

=
MIME_Utils.CreateMessageID();
msg.Date

=
DateTime.Now;
msg.From

=

new
Mail_t_MailboxList();
msg.From.Add(

new
Mail_t_Mailbox(mailFromDisplay, mailFrom));
msg.To

=

new
Mail_t_AddressList();
msg.To.Add(

new
Mail_t_Mailbox(mailTo, mailTo));
msg.Subject

=
mailInfo.Title;


//
设置回执通知



string
notifyEmail
=
SystemConfig.Default.DispositionNotificationTo;

if
(
!
string
.IsNullOrEmpty(notifyEmail)
&&
ValidateUtil.IsEmail(notifyEmail))
{
msg.DispositionNotificationTo

=

new
Mail_t_Mailbox(notifyEmail, notifyEmail);
}


#region
MyRegion


//
--- multipart/mixed -----------------------------------


MIME_h_ContentType contentType_multipartMixed
=

new
MIME_h_ContentType(MIME_MediaTypes.Multipart.mixed);
contentType_multipartMixed.Param_Boundary

=
Guid.NewGuid().ToString().Replace(
'
-
'
,
'
.
'
);
MIME_b_MultipartMixed multipartMixed

=

new
MIME_b_MultipartMixed(contentType_multipartMixed);
msg.Body

=
multipartMixed;


//
--- multipart/alternative -----------------------------


MIME_Entity entity_multipartAlternative
=

new
MIME_Entity();
MIME_h_ContentType contentType_multipartAlternative

=

new
MIME_h_ContentType(MIME_MediaTypes.Multipart.alternative);
contentType_multipartAlternative.Param_Boundary

=
Guid.NewGuid().ToString().Replace(
'
-
'
,
'
.
'
);
MIME_b_MultipartAlternative multipartAlternative

=

new
MIME_b_MultipartAlternative(contentType_multipartAlternative);
entity_multipartAlternative.Body

=
multipartAlternative;
multipartMixed.BodyParts.Add(entity_multipartAlternative);


//
--- text/plain ----------------------------------------


MIME_Entity entity_text_plain
=

new
MIME_Entity();
MIME_b_Text text_plain

=

new
MIME_b_Text(MIME_MediaTypes.Text.plain);
entity_text_plain.Body

=
text_plain;


//
普通文本邮件内容,如果对方的收件客户端不支持HTML,这是必需的



string
plainTextBody
=

"
如果你邮件客户端不支持HTML格式,或者你切换到“普通文本”视图,将看到此内容
"
;

if
(
!
string
.IsNullOrEmpty(SystemConfig.Default.PlaintTextTips))
{
plainTextBody

=
SystemConfig.Default.PlaintTextTips;
}

text_plain.SetText(MIME_TransferEncodings.QuotedPrintable, Encoding.UTF8, plainTextBody);
multipartAlternative.BodyParts.Add(entity_text_plain);


//
--- text/html -----------------------------------------



string
htmlText
=
mailInfo.Content;
//
"<html>这是一份测试邮件,<img src=\"cid:test.jpg\">来自<font color=red><b>LumiSoft.Net</b></font></html>";


MIME_Entity entity_text_html
=

new
MIME_Entity();
MIME_b_Text text_html

=

new
MIME_b_Text(MIME_MediaTypes.Text.html);
entity_text_html.Body

=
text_html;
text_html.SetText(MIME_TransferEncodings.QuotedPrintable, Encoding.UTF8, htmlText);
multipartAlternative.BodyParts.Add(entity_text_html);


//
--- application/octet-stream -------------------------



foreach
(
string
attach
in
mailInfo.Attachments)
{
multipartMixed.BodyParts.Add(Mail_Message.CreateAttachment(attach));
}


foreach
(
string
imageFile
in
mailInfo.EmbedImages)
{
MIME_Entity entity_image

=

new
MIME_Entity();
entity_image.ContentDisposition

=

new
MIME_h_ContentDisposition(MIME_DispositionTypes.Inline);

string
fileName
=
DirectoryUtil.GetFileName(imageFile,
true
);
entity_image.ContentID

=
BytesTools.BytesToHex(Encoding.Default.GetBytes(fileName));
MIME_b_Image body_image

=

new
MIME_b_Image(MIME_MediaTypes.Image.jpeg);
entity_image.Body

=
body_image;
body_image.SetDataFromFile(imageFile, MIME_TransferEncodings.Base64);
multipartMixed.BodyParts.Add(entity_image);
}


#endregion



return
msg;
}

如果使用普通的账号方式发送SMTP邮件,主要代码如下所示,其中可以看出是利用了命令方式一步步和服务器进行交互的。


using
(SMTP_Client client
=

new
SMTP_Client())
{

int
port
=
domainInfo.Ssl
?
WellKnownPorts.SMTP_SSL : WellKnownPorts.SMTP;

if
(domainInfo.Port
>

0
)
{
port

=
domainInfo.Port;
}

client.Connect(domainInfo.SmtpServer, port, domainInfo.Ssl);
client.Authenticate(domainInfo.Username, domainInfo.Password);

//
string text = client.GreetingText;


client.MailFrom(mailFrom,
-
1
);
client.RcptTo(mailTo);

MemoryStream stream

=
Create_Html_Attachment_Image(mailTo, mailFrom, mailFromDisplay);
client.SendMessage(stream);
client.Disconnect();
}

其中构造邮件内容的代码和刚才的部分类似,详细代码如下所示。


private
MemoryStream Create_Html_Attachment_Image(
string
mailTo,
string
mailFrom,
string
mailFromDisplay)
{
Mime m

=

new
Mime();
MimeEntity mainEntity

=
m.MainEntity;

mainEntity.From

=

new
AddressList();
mainEntity.From.Add(

new
MailboxAddress(mailFromDisplay, mailFrom));

mainEntity.To

=

new
AddressList();
mainEntity.To.Add(

new
MailboxAddress(mailTo, mailTo));
mainEntity.Subject

=
mailInfo.Title;
mainEntity.ContentType

=
MediaType_enum.Multipart_mixed;

MimeEntity textEntity

=
mainEntity.ChildEntities.Add();
textEntity.ContentType

=
MediaType_enum.Text_html;
textEntity.ContentTransferEncoding

=
ContentTransferEncoding_enum.QuotedPrintable;
textEntity.DataText

=
mailInfo.Content;
.........................

MemoryStream msg

=

new
MemoryStream();
m.ToStream(msg);
msg.Position

=

0
;


return
msg;
}

利用Lumisoft.NET这个组件,可以实现很多相关的邮件操作,这里介于兴趣及篇幅原因,主要介绍邮件发送的功能模块,其中贴出的代码,一个是为了和感兴趣的朋友相互交流,一个也是为了自己今后做一个借鉴,并不鼓励大家用此软件或者代码来大批量发送垃圾邮件。

本文介绍两个QQ应用中有意思的小功能,都是基于邮件对象实现的功能,一个是利用.NET 内置类MailMessage来实现日志发送功能,一个是利用Lumisoft.NET组件类来检测用户是否开通了SMTP功能。

想看看两个功能的实现效果。





编辑并发送QQ日志的代码很简单,就是简单利用MailMessage对象来实现的,类似发送邮件一样,详细代码如下所示:


try

{
MailMessage mail

=

new
MailMessage();
mail.From

=

new
MailAddress(
string
.Format(
"
{0}@qq.com
"
, Portal.gc.QQ));
mail.To.Add(

new
MailAddress(
string
.Format(
"
{0}@qzone.qq.com
"
, Portal.gc.QQ)));

string
title
=

this
.txtTitle.Text;

if
(
!
string
.IsNullOrEmpty(title))
{
title

=

string
.Format(
"
[{0}]{1}
"
,
this
.txtCategory.Text,
this
.txtTitle.Text);
}
mail.Subject

=
title;
mail.Body

=

this
.txtContent.Text;
SmtpClient client

=

new
SmtpClient(
"
smtp.qq.com
"
,
25
);
client.Credentials

=

new
NetworkCredential(Portal.gc.QQ, Portal.gc.QQPass);
client.Send(mail);

MessageExUtil.ShowTips(

"
QQ留言发表成功!
"
);

this
.txtTitle.Clear();

this
.txtContent.Clear();
}

catch
(Exception ex)
{
LogTextHelper.WriteLine(ex.ToString());
MessageExUtil.ShowError(ex.Message);
}

批量检查开通SMTP功能的代码如下所示:


private

void
StartAtClick(
object
obj)
{

if
(
this
.dg.Rows.Count
==

0

||

this
.dg.SelectedRows.Count
==

0
)
{
SetTips(

"
请先输入资料,并选定一个开始
"
);

return
;
}


string
selectQQ
=

""
;

foreach
(DataGridViewRow row
in

this
.dg.SelectedRows)
{
selectQQ

=
row.Cells[
"
账号
"
].Value.ToString();

break
;
}

QQDict.Clear();
QQList.Clear();

foreach
(DataGridViewRow row
in

this
.dg.Rows)
{

string
qq
=
row.Cells[
"
账号
"
].Value.ToString();

string
pass
=
row.Cells[
"
密码
"
].Value.ToString();

if
(
!
string
.IsNullOrEmpty(qq)
&&

!
QQDict.ContainsKey(qq))
{
QQDict.Add(qq, pass);
QQList.Add(qq);
}
}


int
current
=
QQList.IndexOf(selectQQ);

if
(QQDict.Count
>

0

&&
current
>=

0
)
{
isStart

=

true
;
currentIndex

=
current;
StartQQAt();
}
}


private

void
StartQQAt()
{

if
(
!
isStart
||
QQDict.Count
==

0
)

return
;


if
(currentIndex
>=
QQDict.Count)
{
isStart

=

false
;
SetTips(

"
已经是最后一个QQ
"
);

return
;
}


string
qq
=
QQList[currentIndex];

string
pass
=
QQDict[qq];

if
(
!
string
.IsNullOrEmpty(qq))
{
CallCtrlWithThreadSafety.SetText(

this
.lblOperateTips,
string
.Format(
"
正在处理QQ[{0}]...
"
, qq),
this
);
CallCtrlWithThreadSafety.SetText(

this
.txtQQNumber, qq,
this
);
Application.DoEvents();
Thread.Sleep(

100
);

CheckSmtpOpen(qq, pass);
}
}


private

void
CheckSmtpOpen(
string
user,
string
pass)
{

bool
success
=
CheckLogin(user, pass);

if
(success)
{
SetTips(

string
.Format(
"
QQ用户[{0}] 测试成功
"
, user));


if
(
!
SuccessList.Contains(user))
{
SuccessList.Add(user);

if
(failedList.Contains(user))
{
failedList.Remove(user);

//
移除成功的数据


}
UpdateList();
}
}

else

{

if
(
!
failedList.Contains(user))
{
failedList.Add(user);
UpdateList();
}
SetTips(

string
.Format(
"
QQ用户[{0}] 测试失败,可能密码不正确或未开通SMTP
"
, user));
}

Interlocked.Increment(

ref
currentIndex);
StartQQAt();
}


private

bool
CheckLogin(
string
user,
string
pass)
{

bool
result
=

false
;

using
(SMTP_Client client
=

new
SMTP_Client())
{

try

{

//
匹配使之可以用于其它邮箱



string
smtp
=

"
smtp.qq.com
"
;

int
atIndex
=
user.IndexOf(
"
@
"
);

if
(atIndex
>

0
)
{
smtp

=

"
smtp.
"

+
user.Substring(atIndex
+

1
);
}

if
(user.Contains(
"
@163.com
"
))
{
user

=
user.Replace(
"
@163.com
"
,
""
);
}

client.Connect(smtp, WellKnownPorts.SMTP_SSL,

true
);
client.Authenticate(user, pass);

string
helloText
=
client.GreetingText;

if
(helloText.Contains(
"
220
"
))
{
result

=

true
;
}
}

catch
(Exception ex)
{
;
}
}

return
result;
}


private

void
SetTips(
string
tips)
{
CallCtrlWithThreadSafetyEx.SetText(

this
.lblOperateTips, tips);
CallCtrlWithThreadSafetyEx.SetText(

this
.tsslTipsLable, tips);
Application.DoEvents();
Thread.Sleep(

20
);
}


private

void
UpdateList()
{

try

{

this
.lstFailed.Invoke(
new
MethodInvoker(
delegate
()
{

this
.lstFailed.Items.Clear();

foreach
(
string
item
in
failedList)
{

this
.lstFailed.Items.Add(item);
}

this
.lstFailed.Refresh();
}));

this
.lstSuccess.Invoke(
new
MethodInvoker(
delegate
()
{

this
.lstSuccess.Items.Clear();

foreach
(
string
item
in
SuccessList)
{

this
.lstSuccess.Items.Add(item);
}

this
.lstSuccess.Refresh();
}));
}

catch
(Exception ex)
{
LogTextHelper.WriteLine(ex.ToString());
}
}

其中利用了Lumisoft.NET的SMTP_Client对象来检测是否服务器应答的内容,如果含有220就表示支持SMTP了,呵呵,是不是很简单呢,其实上面的代码最为重要的就是下面这部分而已。

using
(SMTP_Client client
=

new
SMTP_Client())
{

try

{

//
匹配使之可以用于其它邮箱



string
smtp
=

"
smtp.qq.com
"
;

int
atIndex
=
user.IndexOf(
"
@
"
);

if
(atIndex
>

0
)
{
smtp

=

"
smtp.
"

+
user.Substring(atIndex
+

1
);
}

if
(user.Contains(
"
@163.com
"
))
{
user

=
user.Replace(
"
@163.com
"
,
""
);
}

client.Connect(smtp, WellKnownPorts.SMTP_SSL,

true
);
client.Authenticate(user, pass);

string
helloText
=
client.GreetingText;

if
(helloText.Contains(
"
220
"
))
{
result

=

true
;
}
}

catch
(Exception ex)
{
;
}
}


其他部分就是为了实现一个循环机批量的过程而已。本随便介绍的功能,都已经集成的我的软件QQ搜通天系列软件中了,需要了解可以下载软件测试下,有宝贵意见可以相互沟通交流。

由于项目的需要,需要使用SNMP来进行相关的开发,需要在我的程序中利用SNMP的Trap协议给指定的系统程序发送相关的设备数据信息, 使得其系统能够监控到设备的最新信息以及状态,对方只是提供了一个Java的例子,而我的程序是C#开发的,因此对这方面的知识进行了解学习,完成了相关的功能,本文有感于此,对SNMP方面做了一些开发总结,以求树碑到此一游,独乐不如众乐,邀兴趣之士猎奇探秘.

首先介绍标题的几个问题,SNMP是什么,snmpsharpnet又是什么,开发能解决什么问题?

SNMP是什么呢?

简单来说,SNMP主要就是一种特定的网络管理协议,用来获取设备相关信息的一种一些,复杂来讲,就很讲究了,我不在长篇大量介绍重复的内容,需要了解可以参考下面几篇博文:

SNMP介紹及命令

SNMP基础简介

snmpsharpnet又是什么呢?

snmpsharpnet是基于C#开发一个开源组件,主要是为了方便使用SNMP协议而生,支持各种SNMP各种版本、各种指令的一个优秀组件。其官方网站是:
http://www.snmpsharpnet.com/
,里面有很多C#使用例子及相关介绍。

有人也应用它来做了一些小应用,如博客:
c#开发snmp应用

这类开发能解决什么问题呢?

简单来讲,可以开发相关设备管理及监控的程序。

为了使用SNMP来进行调试及开发,需要在操作系统中安装SNMP服务(默认系统没有安装),安装和普通的安装Window组件一样的步骤,选定简单网络管理协议组件即可,安装后,在服务中启动SNMP服务即可,如下所示:

虽然启动了服务,不过要顺利访问,还需要对系统的社区名称及可以访问的IP地址做配置,否则一样无法取得计算机的相关设备信息,配置对话框是双击服务,打开属性对话框进行配置即可。


利用SNMPSharpNet组件来开发SNMP服务应用,省却了很多复杂的组装操作,非常方便,如获取指定机器的基本信息,可以通过


private

void
button1_Click(
object
sender, EventArgs e)

{

//
SNMP community name


OctetString community
=

new
OctetString(
"
public
"
);

AgentParameters param

=

new
AgentParameters(community);
param.Version

=
SnmpVersion.Ver1;
IpAddress agent

=

new
IpAddress(
"
192.168.0.50
"
);


//
Construct target


UdpTarget target
=

new
UdpTarget((IPAddress)agent,
161
,
2000
,
1
);


//
Pdu class used for all requests


Pdu pdu
=

new
Pdu(PduType.Get);
pdu.VbList.Add(

"
1.3.6.1.2.1.1.1.0
"
);
//
sysDescr


pdu.VbList.Add(
"
1.3.6.1.2.1.1.2.0
"
);
//
sysObjectID


pdu.VbList.Add(
"
1.3.6.1.2.1.1.3.0
"
);
//
sysUpTime


pdu.VbList.Add(
"
1.3.6.1.2.1.1.4.0
"
);
//
sysContact


pdu.VbList.Add(
"
1.3.6.1.2.1.1.5.0
"
);
//
sysName


//
Make SNMP request


SnmpV1Packet result
=
(SnmpV1Packet)target.Request(pdu, param);


if
(result
!=

null
)
{

//
ErrorStatus other then 0 is an error returned by

//
the Agent - see SnmpConstants for error definitions



if
(result.Pdu.ErrorStatus
!=

0
)
{

//
agent reported an error with the request



this
.txtContent.Text
+=

string
.Format(
"
Error in SNMP reply. Error {0} index {1} \r\n
"
,
result.Pdu.ErrorStatus,
result.Pdu.ErrorIndex);
}

else

{

//
Reply variables are returned in the same order as they were added

//
to the VbList



this
.txtContent.Text
+=

string
.Format(
"
sysDescr({0}) ({1}): {2} \r\n
"
,
result.Pdu.VbList[

0
].Oid.ToString(), SnmpConstants.GetTypeName(result.Pdu.VbList[
0
].Value.Type),
result.Pdu.VbList[

0
].Value.ToString());

this
.txtContent.Text
+=

string
.Format(
"
sysObjectID({0}) ({1}): {2} \r\n
"
,
result.Pdu.VbList[

1
].Oid.ToString(), SnmpConstants.GetTypeName(result.Pdu.VbList[
1
].Value.Type),
result.Pdu.VbList[

1
].Value.ToString());

this
.txtContent.Text
+=

string
.Format(
"
sysUpTime({0}) ({1}): {2} \r\n
"
,
result.Pdu.VbList[

2
].Oid.ToString(), SnmpConstants.GetTypeName(result.Pdu.VbList[
2
].Value.Type),
result.Pdu.VbList[

2
].Value.ToString());

this
.txtContent.Text
+=

string
.Format(
"
sysContact({0}) ({1}): {2} \r\n
"
,
result.Pdu.VbList[

3
].Oid.ToString(), SnmpConstants.GetTypeName(result.Pdu.VbList[
3
].Value.Type),
result.Pdu.VbList[

3
].Value.ToString());

this
.txtContent.Text
+=

string
.Format(
"
sysName({0}) ({1}): {2} \r\n
"
,
result.Pdu.VbList[

4
].Oid.ToString(), SnmpConstants.GetTypeName(result.Pdu.VbList[
4
].Value.Type),
result.Pdu.VbList[

4
].Value.ToString());
}
}

else

{

this
.txtContent.Text
+=

string
.Format(
"
No response received from SNMP agent. \r\n
"
);
}
target.Dispose();
}

运行后可以显示指定机器的基本信息,如下所示:

sysDescr(1.3.6.1.2.1.1.1.0) (OctetString): Hardware: x86 Family 6 Model 26 Stepping 5 AT/AT COMPATIBLE - Software: Windows Version 5.2 (Build 3790 Multiprocessor Free)
sysObjectID(1.3.6.1.2.1.1.2.0) (ObjectId): 1.3.6.1.4.1.311.1.1.3.1.2
sysUpTime(1.3.6.1.2.1.1.3.0) (TimeTicks): 46d 4h 14m 2s 320ms
sysContact(1.3.6.1.2.1.1.4.0) (OctetString):
sysName(1.3.6.1.2.1.1.5.0) (OctetString): TCC-TX

又例如我们可以通过SNMP命令来监控磁盘的空间大小,例子代码如下所示:



代码


private

void
button7_Click(
object
sender, EventArgs e)
{

double
[] diskstorage1, diskstorage2;

diskstorage1

=
snmpget(
"
127.0.0.1
"
,
"
public
"
,
1
);

this
.txtContent.Text
+=
Environment.NewLine;
diskstorage2

=
snmpget(
"
192.168.101.81
"
,
"
gci_RW
"
,
2
);

//
foreach (double dou in diskstorage1)

//
{

//
this.txtContent.Text += string.Format("{0}  GB \r\n", dou.ToString("f2"));

//
}


}


double
[] snmpget(
string
ipaddress,
string
comname,
int
i)
{


double
cvolumn
=

0
, dvolumn
=

0
, cvolunmn1
=

0
, dvolumn1
=

0
, cvolumn2
=

0
, dvolumn2
=

0
;

double
[] backup
=

new

double
[
6
];

//
SNMP community name


OctetString community
=

new
OctetString(comname);


//
Define agent parameters class


AgentParameters param
=

new
AgentParameters(community);

//
Set SNMP version to 1 (or 2)


param.Version
=
(
int
)SnmpVersion.Ver1;

//
Construct the agent address object

//
IpAddress class is easy to use here because

//
it will try to resolve constructor parameter if it doesn't

//
parse to an IP address


IpAddress agent
=

new
IpAddress(ipaddress);


//
Construct target


UdpTarget target
=

new
UdpTarget((IPAddress)agent,
161
,
2000
,
2
);


//
Pdu class used for all requests


Pdu pdu
=

new
Pdu(PduType.Get);

//
区分两台服务器的硬盘mib代码



if
(i
==

2
)
{
pdu.VbList.Add(

"
1.3.6.1.2.1.25.2.3.1.5.2
"
);
//
硬盘C盘簇数


pdu.VbList.Add(
"
1.3.6.1.2.1.25.2.3.1.5.3
"
);
//
硬盘D盘簇数


pdu.VbList.Add(
"
1.3.6.1.2.1.25.2.3.1.6.2
"
);
//
硬盘C盘已用簇数


pdu.VbList.Add(
"
1.3.6.1.2.1.25.2.3.1.6.3
"
);
//
硬盘D盘已用簇数


pdu.VbList.Add(
"
1.3.6.1.2.1.25.2.3.1.4.2
"
);
//
硬盘C盘分配单元


pdu.VbList.Add(
"
1.3.6.1.2.1.25.2.3.1.4.3
"
);
//
硬盘D盘分配单元


}

else

if
(i
==

1
)
{
pdu.VbList.Add(

"
1.3.6.1.2.1.25.2.3.1.5.1
"
);
//
硬盘C盘簇数


pdu.VbList.Add(
"
1.3.6.1.2.1.25.2.3.1.5.2
"
);
//
硬盘D盘簇数


pdu.VbList.Add(
"
1.3.6.1.2.1.25.2.3.1.6.1
"
);
//
硬盘C盘已用簇数


pdu.VbList.Add(
"
1.3.6.1.2.1.25.2.3.1.6.2
"
);
//
硬盘D盘已用簇数


pdu.VbList.Add(
"
1.3.6.1.2.1.25.2.3.1.4.1
"
);
//
硬盘C盘分配单元


pdu.VbList.Add(
"
1.3.6.1.2.1.25.2.3.1.4.2
"
);
//
硬盘D盘分配单元


}

SnmpV1Packet result

=

new
SnmpV1Packet();


//
Make SNMP request


result
=
(SnmpV1Packet)target.Request(pdu, param);


//
If result is null then agent didn't reply or we couldn't parse the reply.



if
(result
!=

null
)
{

//
ErrorStatus other then 0 is an error returned by

//
the Agent - see SnmpConstants for error definitions



if
(result.Pdu.ErrorStatus
!=

0
)
{

//
agent reported an error with the request



this
.txtContent.Text
+=

string
.Format(
"
Error in SNMP reply. Error {0} index {1} \r\n
"
,
result.Pdu.ErrorStatus,
result.Pdu.ErrorIndex);
}

else

{

//
Reply variables are returned in the same order as they were added

//
to the VbList




int
a
=

int
.Parse(result.Pdu.VbList[
0
].Value.ToString());

int
b
=

int
.Parse(result.Pdu.VbList[
1
].Value.ToString());

int
c
=

int
.Parse(result.Pdu.VbList[
2
].Value.ToString());

int
d
=

int
.Parse(result.Pdu.VbList[
3
].Value.ToString());

int
num1
=

int
.Parse(result.Pdu.VbList[
4
].Value.ToString());

int
num2
=

int
.Parse(result.Pdu.VbList[
5
].Value.ToString());
cvolumn

=
(
double
)a
*
num1
/

1024

/

1024

/

1024
;
dvolumn

=
(
double
)b
*
num2
/

1024

/

1024

/

1024
;
cvolunmn1

=
(
double
)c
*
num1
/

1024

/

1024

/

1024
;
dvolumn1

=
(
double
)d
*
num2
/

1024

/

1024

/

1024
;
cvolumn2

=
cvolumn
-
cvolunmn1;
dvolumn2

=
dvolumn
-
dvolumn1;
backup[

0
]
=
cvolumn;
backup[

1
]
=
dvolumn;
backup[

2
]
=
cvolunmn1;
backup[

3
]
=
dvolumn1;
backup[

4
]
=
cvolumn2;
backup[

5
]
=
dvolumn2;

this
.txtContent.Text
+=

string
.Format(
"
c盘空间{0} GB \r\n
"
, cvolumn.ToString(
"
f2
"
));

this
.txtContent.Text
+=

string
.Format(
"
d盘空间{0} GB \r\n
"
, dvolumn.ToString(
"
f2
"
));

this
.txtContent.Text
+=

string
.Format(
"
c盘已用空间{0} GB \r\n
"
, cvolunmn1.ToString(
"
f2
"
));

this
.txtContent.Text
+=

string
.Format(
"
d盘已用空间{0} GB \r\n
"
, dvolumn1.ToString(
"
f2
"
));

this
.txtContent.Text
+=

string
.Format(
"
c盘剩余空间{0} GB \r\n
"
, cvolumn2.ToString(
"
f2
"
));

this
.txtContent.Text
+=

string
.Format(
"
d盘剩余空间{0} GB \r\n
"
, dvolumn2.ToString(
"
f2
"
));
}
}

else

{

this
.txtContent.Text
+=

string
.Format(
"
No response received from SNMP agent. \r\n
"
);
}
target.Close();


return
backup;
}

出来的结果就是显示各计算机的磁盘信息,如下所示:

c盘空间97.
66
GB
d盘空间73.

25
GB
c盘已用空间36.

61
GB
d盘已用空间31.

00
GB
c盘剩余空间61.

05
GB
d盘剩余空间42.

25
GB

c盘空间48.

83
GB
d盘空间57.

19
GB
c盘已用空间1.

70
GB
d盘已用空间6.

68
GB
c盘剩余空间47.

13
GB
d盘剩余空间50.

51
GB

另外利用SNMP可以发送约定的Trap协议到指定的计算机上,从而实现两个计算机上的交互操作,Trap协议可以发送多种数据类型,如字符类型、整形、日期类型、OID类型等信息,发送Trap协议比较简单,如下所示:


int
i
=

0
;


private

void
button8_Click(
object
sender, EventArgs e)
{
TrapAgent agent

=

new
TrapAgent();
VbCollection col

=

new
VbCollection();


//
连接状态 设备连接状态(0:通信正常 1:通信故障)

//
工作温度

//
告警描述



string
desc
=

string
.Format(
"
测试Trap内容
"
);
col.Add(

new
Oid(
"
.1.3.6.1.4.1.22014.99.2.1.6.2.1.1.1
"
),
new
Integer32(
0
));
col.Add(

new
Oid(
"
.1.3.6.1.4.1.22014.99.2.1.6.2.1.1.2
"
),
new
Integer32(
30
));
col.Add(

new
Oid(
"
.1.3.6.1.4.1.22014.99.2.1.6.2.4.1.1
"
),
new
OctetString(Encoding.Default.GetBytes(desc)));


//
Send the trap to the localhost port 162



string
hostIp
=

"
127.0.0.1
"
;

string
community
=

"
public
"
;
agent.SendV2Trap(

new
IpAddress(hostIp),
162
, community,

13433
,
new
Oid(
"
.1.3.6.1.6.3.1.1.5
"
), col);
i

++
;
}

通过接受数据的控制台,我们能可以查看到Trap协议接受到的情况,如下所示:

由于SNMP方面的应用应该很少人涉及到,因此对多数人来说,还是比较神秘的东西,本文抛砖引玉,希望与大家一起讨论学习。

在上篇随笔《
如何使用C#实现QQ号码的申请
》有介绍如何利用C#实现QQ号码的批量申请功能,其实腾讯还提供了一个注册QQ邮箱伴随有QQ号码的注册方式,就是QQ邮箱的注册,QQ邮箱的批量注册,在很多场合需要用到,如注册淘宝账号,或者用来发送邮件,做营销加群,然后发送群邮件等等操作,注册QQ邮箱的界面如下图所示:

相对来说,QQ邮件的注册会比较容易一些,腾讯没有那么多变态古怪的加密处理方式,是比较正常的POST操作处理。

关键申请代码如下所示:

postData.AppendFormat(
"
email={0}&nick={0}
"
, randomUser, randomUser.Length
>

10

?
randomUser.Substring(
10
) : randomUser);
postData.AppendFormat(

"
&age=1993&age_month=8&age_day=23&regsex=1
"
);
postData.AppendFormat(

"
&password_1={0}&password_2={0}
"
, qqDefaultPass);
postData.AppendFormat(

"
&Country=6&State=26&City=7
"
);
postData.AppendFormat(

"
&validecode={0}&regqqmail=1&asdfg={1}&
"
,
this
.txtVerifyCode.Text, asdfg);


string
tempUrl
=

"
http://emailreg.qq.com/cgi-bin/signup/srv_tj?type=54796&seed=0.
"

+
GetRandomNumber();

string
temp
=
httpHelper.GetHtml(tempUrl, cookieReg, tempUrl);
Thread.Sleep(

100
);


string
content
=
httpHelper.GetHtml(postUrl, cookieReg, postData.ToString(),
true
, refer);

if
(content.Contains(
"
恭喜您,申请成功
"
))
{

string
qq
=
httpHelper.GetHiddenKeyValue(content,
"
uin
"
);

re

=

new
Regex(
"
您申请的Email帐号为:<strong>(?<email>.*?)</strong>
"
, RegexOptions.IgnoreCase
|
RegexOptions.Singleline
|
RegexOptions.IgnorePatternWhitespace);
mc

=
re.Match(content);

string
email
=

""
;

if
(mc.Success)
{
email

=
mc.Groups[
1
].Value;
}

SuccessCount

++
;
//
成功计数


AddToList(
string
.Format(
"
qq:{0} email:{1}
"
, qq, email));
SetTips(

string
.Format(
"
恭喜您,申请到QQ邮件:{0} Email:{1}
"
, qq, email));


int
redialCount
=
Convert.ToInt32(
this
.txtReDailCount.Value);

if
(redialCount
>

0

&&
(SuccessCount
%
redialCount
==

0
))
{
btnRedail_Click(

null
,
null
);
//
重新拨号


}
}

else

{

string
error
=

"
申请失败,请重试
"
;

if
(content.IndexOf(
"
此IP申请的操作过于频繁
"
)
>

0
)
{
error

=

"
此IP申请的操作过于频繁
"
;
}

else

if
(content.IndexOf(
"
验证码错误
"
)
>

0
)
{
error

=

"
验证码错误
"
;
}
SetTips(error);
}

软件实现的效果如下所示:

腾讯一般一天一个IP只是允许注册几个号码,如果太过频繁,容易被封锁一段时间,这个时候,就可以利用快速拨号方式,重新更换IP地址,然后又是好汉一条,非常酷爽,哈哈。

网络步入了营销的时代,营销则进入精准、细化的操作阶段,QQ用户群的分类则是很多进行精准营销的企业和个人紧盯着的一块肥肉。与普通进行大面积邮件发送营销的不同,QQ群邮件的发送成本很低,一次发送,就可以使得群里面成千上百的人都收到邮件,比普通邮件发送的模式和准确率都好很多。用手工登录QQ邮箱发送邮件是一种无奈的选择,效率并不高,如果利用软件自动识别发送群,并能批量发送群邮件,那么效率将会提高很多,不过利用QQ进行群邮件的发送,也并非一件容易的事情,本文就是探讨这块的实现,抛砖引玉,希望大家一起进行学习探讨。

由于邮箱是需要登录的,我们首先需要模拟邮箱的登录,如下图所示

QQ的群,不是你创建的群,就能发送,类似爱情买卖那首歌:“
爱情不是你想卖 想买就能卖“,腾讯做法是,”群邮不是你想发,想发就能发的“,呵呵。

呵呵,言归正传,我们登录邮箱,发送群邮件的时候,就知道这样的情况了,群只有对方运行发送,并且你加入到对方的群,这样才可以发送邮件的,我们看看网页界面:

打开QQ群列表,我们看到他会列出一些可以发送邮件的群列表,如下所示:

这样,我们模拟这个操作,先把账号可以发送的群列表及相关信息获取出来,如下图所示:

获取这些信息,我们就完成了一个较为重要的步骤,剩下的就是发送群邮件的操作了。

看了上面的分析,我们来具体探讨下C#源码的实现,首先需要获取到能发送群邮件的群相关信息,具体的操作如下函数所示:


string
id32
=

""
;

string
id32Value
=

""
;

private

void
GetCanSendMailQun()
{

//
引用地址



string
refUrl
=

string
.Format(
"
http://m411.mail.qq.com/cgi-bin/frame_html?sid={0}&r={1}
"
, Portal.gc.LoginInfo.SID, Portal.gc.LoginInfo.R);

//
可发送邮件的群列表信息



string
url
=

string
.Format(
"
http://m411.mail.qq.com/cgi-bin/grouplist?sid={0}&t=compose_group&gid=&s=from_mail_list
"
, Portal.gc.LoginInfo.SID);

httpHelper.Encoding

=
Encoding.GetEncoding(
"
gb18030
"
);

string
content
=
httpHelper.GetHtml(url, Portal.gc.EmailCookie, refUrl);

//
获取群列表并解析



string
itemRegex
=

"
<option\\s*id=\
"
(
?<
gname
>
.
*?
)\
"
\\s*value=\
"
(
?<
gid
>
.
*?
)\
"
\\s*>\\s*(?<name>.*?)\\s*</option>
"
;
Regex re

=

new
Regex(itemRegex, RegexOptions.IgnoreCase
|
RegexOptions.Singleline
|
RegexOptions.IgnorePatternWhitespace);
Match mc

=
re.Match(content);

if
(mc.Success)
{
MatchCollection mcs

=
re.Matches(content);

this
.txtQunList.Items.Clear();

foreach
(Match me
in
mcs)
{

string
strName
=
me.Groups[
"
name
"
].Value;

string
strEmail
=
me.Groups[
"
gid
"
].Value;
CListItem item

=

new
CListItem(strName, strEmail);

this
.txtQunList.Items.Add(item);
}
}


//
取得一个动态构造的Hidden参数的名称和值,该参数名和值都是采用32位MD5编码,所以比较容易拿到


itemRegex
=
"
<input\\s*id=\
"
(
?<
id
>
.{
32
})\
"
\\stype=\
"
hidden\
"
\\s*name=\
"
(
?<
name
>
.{
32
})\
"
\\s*value=\
"
(
?<
value
>
.{
32
})\
"
/>
"
;
re

=

new
Regex(itemRegex, RegexOptions.IgnoreCase
|
RegexOptions.Singleline
|
RegexOptions.IgnorePatternWhitespace);
mc

=
re.Match(content);

if
(mc.Success)
{
id32

=
mc.Groups[
"
id
"
].Value;
id32Value

=
mc.Groups[
"
value
"
].Value;
}
}

以上操作就是获取群列表的功能代码,剩下的我们继续跟踪如何发送群邮箱的,发送群邮件的操作和一般的POST方式不同,他是采用了一种特别的方式进行处理,也就是发送MultiPart内容方式。下面我发送了一份宣传的邮件,监测到提交的字符列表如下所示:



代码

-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="sid"

ZwcM6rV_qNv5eiRp
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="actiontype"

send
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="bigattachcnt"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="priority"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="contenttype"

html
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="savesendbox"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="sendname"

alias
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="qqshow"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="fmailid"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="cattachelist"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="fattachlist"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="rsturl"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="fileidlist"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="verifykey"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="verifycode"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="verifycode_cn"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="t"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="s"

group
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="contenttext"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="hitaddrbook"

0
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="backurl"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="newwin"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="mailtype"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="sid"

ZwcM6rV_qNv5eiRp
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="21a3f85bcf259a6e1f6f4b31e469b701"

dae08a1893ae318e4e65fb9784596ee1
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="groupname"

武汉人在北京创业群
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="qqgroupid"

20328002@groupmail.qq.com
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="subject"

给大家介绍一个营销好帮手,QQ搜通天企业版
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="votesubject"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="option"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="option"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="option"


-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="content__html"


<
DIV
><
SPAN
id
=picture
><
A
href
="http://www.iqidi.com/Download/QQEnterprise1.png"
><
IMG
height
=756
alt
=QQ搜通天企业版
src
="http://www.iqidi.com/Download/QQEnterprise1.png"
width
=925
border
=0
></
A
></
SPAN
><
BR
></
DIV
>


<
DIV
>
★软件功能
<
BR
>

<
SPAN
style
="MARGIN-LEFT: 16px; LINE-HEIGHT: 150%; MARGIN-RIGHT: 16px"
>
QQ搜通天企业版2010,是一款可以批量申请QQ号码、搜索采集QQ空间QQ号码信息、按地区批量采集QQ号码、QQ群号码采集、批量添加QQ群、个人QQ群成员号码、QQ校友查询、空间日志查看发表、QQ批量留言等,以及邮件群发的软件。该软件通过选择筛选条件,可以在腾讯QQ空间查询来自何地、年龄、性别、状态、职业 等条件的QQ用户号码及相关信息,并支持按地区自动批量导出QQ数据;可以根据关键字及搜索类型采集查询相关QQ群信息;批量添加群;导出群成员等功能,方便寻找共同爱好的组织和进行业务群锁定推广工作。
</
SPAN
><
BR
><
SPAN
style
="MARGIN-LEFT: 16px; LINE-HEIGHT: 150%; MARGIN-RIGHT: 16px"
>
该软件集成邮件群发功能,采用不用发件人信息可发送邮件的
<
SPAN
class
=style17
><
STRONG
><
FONT
color
=#aa0000
>
邮件直投技术
</
FONT
></
STRONG
></
SPAN
>

<
SPAN
class
=style17
><
STRONG
><
FONT
color
=#aa0000
>
普通发送方式结合
</
FONT
></
STRONG
></
SPAN
>
,非常方便向QQ用户发送邮件,也可以发送其他邮件地址的邮件,软件采用多线程发送邮件,有效利用您的机器资源,发送过程不影响您的其他软件操作。
</
SPAN
><
BR
><
BR
>
QQ搜通天企业版2010,支持查找QQ空间的QQ用户数据和日志空间信息,方便您寻找某些方面的QQ用户日志,娱乐与学习共存,休闲和交友并进。
<
BR
>
QQ搜通天企业版2010,独有的QQ按地区自动化批量导出QQ数据,非常方便您大量获取相关条件下的QQ号码等信息,根据方便您寻找商业目标客户,迅速推广您的产品。
<
BR
>
QQ搜通天企业版2010,支持QQ群资料搜索采集、QQ群批量添加和群成员号码采集,能够快速查找相关的QQ号码和QQ群资料,为您的业务推广提供准确的群体,也为您的兴趣找到合适的组织。
<
BR
>
QQ搜通天企业版2010,可以快速发表、查看您自己及其他用户的日志,可以批量给QQ用户快速留言,方便您了解学习其他人的文章并作出相应的留言回复,也可以做宣传使用。
<
BR
>
QQ搜通天企业版2010,支持查询QQ校友录相关的用户数据,非常方便您寻找您黄金岁月的校友及同桌伙伴。
<
BR
>
QQ搜通天企业版2010,采用多线程发送邮件,有效利用您的机器资源,发送过程不影响您的其他软件操作;发送的邮件可以保存起来,可以方便的时候调出来进行发送,邮件发送过程会记录发送历史,可以设置跳过重复发送的邮件,对发送未成功的邮件,可以在再次发送。软件界面美观大方,方便使用。
<
BR
>


<
P
>
1.本软件QQ数据全部来自于“QQ空间-找朋友”、“QQ城市达人”、“QQ群”、“QQ校友录”等腾讯官方频道。
<
BR
>
2.本软件重要功能之一是搜索QQ用户和QQ群,不过他不同于QQ本身自带的搜索功能,这款软件可以使你获得更多信息,可以很方便的使你交到理想的朋友。
<
BR
>
3.本软件另外的重要功能就是可以群发邮件,可以给搜索到的QQ用户发送邮件,也可以发送给其他Email邮箱群发邮件,非常实用方便。
<
BR
>
4.本软件还适用于一些网络推广人群,可以直接做推广使用,通过提供采集号码服务和邮件群发功能,您可以快速方便进行网络信息的推广。
<
BR
><
BR
></
P
>


<
P
>
★系统需求
</
P
>


<
P
>
QQ搜通天企业版2010 使用C# 2.0开发,是一款CS架构的软件, 适运行在 Microsoft WindowsNT/2000/XP/2003 等平台,但必须安装有.Net 2.0平台.
</
P
>


<
P
>
该软件利用了微软.NET Framework2.0优秀的框架,因此在安装软件前,您需要花费一点时间来安装下面的组件(请您按照顺序安装即可):
</
P
>


<
P
>
(1)MicroSoft .NET Framework 2.0 官方下载地址:
<
BR
><
A
href
="http://www.microsoft.com/downloads/info.aspx?na=90&amp;p=&amp;SrcDisplayLang=zh-cn&amp;SrcCategoryId=&amp;SrcFamilyId=0856eacb-4362-4b0d-8edd-aab15c5e04f5&amp;u=http://download.microsoft.com/download/5/6/7/567758a3-759e-473e-bf8f-52154438565a/dotnetfx.exe"
>
http://www.microsoft.com/downloads/info.aspx?na=90
&amp;
p=
&amp;
SrcDisplayLang=zh-cn
&amp;
SrcCategoryId=
&amp;
SrcFamilyId=0856eacb-4362-4b0d-8edd-aab15c5e04f5
&amp;
u=http%3a%2f%2fdownload.microsoft.com%2fdownload%2f5%2f6%2f7%2f567758a3-759e-473e-bf8f-52154438565a%2fdotnetfx.exe
</
A
></
P
>


<
P
>
(2)最后下载 QQ搜通天企业版2010,进行安装即完成整个软件的安装。安装地址为:
<
FONT
face
=宋体
>
&nbsp;
<
SPAN
id
=lblDownUrl
><
A
href
="http://www.iqidi.com/Download/qqenterprise.rar"
>
http://www.iqidi.com/Download/qqenterprise.rar
</
A
></
SPAN
></
FONT
><
BR
></
P
></
DIV
>

-----------------------------7dbda2110d091a--


发送邮件的代码如下所示:


private

void
btnSendQunMail_Click(
object
sender, EventArgs e)

{
CListItem item

=

this
.txtQunList.SelectedItem
as
CListItem;

if
(item
!=

null
)
{

string
refurl
=

string
.Format(
"
http://m411.mail.qq.com/cgi-bin/grouplist?sid={0}&t=compose_group&gid=&s=from_mail_list
"
, Portal.gc.LoginInfo.SID);

string
sendUrl
=

string
.Format(
"
http://m411.mail.qq.com/cgi-bin/groupmail_send?sid={0}
"
, Portal.gc.LoginInfo.SID);


#region
构造邮件内容

NameValueCollection col

=

new
NameValueCollection();
col.Add(

"
sid
"
, Portal.gc.LoginInfo.SID);
col.Add(

"
actiontype
"
,
"
send
"
);
col.Add(

"
bigattachcnt
"
,
""
);
col.Add(

"
priority
"
,
""
);
col.Add(

"
contenttype
"
,
"
html
"
);
col.Add(

"
savesendbox
"
,
""
);
col.Add(

"
sendname
"
,
"
alias
"
);
col.Add(

"
qqshow
"
,
""
);
col.Add(

"
fmailid
"
,
""
);
col.Add(

"
cattachelist
"
,
""
);
col.Add(

"
fattachlist
"
,
""
);
col.Add(

"
rsturl
"
,
""
);
col.Add(

"
fileidlist
"
,
""
);
col.Add(

"
verifykey
"
,
""
);
col.Add(

"
verifycode
"
,
""
);
col.Add(

"
verifycode_cn
"
,
""
);
col.Add(

"
t
"
,
""
);
col.Add(

"
s
"
,
"
group
"
);
col.Add(

"
contenttext
"
,
""
);
col.Add(

"
hitaddrbook
"
,
"
0
"
);
col.Add(

"
backurl
"
,
""
);
col.Add(

"
newwin
"
,
""
);
col.Add(

"
mailtype
"
,
""
);

//
col.Add("sid", Portal.gc.LoginInfo.SID);


col.Add(id32, id32Value);
col.Add(

"
groupname
"
, item.Text);
col.Add(

"
qqgroupid
"
, item.Value);
col.Add(

"
subject
"
,
"
给大家介绍一个营销好帮手,QQ搜通天企业版
"
);
col.Add(

"
votesubject
"
,
""
);

//
col.Add("option", "");

//
col.Add("option", "");


col.Add(
"
option
"
,
""
);
col.Add(

"
content__html
"
,
"
<DIV><SPAN id=picture><A href=\
"
http:
//
www.iqidi.com/Download/QQEnterprise1.png\"><IMG height=756 alt=QQ搜通天企业版 src=\"
http://www.iqidi.com/Download/QQEnterprise1.png
\" width=925 border=0></A></SPAN><BR></DIV>");




#endregion


string
content
=
HttpPostData(sendUrl, col, refurl);

if
(content.Contains(
"
验证码错误
"
))
{
MessageUtil.ShowWarning(

"
验证码错误
"
);
}
}
}

其中
HttpPostData关键函数就是封装发送MultiPart内容的代码,主要是组装各种键值的数据。


private

static

string
HttpPostData(
string
url, NameValueCollection stringDict,
string
referer)
{

string
responseContent
=

""
;
var memStream

=

new
MemoryStream();
var webRequest

=
(HttpWebRequest)WebRequest.Create(url);

//
边界符


var boundary
=

"
---------------------------
"

+
DateTime.Now.Ticks.ToString(
"
x
"
);

//
边界符


var beginBoundary
=
Encoding.ASCII.GetBytes(
"
--
"

+
boundary
+

"
\r\n
"
);

//
最后的结束符


var endBoundary
=
Encoding.ASCII.GetBytes(
"
--
"

+
boundary
+

"
--\r\n
"
);


//
设置属性


webRequest.Method
=

"
POST
"
;
webRequest.Accept

=

"
image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
"
;
webRequest.Referer

=
referer;
webRequest.ContentType

=

"
multipart/form-data; boundary=
"

+
boundary;
webRequest.UserAgent

=

"
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET4.0C; .NET4.0E; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; InfoPath.2)
"
;

//
webRequest.Timeout = timeOut;


//
写入字符串的Key


var stringKeyHeader
=

"
\r\n--
"

+
boundary
+


"
\r\nContent-Disposition: form-data; name=\
"
{
0
}\
""

+


"
\r\n\r\n{1}\r\n
"
;


try

{

foreach
(
string
key
in
stringDict.Keys)
{

string
formitem
=

string
.Format(stringKeyHeader, key, stringDict[key]);

byte
[] formitembytes
=
Encoding.GetEncoding(
"
gb18030
"
).GetBytes(formitem);
memStream.Write(formitembytes,

0
, formitembytes.Length);
}


//
写入最后的结束边界符


memStream.Write(endBoundary,
0
, endBoundary.Length);
webRequest.ContentLength

=
memStream.Length;
webRequest.CookieContainer

=
Portal.gc.EmailCookie;

var requestStream

=
webRequest.GetRequestStream();

memStream.Position

=

0
;
var tempBuffer

=

new

byte
[memStream.Length];
memStream.Read(tempBuffer,

0
, tempBuffer.Length);
memStream.Close();

requestStream.Write(tempBuffer,

0
, tempBuffer.Length);
requestStream.Close();

var httpWebResponse

=
(HttpWebResponse)webRequest.GetResponse();

using
(var httpStreamReader
=

new
StreamReader(httpWebResponse.GetResponseStream(),
Encoding.GetEncoding(

"
gb18030
"
)))
{
responseContent

=
httpStreamReader.ReadToEnd();
}


//
fileStream.Close();


httpWebResponse.Close();
webRequest.Abort();
}

catch
(Exception ex)
{
LogHelper.Error(ex);
}


return
responseContent;
}

完成上面的步骤,发送邮件就差不多了,检测到的内容和在QQ邮件网站发送的内容差不多,就是Cookie的内容有一些差异,不过不知道是什么原因,一直发送不成功,返回的HTML内容提示CGIExeption,可能是哪里出了问题吧,需要继续探讨。

希望各位提供思路,进一步解决这个问题,下面是具体的说明信息:



代码

<!--
cgi exception
-->
<!
DOCTYPE html
>
<
html
><
head
><
meta
http-equiv
="Content-Type"
content
="text/html; charset=gb18030"

/><
script
>
window.gbIsNoCheck
=

true
;
</
script
><
script
>

document.domain

=
"
qq.com
"
;

function
getTop()
{

var
_oSelfFunc
=
arguments.callee;

if
(
!
_oSelfFunc._moTop)
{

try
{_oSelfFunc._moTop
=
window
!=
parent
?
(parent.getTop
?
parent.getTop():parent.parent.getTop()):window;}
catch
(_oError){_oSelfFunc._moTop
=
window;}}

return
_oSelfFunc._moTop;
}

try
{window.top
=
getTop();}
catch
(e){eval(
"
var top=getTop();
"
);}

var
gsTest
=

"
<br/>,-1
"
;

var
g_uin
=
"
1584619360
"
;
window

==
getTop()
&&
document.write(
'
<script src="http://rescdn.qqmail.com/zh_CN/htmledition/js/all02cc76.js"></
'
+
'
script>
'
);
(getTop().initPageEvent

||

function
(){})(window);

</
script
><
script
src
="http://rescdn.qqmail.com/zh_CN/htmledition/js/all02cc76.js"
type
="text/javascript"
></
script
><
title
>
QQ邮箱
</
title
><
style
></
style
><
script
>


var
bIsDiskPanelFrame
=

""

==

"
pannel
"
;
(

function
() {
window.fsuccesss

=

""
;


if
(getTop().initPageEvent)
{
getTop().initPageEvent(window);
}

window.isMainFrameError

=

!
getTop().getMainWin
||

getTop().getMainWin()

==
window
||
( getTop()
==
window
&&

!
getTop().getMainWin );


if
(
!
isMainFrameError )
{

return
;
}

document.write( [

'
<link rel="stylesheet" type="text/css" href="http://rescdn.qqmail.com/zh_CN/htmledition/style/comm201002c844.css" />
'
,

'
<link rel="stylesheet" type="text/css" href="/cgi-bin/getcss?sid=QSF5AV6BdAczHkAN&ft=skin" />
'

].join(

""
) );
window.onerror

=

function
(msg, url, line) {
return

true
;};
window.onload

=

function
()
{
setTimeout(

function
() {
document.body.backgroundColor

=

"
#fff
"
;

var
oMsgText
=
getTop().S(
"
msg_txt
"
,window);

var
_sMsg
=
getTop().trim(oMsgText.innerHTML);

var
bHasHistory
=
history.length
>

0

&&
getTop()
!=
window;

var
sCode
=

""
;

if
(bIsDiskPanelFrame)
//
QQ diskpanel


{
sCode

=
[
'
<center><div class="bd" style="text-align:left; margin:10px;"><div class="settingtable bold bd" style="padding:5px 10px; border-width:0 0 1px;">出错了</div><div style="padding:20px 10px 10px;">系统错误,您可以<a href="javascript:location.reload()">刷新恢复服务</a></div><div style="padding:0 10px 10px; text-align:right;"><input type="button" onclick="history.back()" class="btn" style="width:80px;" value="返回上一步" /></div></div></center>
'
];
}

else

if
(_sMsg)
{
sCode

=
[

'
<center><div class="bd" style="text-align:left;margin-top:20px;width:70%;"><div class="barspace1 toolbgline toolbg b_size bold" style="padding:12px 0 12px 16px;">邮箱提示</div><div style="background:#fff;padding:20px;line-height:20px;">
'
,

'
<div class="b_size">
'
,
_sMsg

?
_sMsg :
'
可能因网络原因造成系统繁忙,服务暂时中断。
'
,
'
<br><br><div class="b_size">您可以尝试
'
,

'
<a href="javascript:;" onclick="location.replace(location.href.split(\x27#\x27)[0]);return false;" style="font:14px;">刷新恢复服务</a> 。</div>
'
,

'
</div>
'
,

'
<div style="padding-top:20px;text-align:right;">
'
,
bHasHistory

?

'
<input type=button class=btn value=" 返回上一步 " onclick="history.back();"></div>
'
:
'
<input type=button value="返回邮箱首页" class=btn onclick="getTop().goUrlTopWin(\
'
http:
//
' + location.host +  '/cgi-bin/frame_html?sid=QSF5AV6BdAczHkAN&from=' + '\');">',


'
</div>
'
,

'
</div>
'
,

'
</div></center>
'

];
}
oMsgText.innerHTML

=
sCode.join(
""
);
oMsgText.style.display

=

"
block
"
;

},

20
);
};
} )();

function
SafeModeSwitchConfirm()
{

if
(getTop().S(
"
divSafeModeOn
"
))
{

return
;
}

if
(confirm(
"
QQ邮箱正在进行系统维护,您必须进入只读模式才能继续使用邮箱。\n请注意:如果您目前正在写邮件或写记事,直接进入只读模式将可能导致数据丢失,请[取消]并手工保存您的数据
"
))
{
getTop().goUrlTopWin(getTop().location.href,

true
);
}
}

</
script
></
head
><
body
class
="tipbg"
style
="text-align:center;background:white;"
><
div
id
="msg_txt"
style
="display:none;"
code
="-141"
>
验证码错误
<
script
>
var
bVerifycode
=
true
;getTop().verifyCode(
"
cncodewrong
"
);
</
script
></
div
></
body
><
script
language
="Javascript"
>

(

function
() {

var
_oMainWin
=
getTop().getMainWin(),
_sMainWinLoc

=
_oMainWin.location.href,
_sActionWinLoc

=
getTop().getActionWin().location.href,
_msgTxtObj

=
getTop().S(
"
msg_txt
"
, window),
_bIsShowVerifyFrame

=
getTop().QMDialog(
"
QMVerify
"
)
||

typeof
(bVerifycode)
!=
"
undefined
"
;


if
(
!
_msgTxtObj
||
getTop().trim
&&
getTop().trim(_msgTxtObj.innerText
||
_msgTxtObj.textContent)
==

"
[发送错误报告]
"
)
{

return
;
}
_msgTxtObj.innerHTML

=
getTop().filteScript( _msgTxtObj.innerHTML );


if
(window[
"
misslist
"
])
{

var
infos
=
_oMainWin.QMAttach.getInfoUid(misslist);
getTop().confirmBox( {
msg : getTop().TE([

'
以下%len%个附件绑定失败,请重试:<br/>
'
,

'
%@%for(%list%)%@%
'
,

'
<span style="color:red;" title="%name%">
'
,

'
%@%eval subAsiiStr(%name%,%_root_.width%,"...",1)%@%
'
,

'
</span>;&nbsp;
'
,

'
%@%if(%_root_.len%<5)%@%<br/>%@%endif%@%
'
,

'
%@%endfor%@%
'
],
'
%
'
).replace( {
len : infos.length,
width : infos.length

<

5

?

40
:
15
,
list : infos
} ),
title :

"
失败信息
"
,
confirmBtnTxt :

"
重试
"
,
cancelBtnTxt :

"
取消
"
,
onreturn :

function
(_abIsOk) {

if
(_abIsOk)
{
_oMainWin.fireMouseEvent(_oMainWin.SN(

"
sendbtn
"
)[
0
],
"
click
"
);
}

else

{
E(infos,

function
(_aoInfo) {
_oMainWin.delAttach(_aoInfo.id);
});
}
}
} );
}

else

if
(
!
_bIsShowVerifyFrame

&&

!
isMainFrameError

&&
(_sMainWinLoc.indexOf(
"
t=compose
"
)
!=

-
1
)
//
|| _sMainWinLoc.indexOf("t=mail_list_groupsms")>-1)


&&
_sActionWinLoc.indexOf(
"
customstationery
"
)
==

-
1


&&

"
<br/>
"

!=

"
autosave
"

&&

"
<br/>
"

!=

"
sms_notify
"
)
{
getTop().msgBox((_msgTxtObj

&&
_msgTxtObj.innerHTML)
||

"
系统错误
"
,
"
dialog
"
,
true
,
0
,
"
失败信息
"
, window );
}

else

if
(
!
isMainFrameError ) {

if
(
!
bIsDiskPanelFrame
||
window.name
!=

"
keepSession
"
)
{
getTop().msgBox(

null
, fsuccesss,
true
,
5000
,
null
, window);
}
}


try
{
getTop().errorProcess();
}

catch
( e ){}
} )();

</
script
></
html
>