精华内容
下载资源
问答
  • 正确配置和使用SQL mail

    千次阅读 2008-01-15 16:36:00
    正确配置和使用SQL mail 使用SQL Mail收发和自动处理邮件的扩展存储过程简介SQL SERVER提供了通过EXCHANGE或OUTLOOK收发邮件的扩展存储过程,下面将这几个过程简单的介绍一下。 一、启动SQL Mail xp_startmail @...

    正确配置和使用SQL mail

    使用SQL Mail收发和自动处理邮件中的扩展存储过程简介

    SQL SERVER提供了通过EXCHANGE或OUTLOOK收发邮件的扩展存储过程,下面将这几个过程简单的介绍一下。

    一、启动SQL Mail

    xp_startmail @user,@password

    @user和@password都是可选的

    也可打开Enterprise Manager中的Support Services,在SQL Mail上单击右键打开右键菜单,然后按Start来启动

    二、停止SQL Mail

    xp_stopmail

    也可用上述方法中的菜单里的Stop来停止

    三、发送邮件

    xp_sendmail {[@recipients =] 'recipients [;...n]'}
    [,[@message =] 'message>
    [,[@query =] 'query>
    [,[@attachments =] attachments]
    [,[@copy_recipients =] 'copy_recipients [;...n]'
    [,[@blind_copy_recipients =] 'blind_copy_recipients [;...n]'
    [,[@subject =] 'subject>
    [,[@type =] 'type>
    [,[@attach_results =] 'attach_value>
    [,[@no_output =] 'output_value>
    [,[@no_header =] 'header_value>
    [,[@width =] width]
    [,[@separator =] 'separator>
    [,[@echo_error =] 'echo_value>
    [,[@set_user =] 'user>
    [,[@dbuse =] 'database>

    其中@recipients是必需的

    参数说明:

    参数 说明
    @recipients 收件人,中间用逗号分开
    @message 要发送的信息
    @query 确定执行并依附邮件的有效查询,除触发器中的插入表及删除表外,此查询能引用任何对象
    @attachments 附件
    @copy_recipients 抄送
    @blind_copy_recipients 密送
    @subject 标题
    @attach_results 指定查询结果做为附件发送
    @no_header 不发送查询结果的列名
    @set_user 查询联接的用户名,默认为Guset
    @dbuse 查询所用的数据库,默认为缺省数据库

    四、阅读邮件收件箱中的邮件

    xp_readmail [[@msg_id =] 'message_number> [, [@type =] 'type' [OUTPUT]]
    [,[@peek =] 'peek>
    [,[@suppress_attach =] 'suppress_attach>
    [,[@originator =] 'sender' OUTPUT]
    [,[@subject =] 'subject' OUTPUT]
    [,[@message =] 'message' OUTPUT]
    [,[@recipients =] 'recipients [;...n]' OUTPUT]
    [,[@cc_list =] 'copy_recipients [;...n]' OUTPUT]
    [,[@bcc_list =] 'blind_copy_recipients [;...n]' OUTPUT]
    [,[@date_received =] 'date' OUTPUT]
    [,[@unread =] 'unread_value' OUTPUT]
    [,[@attachments =] 'attachments [;...n]' OUTPUT])
    [,[@skip_bytes =] bytes_to_skip OUTPUT]
    [,[@msg_length =] length_in_bytes OUTPUT]
    [,[@originator_address =] 'sender_address' OUTPUT]]

    参数说明:

    参数 说明
    @originator 发件人
    @subject 主题
    @message 信息
    @recipients 收件人
    @skip_tytes 读取邮件信息时跳过的字节数,用于顺序获取邮件信息段。
    @msg_length 确定所有信息的长度,通常与@skip_bytes一起处理长信息

    五、顺序处理下一个邮件

    xp_findnextmsg [[@msg_id =] 'message_number' [OUTPUT]]
    [,[@type =] type]
    [,[@unread_only =] 'unread_value> )

    六、删除邮件

    xp_deletemail {'message_number'}

    如果不指定邮件编号则删除收件箱中的所有邮件

    七、自动处理邮件

    sp_processmail [[@subject =] 'subject>
    [,[@filetype =] 'filetype>
    [,[@separator =] 'separator>
    [,[@set_user =] 'user>
    [,[@dbuse =] 'dbname>

     

     


    >用户在网上注册后,系统将随机产生的密码发送到用户登记的Email
    >用户在论坛的帖子有回复时将内容发送到用户的Email
    因为上述过程都是在存储过程中完成的,所以避免了前台程序对参数的传输处理,也不需要再用第三方的组件完成,感觉比较方便。

    1.为了使用SQL mail,首先你的服务器上得有SMTP服务,我没有安装win2000 server自带的SMTP,而是用imail6.04的SMTP,感觉比较稳定,功能也比较强。
    2.安装一个邮件系统,我安装了outLook 2000,我发现在配置邮件profile时,如果
    不安装outLook而是用别的第三方程序,win2k中文server版在控制面板中就找不到“邮件”一项.
    3.安装完outlook后再刷新控制面板,就会找到“邮件”一项,双击进行邮件的配置,为配置文件起一个名字(假设为myProfile),以便以后SQL mail使用,在该配置文件中设置各项属性。
    4.启动outlook(设置为用myProfile作为默认的配置文件),测试进行收发邮件,确认outlook工作正常。
    5.用当前的域帐户启动SQL server,在企业管理器的支持服务中,点击SQL mail的属性,可以看到在配置文件选择中,出现了刚才定义的myProfile配置文件(你也可以定义多个profile),选择这个配置文件进行测试,SQL将返回成功开始和结束一个MAPI会话的信息,如果出现错误或是没有找到邮件配置文件,那一定是你启动SQL server用的帐号有问题
    6.现在你就可以在查询分析器中用XP_sendmail这个扩展存储过程发送SQL mail了,格式如下:
    xp_sendmail {[@recipients =] 'recipients [;...n]'}
    [,][@message =] 'message>
    [,][@query =] 'query>
    [,][@attachments =] attachments]
    [,][@copy_recipients =] 'copy_recipients [;...n]'
    [,][@blind_copy_recipients =] 'blind_copy_recipients [;...n]'
    [,][@subject =] 'subject>
    [,[@type =] 'type>
    [,][@attach_results =] 'attach_value>
    [,][@no_output =] 'output_value>
    [,][@no_header =] 'header_value>
    [,][@width =] width]
    [,][@separator =] 'separator>
    [,][@echo_error =] 'echo_value>
    [,][@set_user =] 'user>
    [,][@dbuse =] 'database>

    其中@recipients是必需的

    参数说明:

    参数 说明
    @recipients 收件人,中间用逗号分开
    @message 要发送的信息
    @query 确定执行并依附邮件的有效查询,除触发器中的插入表及删除表外,此查询能引用任何对象
    @attachments 附件
    @copy_recipients 抄送
    @blind_copy_recipients 密送
    @subject 标题
    @attach_results 指定查询结果做为附件发送
    @no_header 不发送查询结果的列名
    @set_user 查询联接的用户名,默认为Guset
    @dbuse 查询所用的数据库,默认为缺省数据库


    7.不过,如果是在web应用中使用SQL mail,还有一些问题要解决:首先,就是应用程序中连接数据库的帐号,我在网站程序中的数据库连接是使用UDL文件,帐号为DbGuest,这是一个普通帐户,所以还必须在master库的扩展存储过程找到XP_sendmail,并在其属性中增加DbGuest这个用户,并选择EXEC权限。
    好了,现在设置完毕,运行网站程序,测试用户注册,几乎没有什么延迟,我测试用的邮箱中就收到了这封SQL mail发出的Email:
    "谢谢你的注册,建议你首次登录后修改密码"

     

     


    Sql Mail技术给每一位数据库开发人员和DBA(数据库管理员)带来了极大的方便,利用该技术,Sql Server数据库代理程序可以在系统出现异常的时候自动发送Email通知管理员,开发人可以利用它让数据库自动定期的修改用户密码,然后发送Email通知用户……等等这些应用,都不同程度上把我们从繁杂的工作中解放出来。但是,Sql Mail的配置是比较复杂的,相信90%以上的人在配置Sql Mail的时候都遇到过各种各样的麻烦,至少有70%的人放弃了Sql Mail而选择其他方案来解决这个问题。笔者是一名Web开发人员,亲身经历了这一切,并找到了一个更好的替代方法。不敢独享,写出来以飨读者。
    Sql Mail配置有几种方式,按照支持软件可划分为基于Exchange、Outlook2000(以上)和第三方软件的配置方案,三种方式各有利弊,主要表现在以下几个方面:

        使用Outlook客户端配合Sql Server实现Sql Mail
        此方案软件要求较低,只需要在Sql Server所在服务器上安装Outlook2000以上版本客户端即可。它要求在Sql Mail使用期间,OutLook客户端必须打开,否则,只能到下次打开时,邮件才能发送出去。另外,如果服务器为远程服务器,用微软官方的远程桌面无法完成配置,可替代的方案是DBA亲自去机房直接操作,或者安装PcAnywhere10替代远程桌面进行操作。

        使用Exchange要求较高
        Microsoft推荐使用Exchange作为Sql Mail的最佳拍档,MSDN的资料提出:“由于 POP3/SMTP 协议存在的局限性和登录问题,Microsoft 建议您使用 Exchange Server 来实现可靠性”。但是Exchange并不是专门做这个来使用的,可以说是屈才了,而且Exchange要求服务器配置域管理器,相信这个东东对大多数数据库服务器来说用处不大,只不过是浪费资源罢了。如果我们要在多台服务器上配置Sql Mail那么就需要在每一台服务器上配置域管理器,或者所有的服务器都配置到一个域内,但是对于服务器比较分散的系统来说这样是不现实的。

        使用第三方系统支持
        微软MSDN称:“如果您使用的是第三方邮件服务器,则必须将邮件服务器配置为 POP3 服务器。如果这些邮件服务器使用的本地邮件服务可能是由第三方邮件客户端安装的,Microsoft 将不支持连接到这些服务器”。这就意味着你还要使用Windows平台的邮件服务,使用ASP编写网站的朋友一定都知道,Cdonts.dll组件实在是……。
    面对这些问题,笔者就变成了我刚才说的那70%了,虽然爬网无数,读文字数万把Sql Mail配置好了,但是我仍然绝对放弃,因为我不想在做第2台、第3台……的时候重蹈覆辙。替代的方案就是Jmail组件+OLE自动化对象,以上的问题迎刃而解。

        预备知识
        1.OLE自动化函数
        OLE自动化使应用程序能够对另一个应用程序中实现的对象进行操作,或者将对象公开以便可以对其进行操作。自动化客户端是可对属于另一个应用程序的公开对象进行操作的应用程序,本文值得是Sql Server。公开对象的应用程序称为自动化服务器,又成为自动化组件,本文中即Jmail组件咯。客户端通过访问应用程序对象的属性和函数对这些对象进行操作。
        在Sql Server使用Ole组件的途径是几个系统扩展存储过程sp_OACreate、sp_OADestroy、sp_OAGetErrorInfo、sp_OAMethod、sp_OASetProperty和sp_OAGetProperty,再次简单地介绍一下使用方法,详细资料参考Sql Server联机丛书。
        OLE自动化对象的使用方法:
        (1)调用 sp_OACreate 创建对象。
        格式:sp_OACreate clsid,objecttoken OUTPUT [ , context ]
        参数:clsid——是要创建的 OLE 对象的程序标识符 (ProgID)。此字符串描述该 OLE 对象的类,其形式,如 'OLEComponent.Object',OLEComponent 是 OLE 自动化服务器的组件名称,Object 是 OLE 对象名,本文中使用的“JMail.Message”;
    Objecttoken——是返回的对象标志,并且必须是数据类型为 int 的局部变量。用于标识所创建的 OLE 对象,并将在调用其它 OLE 自动化存储过程时使用。本文中就是通过它来调用JMail.Message组件的属性和方法的。
        Context——指定新创建的 OLE 对象要在其中运行的执行上下文。本文不使用该参数,故不赘述。以下与此一致,所有方法属性的其他用法请参阅Sql Server联机文档。
        (2)使用该对象。
        (a)调用 sp_OAGetProperty 获取属性值。
        格式:_OAGetProperty objecttoken,propertyname [, propertyvalue OUTPUT]
        参数:(前面出现过的参数,以下均省略。)
        Propertyname——对象的属性名称;
        Propertyvalue——返回的对象的属性值,该参数带OUTPUT属性,执行该操作后,你就可以从propertyvalue中得到属性的值了。
        (b)调用 sp_OASetProperty 将属性设为新值。
        格式:sp_OASetProperty objecttoken, propertyname, propertyvalue
        (c)调用 sp_OAMethod 以调用某个方法。
        格式:sp_OAMethod objecttoken, methodname [, returnvalue OUTPUT] [ , [ parametername = ] parametervalue  [...n]]
        参数:Returnvalue——调用方法的返回值,如果没有返回值,此参数设置为NULL;
        Parametername——方法定义中的参数名称,也就是形参;
        Parametervalue——参数值;
        ……n——表示,可以带很多参数,个数由方法定义限制;
        (d)调用 sp_OAGetErrorInfo 获取最新的错误信息。
        格式:sp_OAGetErrorInfo [objecttoken ] [, source OUTPUT] [, description OUTPUT]
        参数:Source——错误源;
        Description——错误描述;
        (3)调用 sp_OADestroy 释放对象。
        格式:sp_OADestroy objecttoken

        2.xp_cmdshell扩展存储过程
        该扩展存储过程在master数据库中,它的全路径是master..xp_cmdshell(注意,中间是2个点),它的功能是:以操作系统命令行解释器的方式执行给定的命令字符串,并以文本行方式返回任何输出。
        格式:xp_cmdshell {'command_string'} [, no_output]
        参数:'command_string'——是在操作系统命令行解释器上执行的命令字符串。
        no_output——是可选参数,表示执行给定的 command_string,但不向客户端返回任何输出。本文应用中不使用该参数。

        操作方法
        (1)软件准备
        请先到http://www.dimac.net/或者国内提供组件下载的网站下载最新版的JMail组件,如果你得到的是安装版,执行weJMailx.exe即可,系统的配置安装程序会自动完成。如果只有一个JMail.dll文件,请按照下面的步骤安装:
        (a)新建文本文件,输入如下命令:
        regsvr32 JMail.dll
        net start w3svc
        另存为Install.Bat(注意,千万不要保存成Install.Bat.Txt啊)
        (b)此文件连同Jmail.dll一起拷贝到Sql Server数据库服务器的System32目录下,并执行双击Install.Bat即可。
        (2)准备好了吗?跟我来吧
        (a)运行Sql Server查询分析器,并以sa身份登录到Sql Server数据库;
        (b)如果你的存储过程要添加到YourDefaultCatalog数据库,请在空白Sql窗口输入如下指令,否则请相应修改数据库名。
        Use YourDefaultCatalog
        按F5或者运行按钮运行该指令;
        (c)创建基本发送存储过程
        复制如下代码到Sql Server命令窗口,并运行。下面的代码中有相应的注释,文中不多做解释,如有疑问请查看前面的“预备知识”或者查询Sql Server帮助文件,当然也可以和作者联系。

    Create Procedure dbo.sp_jmail_send
    @sender varchar(100),
    @sendername varchar(100)='',
    @serveraddress varchar(255)='SMTP服务器地址',
    @MailServerUserName varchar(255)=null,
    @MailServerPassword varchar(255)=null,
    @recipient varchar(255),
    @recipientBCC varchar(200)=null,
    @recipientBCCName varchar(200)=null,
    @recipientCC varchar(200)=null,
    @recipientCCName varchar(100)=null,
    @attachment varchar(100) =null,
    @subject varchar(255),
    @mailbody text
    As
    /*
    该存储过程使用办公自动化脚本调用Dimac w3 JMail AxtiveX组件来代替Sql Mail发送邮件
    该方法支持“服务器端身份验证”
    */
    --声明w3 JMail使用的常规变量及错误信息变量
    Declare @object int,@hr int,@rc int,@output varchar(400),@description varchar (400),@source varchar(400)

    --创建JMail.Message对象

    Exec @hr = sp_OACreate 'jmail.message', @object OUTPUT

    --设置邮件编码
    Exec @hr = sp_OASetProperty @object, 'Charset', 'gb2312'

    --身份验证
    If Not @MailServerUserName is null
    Exec @hr = sp_OASetProperty @object, 'MailServerUserName',@MailServerUserName
    If Not @MailServerPassword is null
    Exec @hr = sp_OASetProperty @object, 'MailServerPassword',@MailServerPassword

    --设置邮件基本参数
    Exec @hr = sp_OASetProperty @object, 'From', @sender
    Exec @hr = sp_OAMethod @object, 'AddRecipient', NULL , @recipient
    Exec @hr = sp_OASetProperty @object, 'Subject', @subject
    Exec @hr = sp_OASetProperty @object, 'Body', @mailbody

    --设置其它参数
    if not @attachment is null
    exec @hr = sp_OAMethod @object, 'Addattachment', NULL , @attachment,'false'
    print @attachment
    If (Not @recipientBCC is null) And (Not @recipientBCCName is null)
    Exec @hr = sp_OAMethod @object, 'AddRecipientBCC', NULL , @recipientBCC,@recipientBCCName
    Else If Not @recipientBCC is null
    Exec @hr = sp_OAMethod @object, 'AddRecipientBCC', NULL , @recipientBCC

    If (Not @recipientCC is null) And (Not @recipientCCName is null)
    Exec @hr = sp_OAMethod @object, 'AddRecipientCC', NULL , @recipientCC,@recipientCCName
    Else If Not @recipientCC is null
    Exec @hr = sp_OAMethod @object, 'AddRecipientCC', NULL , @recipientCC

    If Not @sendername is null
    Exec @hr = sp_OASetProperty @object, 'FromName', @sendername

    --调用Send方法发送邮件
    Exec @hr = sp_OAMethod @object, 'Send', null,@serveraddress

    --捕获JMail.Message异常
    Exec @hr = sp_OAGetErrorInfo @object, @source OUTPUT, @description OUTPUT

    if (@hr = 0)
    Begin
    Set @output='错误源: '+@source
    Print @output
    Select @output = '错误描述: ' + @description
    Print @output
    End
    Else
    Begin
    Print '获取错误信息失败!'
    Return
    End

    --释放JMail.Message对象
    Exec @hr = sp_OADestroy @object

        (d)简化存储过程操作,以适合我们平时的使用习惯
        上面的存储过程基本可以完成邮件发送操作,但是非常冗长,而且不符合我们的习惯,比如它不支持多个发送给接收者、不支持将Sql指令运行结果以附件形式发送(这是Sql Mail的功能,我们也可以做到)等,所以我们要再写一个存储过程来调用它,以简化操作,并扩展功能。

    Create Procedure SendMail
    @Sender varChar(50)=null,
    @strRecipients varChar(200),
    @strSubject varChar(200),
    @strMessage varChar(2000),
    @sql varChar(50)=null)
    As

    Declare @SplitStr varchar(1) --定义邮件地址分割符变量
    Declare @strTemp varchar(200) --定义多个收件人字符串临时变量
    Declare @email varchar(50) --用分割符分割后的单个收件人字符串变量

    Declare @SenderAddress varChar(50)
    Declare @Attach varChar(200)

    Declare @DefaultSender varChar(50)
    Declare @MailServer varChar(50)
    Declare @User varChar(50)
    Declare @Pass varChar(50)
    Declare @SenderName varChar(50)
    Declare @AttachDir varChar(100)

    --初始化缺省变量
    Set @DefaultSender='缺省发送地址'
    Set @MailServer='邮件服务器地址'
    Set @User='SMTP服务器验证用户地址'
    Set @Pass='SMTP服务器验证地址'
    Set @SenderName='缺省发件人名称'
    Set @AttachDir='E:/LOG/WebData/Jmail/'+Replace(Replace(Replace(Convert(varChar(19),GetDate(),120),'-',''),' ',''),':','')+'.txt'

    --将Email地址分割符统一为分号
    set @SplitStr=';'
    Set @strTemp=@strRecipients+@SplitStr+'end'
    Set @strTemp=Replace(@strTemp,',',';')

    --判断是否有sql语句
    If (@Sql is Null) Or (len(@Sql)=0)
    Set @AttachDir=Null
    Else
    Begin
    Declare @CmdStr varChar(200)
    Set @CmdStr='bcp "'+@Sql+'" queryout '+@AttachDir+' -c'
    EXEC master..xp_cmdshell @CmdStr
    End

    while CharIndex(@SplitStr,@strTemp,1)<>0
    Begin
    Set @email=left(@strTemp,CharIndex(@SplitStr,@strTemp,1)-1)
    Set @strTemp=right(@strTemp,len(@strTemp)-len(@email)-1)
    If (@Sender Is Null) Or (Len(@Sender)=0)
    Set @SenderAddress=@DefaultSender
    Else
    Set @SenderAddress=@Sender
    Print @email
    --调用sp_jmail_send发送邮件
    EXEC sp_jmail_send @sender=@SenderAddress,@sendername=@SenderName,
    @serveraddress=@MailServer,@MailServerUserName=@User,@MailServerPassword=@Pass,
    @recipient=@email,@subject=@strSubject,@mailbody=@strMessage,@attachment=@AttachDir
    End
        此存储过程只扩展了Sql查询结果附件发送,如果你要发送标准附件,请直接使用sp_jmail_send存储过程或者自行扩展功能。

     

     

    展开全文
  • qt信号槽之创建连接自定义的槽

    千次阅读 2012-11-29 10:50:49
    在第一版的Qt设计器,你可以创建你自定义的槽的信号并使他们连接起来,但是你不能直接实现你的槽,你不得不子集于该窗体,并在这个子集中对你自定义的槽编码,子集的方法依然有用,在某些情况下仍起作用(Make ...
    
    

    在第一版的Qt设计器中,你可以创建你自定义的槽的信号并使他们连接起来,但是你不能直接实现你的槽,你不得不子集于该窗体,并在这个子集中对你自定义的槽编码,子集的方法依然有用,在某些情况下仍起作用(Make sense),但是现在你可以在Qt设计器中直接实现你的槽,在多的对话框和窗体的子集也不再需要了。(Qt设计器在.ui.h文件中存贮者槽的实现,具体的细节参看 走近设计器(The Designer Approach)中的.ui.h扩展方法)。

      多项剪辑应用程序需要四个槽,一个用于每个按钮,因为我们将一个信号连接到预定义的槽使得按下退出按钮时退出程序,所以仅有三个槽需要我们自定义,我们需要我增加剪辑按钮实现一个槽,其作用是将当前剪辑添加到列表框中,复制以前的按钮(Copy Previous)需要一个槽实现复制列表框中选中的项到线型编辑框中(也复制到剪贴板上),删除剪辑按钮(Delete Clipping)需要一个槽实现删除党旗那剪辑和列表框中的当前项,我们也需要写一些初始化的代码使得程序启动时,将当前剪贴板上的文本(如果有的话)复制到线型编辑框中,代码可以直接在Qt设计器中编写,声称的已写好的代码片断可以下面的文件中获取:qt/tools/designer/examples/multiclip/multiclip.ui.h

      我们需要一个Qt的全局剪贴板对象,在整个代码中有几个地方需要调用其含义是调用QApplication::clipboard()或者是qApp->clipboard(),比执行这些函数调用更好的方法,我们在窗体本身添加一个指向剪贴板的指针,点击对象浏览器中的源码标签(如果对象浏览器部可见,点击 Windows|Views|Object Explorer [由于Qt设计器没有汉化,再说汉化后似乎失去了其魅力,所以菜单的引用依然是English])源码标签显示了当前窗体中的函数、类变量、前部的声明和我们需要的一些包含文件的名字。

       鼠标右键点击类变量(Class Variable)项(可能在底部,你需要点击滑动条),然后再弹出的菜单中选择New子菜单,(如果存在变量,则弹出来的菜单中会有一个“Delete”删除的选项)键入QClipboard * cb 并按下Enter键确认输入,我们将创建一个init()函数,在这个函数中我们会赋予这个指针一个Qt的全局剪贴板对象的值。我们也需要声明剪贴板对象的头文件,右键点击包含(声明文件)[在Qt设计器中对应的为 Include(in Declaration)项],然后再弹出的菜单上选择 New ,键入“”,并按下Enter键确认输入,因为我们需要一个全局对象,qApp,我们就必须包含另一个声明文件,鼠标右键点击 包含(实现)项[在Qt设计器中对应为Include (in Implementation)],然后点击New 菜单项,键入“”并按下Enter键确认输入,这个变量和声明文件将被Qt设计器的.ui文件生成后包含(丛.ui文件生成的源文件.h 和.cpp文件会包含这个变量和声明文件)。

    We will invoke Qt Designer's code editor and write the code.  现在我们将激活Qt设计器的代码编辑器并编写代码。

      首先我们创建一个init()函数,激活代码编辑器的一种途径是点击Source标签,然后点击你想编写代码的函数的名字,如果没有你需要的函数或者你想创建一个在Source标签中你可用的新函数,鼠标右键点击Source标签的槽(Slots)列表中的“protected”子项,然后点击New子菜单,出现一个“Edit Slots”编辑槽的对话框,改变槽的名字为Init(),然后点击OK,这样你可以点击编辑窗口中出现的函数名字并键入你的代码。

       注意到并不是强迫你使用Qt设计器的代码编辑器,在Qt设计器中你可以尽管的增加、删除或者重命名你的槽,你也可以用一个外部的编辑器编写的实现代码,Qt设计器会保存你所写的代码。下面是你需要实现的init()函数:

        void MulticlipForm::init()
        {
            lengthLCDNumber->setBackgroundColor( darkBlue );
            currentLineEdit->setFocus();

     

            cb = qApp->clipboard();
            connect( cb, SIGNAL( dataChanged() ), SLOT( dataChanged() ) );
            if ( cb->supportsSelection() )
                connect( cb, SIGNAL( selectionChanged() ), SLOT( selectionChanged() ) );

     

            dataChanged();
        }

    函数体中开始的两行改变了数字指示器的背景颜色并使窗体的启动焦点在线型编辑框中,我们使用了一个指向Qt全局剪贴板的指针,并保存在我们定义的类变量 cb 中,我们连接剪贴板的dataChanged()信号到一个叫做 dataChanged()的槽,这个操我们马上就会创建,如果剪贴板支持选择(例如在X Windows系统中),我们也要连接剪贴板的selectionChanged()信号到一个我们将创建的具有相同名字的槽,最后我们将调用我们的dataChanged()槽函数,当程序启动时,将当前剪贴板中的文本内容(如果有的话)粘贴但线型编辑框中

     既然我们提到了dataChanged()和selectionChanged()槽,下面我们将对他们编码,从dataChanged()开始:

        void MulticlipForm::dataChanged()
        {
            QString text;
            text = cb->text();
            clippingChanged( text );
            if ( autoCheckBox->isChecked() )
                addClipping();
        }

      我们复制了剪贴板的文本并用我们获得文本调用我们自己定义的clippingChanged()槽,如果用户选中了自动增加剪辑检测框,我们将调用addClipping()槽增加该剪辑到列表框中。

     下面的代码只是在X Windows系统中可用,微软的Windows用户也能包含下面的代码确保该应用程序可以在多平台下运行。

        void MulticlipForm::selectionChanged()
        {
            cb->setSelectionMode( TRUE );
            dataChanged();
            cb->setSelectionMode( FALSE );
        }

     上面的代码中我们首先告诉剪贴板使用选择模式 ,我们调用dataChanged()槽获得任何选中的文本,然后设置剪贴板为默认模式。

    In the another custom slot, clippingChanged().在另一个自定义的槽中,clippingChanged():

        void MulticlipForm::clippingChanged( const QString & clipping )
        {
            currentLineEdit->setText( clipping );
            lengthLCDNumber->display( (int)clipping.length() );
        }

      我们用任何传递给clippingChanged()槽的文本设置当前的线型编辑框,并用这个新文本的长度更新数字指示器。

       我们将要编码实现的下一个槽实现增加剪辑功能,当用户点击增加剪辑按钮时,这个槽被我们的代码内部调用(看上面的dataChanged()槽),即使我们让Qt设计器能够通过在编辑窗口键入代码来创建一个槽与信号的连接,我们也让Qt设计器为我们创建一个结构,(为我们生成程序框架),点击Edit|Slots 激活编辑槽(Edit Slots)对话框,点击New Slot按钮并把槽的缺省名字“new_slot()”改为“addClipping()”,这里不必改变访问标着和返回类型,现在我们已经创建了我们的槽,我们能在代码编辑器中出现的地方创建我们的槽并实现之。

    增加剪辑按钮用于从当前剪辑线型编辑框中复制文本到列表框中,同时我们更新了文本长度数字指示器。

        void MulticlipForm::addClipping()
        {
            QString text = currentLineEdit->text();
            if ( ! text.isEmpty() ) {
                lengthLCDNumber->display( (int)text.length() );
                int i = 0;
                for ( ; i < (int)clippingsListBox->count(); i++ ) {
                    if ( clippingsListBox->text( i ) == text ) {
                        i = -1; // Do not add duplicates
                        break;
                    }
                }
                if ( i != -1 )
                    clippingsListBox->insertItem( text, 0 );
            }
        }

      如果有新文本需要显示,同时改变数字指示器的值为当前文本的长度,然后我们在列表框中逐个比较文本项,看当前输入的文本是否存在列表框中,如果列表框中不存在该剪辑文本项,就在列表中插入该剪辑文本。

     为了使增加剪辑按钮工作,我们需要连接这个按钮的addClipping()槽,点击 Connect Signal/Slots 工具栏按钮,然后点击增加剪辑按钮,(将变成十字型的鼠标拖动到窗体上点击该按钮然后释放),(确认你拖向窗体而不是其他部件—拖动过程中窗体会有一个细红色边界,如果你操作错误你只需在槽组合框中改变其名字就可以了[这里我按原文翻译,可是有些地方和单词觉得上下文不对,如果你发现了正确的请与我联系])。连接编辑对话框将会显示,点击addClipping()槽,点击OK按钮确认该连接。

      复制前一个文本按钮(Copy Previous)用于从列表框中复制选中的文本项到线型编辑框中,同时该文本也放到了剪贴板上,剩下来的过程和增加剪辑按钮(Add Clipping)是一样的:首先我们创建一个槽,然后我们实现该槽,最后我们将按钮点击的信号和槽连接起来。

    1.    创建槽:

     点击Edit|Slots菜单子项,激活槽编辑(Edit Slots)对话框,点击 New Slots 并将缺省的槽函数名字“new_slot()”改变为“copyPrevious()”,点击OK确认。

    2.    实现槽函数

    3.               void MulticlipForm::copyPrevious()
    4.               {
    5.                   if ( clippingsListBox->currentItem() != -1 ) {
    6.                       cb->setText( clippingsListBox->currentText() );
    7.                       if ( cb->supportsSelection() ) {
    8.                           cb->setSelectionMode( TRUE );
    9.                           cb->setText( clippingsListBox->currentText() );
    10.                       cb->setSelectionMode( FALSE );
    11.                   }
    12.               }
    13.           }

        这段复制上次剪辑的代码检测列表框中是否有选项选中,如果有选项被选中则该项会被复制到线型编辑框中,如果我们使用了一个支持选择的系统,我们将不得不重复复制,复制的次数和选择模式有关,我们不要显示的更新剪贴板,当线型编辑框的文本改变时,他发射了一个dataChanged()的信号,这个信号被我们创建的dataChanged()槽接收,我们的这个dataChanged()槽同时更新剪贴板。

    14.连接到槽

     点击 Connect Signal/Slots 工具栏按钮,点击复制上次剪辑(Copy Previous)按钮,拖动到窗体然后释放鼠标,(光标变成十字型后,点击某个部件1,如按钮,拖动鼠标到另一个部件2或者窗体,则在出现的信号槽连接编辑对话框中,信号的发射者是部件1,接受信号的槽是部件2或者窗体的[试试就知道了J]),在弹出的连接编辑对话框中,点击clicked()信号和copyPrevious()槽,点击OK确认连接编辑。

      用同样的方法我们对删除剪辑(Delete Clipping)按钮进行信号和槽的连接编辑。

    1.    点击Edit|Slots 菜单激活槽编辑对话框,点击 New Slot 按钮并替换缺省的槽名字为“deleteClipping()”,点击OK按钮。

    2.    删除按钮必须删除列表框中的当前项并清除线型编辑框中的文本。

    3.               void MulticlipForm::deleteClipping()
    4.               {
    5.                   clippingChanged( "" );
    6.                   clippingsListBox->removeItem( clippingsListBox->currentItem() );
    7.               }

    我们以一个空字符串调用我们自己创建的clippingChanged()槽,使用列表框的removeItem()函数删除当前选项。

    8.    连接删除剪辑(Delete Clipping )按钮的clicked()信号到我们创建的deleteClipping()槽,(按下F3键---这个点击Connnect Signals/Slots工具栏按钮效果一样,点击删除剪辑按钮并拖动鼠标到窗体上,释放鼠标,出现连接编辑对话框,点击clicked()信号和deleteClipping()槽,点击OK()确认连接编辑)

    编译和生成应用程序

    至此在Qt设计器中,我们写了一个Qt整个应用程序大约99%的代码,为了使应用程序编译和运行我们必须创建一个main.cpp文件,在这个文件中我们将显示调用我们的窗体。

      创建一个新的源文件的简单方法是点击File|New激活“New File”新文件对话框,适当的点击“C++ Source”或者“C++ Header”图标,然后点击“OK”按钮确认,一个新的空源文件显示出来,点击File|Save 激活另存为对话框(Save As ),键入“main.cpp”然后点击Save按钮保存该文件。

    在main.cpp这个C++编辑窗口,键入以下代码。

        #include <qapplication.h>
        #include "multiclip.h"

     

        int main( int argc, char *argv[] )
        {
            QApplication app( argc, argv );

     

            MulticlipForm clippingForm;
            app.setMainWidget( &clippingForm );
            clippingForm.show();

     

            return app.exec();
        }

      这个程序创建了一个QApplication对象和我们的多项剪辑窗体的实例,并将该窗体设置为主部件并显示该窗体,app.exec()开始调用程序事件循环。

     现在启动控制台命令(命令行格式),并将当前目录切换到multiclip程序下,运行qmake命令,一个与你的系统兼容的Makefile文件将会生成。

    qmake -o Makefile multiclip.pro

       现在你可以通过运行make或者nmake命令生成该应用程序。试着编译和运行Multiclip,你会取得许多的进步,使用布局工具和编写代码的试验可以帮助你学到关于Qt和Qt设计器的更多的东西。

      这一章向你介绍了使用Qt设计器创建一个跨平台的应用程序,我们创建了一个窗体并且用部件对其修饰,这些部件整齐优美的放置在窗体上并可以随着窗体缩放,我们已经使用了Qt的信号和槽的机制实现程序的功能,并生成了Makefile文件,这些增加部件到窗体并用布局工具对其放置,以及创建、编写和连接槽的技术,当你再次用Qt设计器创建程序是会用到,下面的章节会给出进一步的例子并介绍使用Qt设计器的更多技术。

    翻译者 volrender

    E-mail  volrender@163.com 


    展开全文
  • 海康RTSP客户端连接深入分析

    万次阅读 多人点赞 2014-11-27 15:23:44
    海康相机RTSP连接代码...一、分析url的端口号,创建一个与服务器[S-PORT]端口号的TCP连接用于RTSP命令交互 二、执行RTSP命令请求,告诉服务器可以连接到本地的端口[C-PORT]进行数据发送 三、客户端从本地[C-PORT]读取

    海康相机RTSP连接代码分析

    最近在做海康相机rtsp连接获取音视频的工作,现在介绍一下分析过程和源码。

    【源码在我上传的共享资料中:http://download.csdn.net/detail/zhouyongku/8203521

       一、基本原理

     RTSP客户端去连接服务器的最基本步骤如下:   

    (1)分析url中的端口号,创建一个与服务器[S-PORT]端口号的TCP连接用于RTSP命令交互
    (2)执行RTSP命令请求,告诉服务器可以连接到本地的端口[C-PORT]进行数据发送
    (3)客户端从本地[C-PORT]读取数据

      

       二、研究步骤

     来研究一下海康相机的RTSP数据是怎么交互的

    (1)打开Wireshark,选择"抓包"->"网络接口"->选择网卡->“开始”,在"过滤“栏输入"rtsp",屏蔽不相关信息。

    (2)打开VLC播放器,输入海康相机RTSP地址

    例如:【rtsp://admin:12345@192.168.1.145:554/MPEG-4/ch2/main/av_stream】

    (3)播放几秒后再点击停止播放,退出VLC播放器。
    (4)分析Wireshark数据

    三、RTSP协议过程分析

    1、RTSP协议梗概

    就这几个简单的交互命令就能实现RTSP对接,C代表Client S代表Server

            例如:C1-客户端发的第一个命令 S1-服务器响应的第一个回复

    【C1】OPTIONS rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream RTSP/1.0RTSP

    【S1】Reply: RTSP/1.0 200 OKRTSP
    【C2】DESCRIBE rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream RTSP/1.0RTSP
    【S2】Reply: RTSP/1.0 401 Unauthorized RTSP
    【C3】DESCRIBE rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream RTSP/1.0RTSP
    【S3】Reply: RTSP/1.0 200 OK, with session descriptionRTSP/SDP
    【C4】SETUP rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/trackID=1 RTSP/1.0RTSP
    【S4】Reply: RTSP/1.0 200 OK RTSP
    【C5】SETUP rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/trackID=2 RTSP/1.0RTSP
    【S5】Reply: RTSP/1.0 200 OK RTSP
    【C6】PLAY rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/ RTSP/1.0RTSP
    【S6】Reply: RTSP/1.0 200 OK RTSP
    【C7】GET_PARAMETER rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/ RTSP/1.0RTSP
    【S7】Reply: RTSP/1.0 200 OK RTSP
    【C8】TEARDOWN rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/ RTSP/1.0RTSP
    【S8】Reply: RTSP/1.0 200 OK RTSP

    2、请求OPTION

    【C1-OPTION】

     Request: OPTIONS rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream RTSP/1.0\r\n
    CSeq: 2\r\n
    User-Agent: LibVLC/2.1.3 (LIVE555 Streaming Media v2014.01.21)\r\n
    \r\n

    询问RTSP服务器有哪些命令可以使用

    【S1-REPLY】

    Response: RTSP/1.0 200 OK\r\n
    CSeq: 2\r\n
    Public: OPTIONS, DESCRIBE, PLAY, PAUSE, SETUP, TEARDOWN, SET_PARAMETER, GET_PARAMETER\r\n
    Date:  Thu, Nov 27 2014 11:59:41 GMT\r\n 

    服务器回应客户端的请求。这个回应第一行为OK,则表明服务器接受查询命令,并且反馈给客户端信息,可以使用的命令有:OPTIONS, DESCRIBE, PLAY, PAUSE, SETUP, TEARDOWN, SET_PARAMETER, GET_PARAMETER,则下一步就可以向服务器发送这些命令,如果没有,则服务器不接收这些命令。

    3、无验证请求资源描述DESCRIBE

    【C2-DESCRIBE】

      Request: DESCRIBE rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream RTSP/1.0\r\n
      CSeq: 3\r\n
      User-Agent: LibVLC/2.1.3 (LIVE555 Streaming Media v2014.01.21)\r\n

     Accept: application/sdp\r\n

      \r\n

    请求RTSP服务器描述自身有哪些音视频资源
     

    【S2-REPLY】

    Response: RTSP/1.0 401 Unauthorized\r\n
    CSeq: 3\r\n
    WWW-Authenticate: Digest realm="4419b727ab09", nonce="66bb9f0bf5ac93a909ac8e88877ae727", stale="FALSE"\r\n
    WWW-Authenticate: Basic realm="4419b727ab09"\r\n

    Date:  Thu, Nov 27 2014 11:59:41 GMT\r\n

    服务器回应客户端的请求。第一行为Unauthorized,表明认证不成功服务器拒绝给出拥有的资源,因此我们需要再次发送认证的请求到服务器,则服务器才能给出拥有的资源

    4、认证请求资源描述DESCRIBE

    【C3-DESCRIBE】

    Request: DESCRIBE rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream RTSP/1.0\r\n
    CSeq: 4\r\n
    Authorization: Digest username="admin", realm="4419b727ab09", nonce="66bb9f0bf5ac93a909ac8e88877ae727", uri="rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream", response="108084646408d21aa255664781c886fc"\r\n
    User-Agent: LibVLC/2.1.3 (LIVE555 Streaming Media v2014.01.21)\r\n
    Accept: application/sdp\r\n
    \r\n

     
    发送具有认证标识的信息到服务器,去获取服务器的资源列表。怎样的格式算正确认证的信息?这个关键在于response字段。[S2-REPLY]中反馈了两个信息realm="4419b727ab09", nonce="66bb9f0bf5ac93a909ac8e88877ae727",

    **************************************response区域的计算规则如下***********************************

    (1)当password为MD5编码,则
    response = md5(<password>:<nonce>:md5(<cmd>:<url>));
    (2)当password为ANSI字符串,则
    response = md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<uri>));

    ***********************************************************************************************************

    其中 password=密码,nonce=服务器返回的nonce,cmd=当前命令(DESCRIBE、SETUP等),realm=服务器返回的reaml,uri=请求的uri地址.当服务器接收到response为108084646408d21aa255664781c886fc的时候,服务器也会用这一套公式去计算,当发现计算出的值与客户端提供的值吻合的时候,则表明用户名和密码校验成功

    void RtspRequest::SendRegisterRequest(string requestType)
    {
    	string requestCmd;
    	char cseq[256];
    	char session[256];
    	char author[500] = { 0 };
    	char acc[200] = { 0 };
    	char szInput[200] = { "" };
    	char szOutput[200] = { 0 };
    	char szMd5Pwd[200] = { 0 };
    	char szmd5methorduri[200] = { 0 };
    	
    	m_CSeq++;
    
    	if (m_SetupName.length())
    	{
    		requestCmd = requestType;
    		requestCmd += " ";
    		requestCmd += m_RequestsMrl;
    		requestCmd += "/";
    		requestCmd += m_SetupName;
    		requestCmd += " RTSP/1.0";
    
    		m_SetupName = "";
    	}
    	else
    	{
    		requestCmd = requestType;
    		requestCmd += " ";
    		requestCmd += m_RequestsMrl;
    		requestCmd += " RTSP/1.0";
    	}
    
    
    	//当password为ANSI字符串 response = md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<uri>));
    	
    	//md5(<username>:<realm>:<password>)=md5(admin:4419b727ab09:12345)=fa47d934c754db5ebebd9b42a4412073
    	sprintf_s(szInput, 200, "%s:%s:%s", m_uername.c_str(), m_realm.c_str(), m_password.c_str());
    	md5_hash(szInput, strlen(szInput), szMd5Pwd);
    	
    	//md5(<cmd>:<uri>)=md5(DESCRIBE:rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream) =0b0a4e77e852876bdfdf6ecb72ba3900
    	sprintf_s(szInput, 200, "DESCRIBE:%s", m_RequestsMrl.c_str());
    	md5_hash(szInput, strlen(szInput), szmd5methorduri);
    	
    	//md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<uri>));
    	//=md5(fa47d934c754db5ebebd9b42a4412073:66bb9f0bf5ac93a909ac8e88877ae727:0b0a4e77e852876bdfdf6ecb72ba3900)=108084646408d21aa255664781c886fc
    	//关于这一条,http://www.3464.com/Tools/MD5/index.asp 网站给出的结论则不一致:98f90a583e5c436f83da956c9b00767b 
    <span style="white-space:pre">	</span>//我的代码给出的值与VLC和其它工具给出的值完全匹配
    	sprintf_s(szInput, 200, "%s:%s:%s", szMd5Pwd, m_nonce.c_str(), szmd5methorduri);
    	md5_hash(szInput, strlen(szInput), szOutput);
    	m_response = szOutput;
    
    	_snprintf(cseq, 256, "CSeq: %u", m_CSeq);
    	_snprintf(author, 500, "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
    		m_uername.c_str(), m_realm.c_str(), m_nonce.c_str(), m_RequestsMrl.c_str(), m_response.c_str());
    	_snprintf(acc, 500, "Accept: application/sdp");
    
    	if (requestType.compare("TEARDOWN") == 0)
    		m_Session = 0;
    	_snprintf(session, 256, "Session: %I64u", m_Session);
    
    	Write(requestCmd.c_str());
    	Write(cseq);
    	Write(author);
    	Write("User-Agent: LibVLC/2.1.3 (LIVE555 Streaming Media v2014.01.21)");
    	Write(acc);
    	if (m_Session > 0)
    		Write(session);
    
    	WriteFields();
    	Write("");
    }
    
     

    【S3-REPLY】

    Response: RTSP/1.0 200 OK\r\n
    CSeq: 4\r\n
    Content-type: application/sdp
    Content-Base: rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/\r\n
    Content-length: 672
    \r\n
    Session Description Protocol
    Session Description Protocol Version (v): 0
    Owner/Creator, Session Id (o): - 1417089581269778 1417089581269778 IN IP4 192.168.1.145
    Session Name (s): Media Presentation
    E-mail Address (e): NONE
    Bandwidth Information (b): AS:5100
    Time Description, active time (t): 0 0
    Session Attribute (a): control:rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/
    Media Description, name and address (m): video 0 RTP/AVP 96
    Bandwidth Information (b): AS:5000
    Media Attribute (a): control:rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/trackID=1
    Media Attribute (a): rtpmap:96 H264/90000
    Media Attribute (a): fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z0KAH4iLUCgC3QgAADhAAAr8gCA=,aM44gA==
    Media Description, name and address (m): audio 0 RTP/AVP 0
    Bandwidth Information (b): AS:50
    Media Attribute (a): control:rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/trackID=2
    Media Attribute (a): rtpmap:0 PCMU/8000
    Media Attribute (a): Media_header:MEDIAINFO=494D4B48010100000400010010710110401F000000FA000000000000000000000000000000000000;
    Media Attribute (a): appversion:1.0
     

    首先第一行给了OK,表明上一个命令成功--DESCRIBE成功得到服务器认证
    其次,给出了服务器的媒体信息-具有两路码流--音频(audio)和视频(video) 并给出了请求两路数据的地址

    5、请求视频资源

    【C4-SETUP】

    Request: SETUP rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/trackID=1 RTSP/1.0\r\n
    CSeq: 5\r\n
    Authorization: Digest username="admin", realm="4419b727ab09", nonce="66bb9f0bf5ac93a909ac8e88877ae727", uri="rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/", response="dfd7cfc85819d15d622e4491ad12217d"\r\n
    User-Agent: LibVLC/2.1.3 (LIVE555 Streaming Media v2014.01.21)\r\n
    Transport: RTP/AVP;unicast;client_port=56732-56733

    \r\n

    请求获取流媒体服务器上的视频资源 并告知服务器可以往客户端56732端口发送视频数据,56733为RTCP接收端口,发送方式为单播。传输方式为UDP 

    **********************传输方式与Transport string的对应关系**********************

    UDP传输:Transport:RTP/AVP

    TCP传输:Transport:RTP/AVP/TCP

    RAW UDP传输:Transport:RAW/RAW/UDP

    ****************************************************************************************

    【S4-REPLY】

    Response: RTSP/1.0 200 OK\r\n
    CSeq: 5\r\n
    Session: 1350856257;timeout=60
    Transport: RTP/AVP;unicast;client_port=56732-56733;server_port=8208-8209;ssrc=4a3a67d5;mode="play"
    Date:  Thu, Nov 27 2014 11:59:41 GMT\r\n

    \r\n

    服务器同意发送视频数据给客户端 56732为视频接收端,56733为RTCP接收端口,发送方式为单播。传输方式为UDP 

    6、请求音频资源

    【C5-SETUP】

    Request: SETUP rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/trackID=2 RTSP/1.0\r\n
    CSeq: 6\r\n
    Authorization: Digest username="admin", realm="4419b727ab09", nonce="66bb9f0bf5ac93a909ac8e88877ae727", uri="rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/", response="dfd7cfc85819d15d622e4491ad12217d"\r\n
    User-Agent: LibVLC/2.1.3 (LIVE555 Streaming Media v2014.01.21)\r\n
    Transport: RTP/AVP;unicast;client_port=56734-56735
    Session: 1350856257

    \r\n

    请求获取服务器上的音频资源 并告知服务器可以往客户端56734端口发送视频数据,56735为RTCP接收端口,发送方式为单播。传输方式为UDP 
     

    【S5-REPLY】

    Response: RTSP/1.0 200 OK\r\n
    CSeq: 6\r\n
    Session: 1350856257;timeout=60
    Transport: RTP/AVP;unicast;client_port=56734-56735;server_port=8226-8227;ssrc=232306b5;mode="play"
    Date:  Thu, Nov 27 2014 11:59:41 GMT\r\n

    \r\n

    服务器同意发送音频数据给客户端 56734为视频接收端,56735为RTCP接收端口,发送方式为单播。传输方式为UDP 

    7、开始数据接收

    【C6-PLAY】

    Request: PLAY rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/ RTSP/1.0\r\n
    CSeq: 7\r\n
    Authorization: Digest username="admin", realm="4419b727ab09", nonce="66bb9f0bf5ac93a909ac8e88877ae727", uri="rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/", response="4c4e89a5894d2b208489fde1aeaf9e44"\r\n
    User-Agent: LibVLC/2.1.3 (LIVE555 Streaming Media v2014.01.21)\r\n
    Session: 1350856257
    Range: npt=0.000-\r\n

    \r\n

    告知服务器一切准备就绪,数据可以发过来了!come on baby!

    【S6-REPLY】

    Response: RTSP/1.0 200 OK\r\n
    CSeq: 7\r\n
    Session: 1350856257
    RTP-Info: url=rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/trackID=1;seq=31283;rtptime=1712470431,url=rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/trackID=2;seq=3370;rtptime=1846536290\r\n
    Date:  Thu, Nov 27 2014 11:59:41 GMT\r\n

    \r\n

    服务器告知客户端,朋友我已经送数据过来了,你可以接收了!
    这个时候我们打开Wireshark,在过滤栏输入rtp,可以看到如下数据,表明服务器已经向客户端的56732、56734端口发送数据

    RTP拆包到图像显示和音频播放,会在我的下一篇博客中详细解释

    8、关闭播放

    【C7-TEARDOWN】

    Request: TEARDOWN rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/ RTSP/1.0\r\n
    Method: TEARDOWN
    URL: rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/
    CSeq: 9\r\n
    Authorization: Digest username="admin", realm="4419b727ab09", nonce="66bb9f0bf5ac93a909ac8e88877ae727", uri="rtsp://192.168.1.145:554/MPEG-4/ch2/main/av_stream/", response="af15346442151a51f1478da1858778d5"\r\n
    User-Agent: LibVLC/2.1.3 (LIVE555 Streaming Media v2014.01.21)\r\n
    Session: 1350856257

    \r\n

    关闭数据传输 释放服务器资源 如果需要重新获取码流 则需要从1~6再来一次

    【S7-REPLY】

    Response: RTSP/1.0 200 OK\r\n
    Status: 200
    CSeq: 9\r\n
    Session: 1350856257
    Date:  Thu, Nov 27 2014 11:59:42 GMT\r\n

    \r\n

     服务器已经成功关闭当前链接
     

    本文引用:http://blog.163.com/seek_for/blog/static/1116353920116154386537/

    展开全文
  • Java Mail(三):Session、Message详解

    万次阅读 多人点赞 2014-01-06 12:29:25
    上篇文章介绍了JavaMail并实现了一个发送邮件的简单示例,JavaMail API使用上非常灵活,比如,服务器信息可以设置到Session,也可以设置到Transport,收件人可以设置到Message,也可以设置到Transport,如何...

           本文来自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/17909093,转载请注明。
           上篇文章介绍了JavaMail并实现了一个发送邮件的简单示例,JavaMail API使用上非常灵活,比如,服务器信息可以设置到Session中,也可以设置到Transport中,收件人可以设置到Message中,也可以设置到Transport中,如何使用,取决于我们应用程序中的实际情况。本文详细的介绍一下这三个类的主要方法。

    Session


           Session用于收集JavaMail运行过程中的环境信息,它可以创建一个单例的对象,也可以每次创建新的对象,Session没有构造器,只能通过如下方法创造实例:
    static SessiongetDefaultInstance(Properties props)
              Get the default Session object.
    static SessiongetDefaultInstance(Properties props,Authenticator authenticator)
              Get the default Session object.
    static SessiongetInstance(Properties props)
              Get a new Session object.
    static SessiongetInstance(Properties props,Authenticator authenticator)
              Get a new Session object.
           getDefaultInstance得到的始终是该方法初次创建的缺省的对象,而getInstance得到的始终是新的对象,Authenticator的使用后面会说到。通过Session可以创建Transport(用于发送邮件)和Store(用于接收邮件),Transport和Store是JavaMail API中定义好的接口,通过上文我们知道JavaMail分为API和service provider两部分,API定义了相关接口(eg.:Transport and Store),service provider中实现了这些接口,这些实现类配置在名为javamail.providersjavamail.default.providers的文件中,该文件放在mail.jar/smtp.jar/pop3.jar/imap.jar中的META-INF下,文件内容格式如:
    # JavaMail IMAP provider Sun Microsystems, Inc
    protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=Sun Microsystems, Inc;
    protocol=imaps; type=store; class=com.sun.mail.imap.IMAPSSLStore; vendor=Sun Microsystems, Inc;
    # JavaMail SMTP provider Sun Microsystems, Inc
    protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=Sun Microsystems, Inc;
    protocol=smtps; type=transport; class=com.sun.mail.smtp.SMTPSSLTransport; vendor=Sun Microsystems, Inc;
    # JavaMail POP3 provider Sun Microsystems, Inc
    protocol=pop3; type=store; class=com.sun.mail.pop3.POP3Store; vendor=Sun Microsystems, Inc;
    protocol=pop3s; type=store; class=com.sun.mail.pop3.POP3SSLStore; vendor=Sun Microsystems, Inc;
           每一行声明了协议名称、类型、实现类、供应商、版本等信息,如果需要自己实现相应的协议,必须按照该格式配置好,Java Mail API中才能正确的调用到。Session中提供的创建Trasnsport和Store的方法如下:
     StoregetStore()
              Get a Store object that implements this user's desired Store protocol.
     StoregetStore(Provider provider)
              Get an instance of the store specified by Provider.
     StoregetStore(String protocol)
              Get a Store object that implements the specified protocol.
     StoregetStore(URLName url)
              Get a Store object for the given URLName.
     TransportgetTransport()
              Get a Transport object that implements this user's desired Transport protcol.
     TransportgetTransport(Address address)
              Get a Transport object that can transport a Message of the specified address type.
     TransportgetTransport(Provider provider)
              Get an instance of the transport specified in the Provider.
     TransportgetTransport(String protocol)
              Get a Transport object that implements the specified protocol.
     TransportgetTransport(URLName url)
              Get a Transport object for the given URLName.
           可以看到,重载了很多,这些方法最终都会去解析上文提到的配置文件,找到对应配置信息。

    Message


           Message是邮件的载体,用于封装邮件的所有信息,Message是一个抽象类,已知的实现类有MimeMessage。一封完整的邮件都有哪些信息呢?我们打开一个邮件客户端,我用的是FoxMail,新建一封邮件,如下图所示:
           这就是一封完整的邮件包含的所有信息,默认情况下是没有暗送和回复设置的,可以通过菜单栏-->查看-->暗送地址/回复地址来显示出来,回复地址默认情况下为发件人,暗送是比较猥琐的发邮件方式,暗送邮件除了被暗送者,没有人能知道暗送给谁了,邮件头信息中也不会记录。下面来看下Message中设置邮件信息的一些方法。

    发件人

    abstract  voidsetFrom()
              Set the "From" attribute in this Message.
    abstract  voidsetFrom(Address address)
              Set the "From" attribute in this Message.
           现在大多数SMTP服务器要求发件人与连接账户必须一致,否则会抛出验证失败的异常,这样是防止用户伪装成其它人的账户恶意发送邮件。

    收件人/抄送人/暗送人

    voidsetRecipient(Message.RecipientType type,Address address)
              Set the recipient address.
    abstract  voidsetRecipients(Message.RecipientType type,Address[] addresses)
              Set the recipient addresses.
           第一个方法设置单个人,第二个方法设置多个人。方法中第一个参数涉及到另一个类RecipientType,该类是Message的静态内部类,期内有三个常量值:
    static Message.RecipientTypeBCC
              The "Bcc" (blind carbon copy) recipients.
    static Message.RecipientTypeCC
              The "Cc" (carbon copy) recipients.
    static Message.RecipientTypeTO
              The "To" (primary) recipients.
           TO - 收件人;CC - 抄送人;BCC - 暗送人。

    回复人

     voidsetReplyTo(Address[] addresses)
              Set the addresses to which replies should be directed.
           设置收件人收到邮件后的回复地址。

    标题

    abstract  voidsetSubject(String subject)
              Set the subject of this message.
           设置邮件标题/主题。

    内容

     voidsetContent(Multipart mp)
              This method sets the given Multipart object as this message's content.
     voidsetContent(Object obj,String type)
              A convenience method for setting this part's content.
     voidsetText(String text)
              A convenience method that sets the given String as this part's content with a MIME type of "text/plain".
           设置邮件文本内容、HTML内容、附件内容。

    示例


           下面来看一个稍复杂点的示例:
    public class JavaMailTest2 {
    
    	public static void main(String[] args) throws MessagingException {
    		Properties props = new Properties();
    		// 开启debug调试
    		props.setProperty("mail.debug", "true");
    		// 发送服务器需要身份验证
    		props.setProperty("mail.smtp.auth", "true");
    		// 设置邮件服务器主机名
    		props.setProperty("mail.host", "smtp.163.com");
    		// 发送邮件协议名称
    		props.setProperty("mail.transport.protocol", "smtp");
    		
    		// 设置环境信息
    		Session session = Session.getInstance(props, new Authenticator() {
    			// 在session中设置账户信息,Transport发送邮件时会使用
    			protected PasswordAuthentication getPasswordAuthentication() {
    				return new PasswordAuthentication("java_mail_001", "javamail");
    			}
    		});
    		
    		// 创建邮件对象
    		Message msg = new MimeMessage(session);
    		// 发件人
    		msg.setFrom(new InternetAddress("java_mail_001@163.com"));
    		// 多个收件人
    		msg.setRecipients(RecipientType.TO, InternetAddress.parse("java_mail_002@163.com,java_mail_003@163.com"));
    		// 抄送人
    		msg.setRecipient(RecipientType.CC, new InternetAddress("java_mail_001@163.com"));
    		// 暗送人
    		msg.setRecipient(RecipientType.BCC, new InternetAddress("java_mail_004@163.com"));
    		
    		// 主题
    		msg.setSubject("中文主题");
    		// HTML内容
    		msg.setContent("<div align=\"center\">你好啊</div>", "text/html;charset=utf-8");
    		
    		// 连接邮件服务器、发送邮件、关闭连接,全干了
    		Transport.send(msg);
    	}
    
    }
           本文来自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/17909093,转载请注明。
    展开全文
  • 使用MailCore创建iOS邮件客户端(一)

    万次阅读 热门讨论 2012-12-05 10:13:58
    本教程以IMAP 为例,教你如何利用MailCore框架创建自己的iOS邮件客户端。邮件客户端一般会包含收、发邮件两个功能。相对于发送邮件,接收邮件要复杂得多。本文采用IMAP协议实现邮件的接收,正如Mail程序所做的一样。...
  • java mail 学习笔记

    千次阅读 2017-03-28 22:04:20
    JAVA MAIL 学习 笔记电子邮件协议的简介SMTP 简单邮件传输SMTP是Simple Mail Transfer Protocol的...在Java Mail API,基于Java Mail的程序将与本公司或Internet服务提供商(ISP)的SMTP服务器通信,该SMTP服务器将会
  • putty连接linux 中文输入及显示问题

    千次阅读 2009-02-19 23:55:00
    我的这些文字是从这几年来的 PuTTY 使用经验慢慢得来的,也不仅仅是介绍 PuTTY,还包括了一些相关的软件,比如:优秀的 FTP 工具 FileZilla、功能强大的 SFTP 客户端 WinSCP。 杂七杂八的乱说了一些,这里把大致...
  • SpringBoot集成Java Mail实现邮件发送功能 概述 springboot-email是基于SpringBoot环境集成Java Mail,并实现了邮件发送功能,包括简易邮件发送、html正文邮件发送、内联资源(静态资源)邮件发送、模板邮件发送 ...
  • 本文逐步介绍了如何计划和创建简单邮件传输协议 (SMTP) 连接器,以使 Exchange 计算机可以将邮件传递到外部 Internet 域并从其中接收邮件。 要求下面的列表概括了推荐使用的硬件、软件、网络结构以及所需的服务
  • 概要 对于很多组织,内部和外部电子邮件连接是一种基本的商务和通讯手段。Microsoft Exchange 提供了将内部邮件网络连接到 Internet 上的外部组织的功能。本文逐步介绍了如何计划和创建简单邮件传输协议 (SMT
  • 众所周知,创建数据库连接需要消耗较多的资源,且创建时间也较长。如果网站一天100万PV(假设每个页面都有DB读取或修改操作),程序就需要创建100万次连接,极大的浪费资源。 事实上,同一时间需要创建数据库连接的...
  • android里发送邮件mail的几种方式

    万次阅读 2014-10-23 19:15:06
    在android里进行邮件客户端开发可以有两种方式: ...l 令一种方法是采用javamail功能包 优点:可以设置邮件服务器地址,不必局限于gmail邮箱 缺点:用法比较复杂   下面依次介绍这两种方式:   先看
  • TCPIP-TCP是一种面向连接连接导向)的、可靠的、基于字节流的运输层(Transport layer)通信协议 本页主题:TCPIP 访问次数:1516 相关资料:3 创建时间:2010-12-07 21:33:05 修改时间:2010-12-07 22:19:...
  • 连接是客户和服务器建立连接后不断开,持续用这个连接通信,持续过程一般需要连接侦测,客户探测服务,或者服务心跳告知客户,应用层的保活机制。 短连接是通信一次后就关闭连接。长短连接是一种通信约定,需要...
  • Java Mail发送和接收-长文详解篇

    千次阅读 2020-08-08 12:31:31
    但它并没有包含在JDK,而是作为J2EE的一部分。 厂商所提供的Java Mail服务程序可以有选择地实现某些邮件协议,常见的邮件协议包括: lSMTP:简单邮件传输协议,用于发送电子邮件的传输协议; lPOP3:用于接收...
  • Linux--FTP和MAIL服务器

    千次阅读 2013-12-22 22:14:10
    (6) 修改/etc/mail/access文件,在该文件加入下面一行: [root@lab labsoft] #vi /etc/mail/access mail.test.com Replay (7) 由/etc/mail/access 文件生成/etc/mail/access.db ,使用下面的命令: ...
  • 数据库的连接

    千次阅读 2005-02-25 12:08:00
    数据库的连接串 在数据库的各种应用程序开发连接数据库是数据库应用程序开发的第一步,同时也是最重要...当然很多编程工具能够通过可视化的界面直接产生正确连接字符串,但字符串各个参数的具体含义也不清楚,
  • js常用方法总结

    千次阅读 2017-07-27 22:22:28
    JS选取DOM元素的方法 注意:原生JS选取DOM元素比使用jQuery类库选取要快很多 1、通过ID选取元素 document.getElementById('myid'); 2、通过CLASS选取元素 document.getElementsByClassName('myclass')[0]; 3、...
  • ZendFramework-Zend_Mail-邮件

    千次阅读 2010-01-07 09:53:00
     Zend_Mail通过php内建的mail()函数或者直接通过SMTP连接来发送邮件使用Zend_Mail发送简单邮件 一个简单邮件由一个或者几个收件人,一个主题,一个邮件主体和一个发件人组成。 下面的步骤,使用了PHP的mail()函数...
  • 海康相机RTSP连接代码分析

    千次阅读 2017-03-27 16:32:23
    (1)分析url的端口号,创建一个与服务器[S-PORT]端口号的TCP连接用于RTSP命令交互  (2)执行RTSP命令请求,告诉服务器可以连接到本地的端口[C-PORT]进行数据发送  (3)客户端从本地[C-PORT]读取数据
  • Java mail 实现发送电子邮件

    千次阅读 2010-09-02 12:10:00
    java mail api 提供了一系列的抽象类定义了一个构成邮件系统的对象,api 定义的类如Message,Store,Transport,你可以继承这些类来实现你所需要的功能和协议。 除此之外,api提供了一套具体的实例化的类,这些...
  • UNIX提供了数百个(甚至数千个)命令,您可以使用这些命令来操纵内核和用户空间的各种可用资源。 需要监控CPU使用率吗? 尝试top或ps 。 是否需要删除所有以.bak结尾的文件? 尝试rm *.bak 。 在新命令方面需要...
  • 这个是验证没通过,一般需要检查一下以下几点: 1、你的用户名和授权码是否正确 2、你的邮箱服务是否是你的的发送邮箱再介绍一些异常: 530 Error: A secure connection is requiered(such as ssl)错误原因是...
  • Postfix+Dovecot搭建MailServer配置说明

    万次阅读 2011-12-31 13:41:42
    Postfix+Dovecot搭建MailServer配置说明 1. 概述  MailServer是网络邮件传递及接受服务器,只需注册个人邮箱,通过邮件客户端与他人发送邮件。 MailServer通过SMTP协议发送邮件,提供这种服务器的软件有很多,...
  • 【转】http://f.dataguru.cn/thread-454328-1-1.html 一.... ...二.解决方法:将insert语句‘Y’或‘N’ 改成TRUE或FALSE即可,共两张表3个地方 (1)INSERT INTO R_VERSION(ID_VERSION, MAJOR_VE
  • 诡异的 spring mail 发送邮件问题

    千次阅读 2019-07-04 15:29:06
    项目有个消息系统,通过 RabbitMQ 发送系统的邮件时,采用5个固定线程池去发送邮件。发现虽然有消费,但是日志却没有任何输出相关的成功或失败或抛出异常的信息。 伪代码如下,消费者主线程使用固定线程池...
  • Archiva 创建私服

    千次阅读 2013-05-24 14:59:40
    连接的 repository============== 虽然可以直接修改 pom.xml 文件来更改 repository 地址,但是可重用性并不好。 建议修改 ~/.m2 目录下的 settings.xml 文件 只要建立一个镜像就可以了,如下 ...
  • Unity连接MySQL数据库

    万次阅读 2016-03-30 23:18:09
    前两天研究Socket和C#连接数据库其实都是为了将这些和unity结合使用做的基础学习。 所以最后都会归结到Unity上面。其实学会了C#关于数据库的操作,Unity肯定也就会了。 首先准备工作,新建一个unity项目就不用讲了...
  • 1 调优方案 1.1 调优:如何提高访问连接并发(运维层面) 根据书籍的介绍,kylin单个的实例,支持的访问连接并发是70个左右,所以,为了让kylin能够支持更多的访问并发,可以通过增加实例的方式实现。将单实例的...
  • 第四章 创建一个社交网站

    千次阅读 2017-05-26 10:09:45
    4 创建一个社交网站在上一章,你学习了如何创建站点地图和订阅,并且为博客应用构建了一个搜索引擎。在这一章,你会开发一个社交应用。你会为用户创建登录,登出,编辑和修改密码的功能。你会学习如何为用户创建...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 36,480
精华内容 14,592
热门标签
关键字:

创建mail连接方法中正确的是