用WINSOCK发送Email,调用FTP

2021-11-12 14:07:14 浏览数 (1)

猫猫用WSOCK32.DLL实现了MQTT协议的发送,其实在TCP/IP上面的所有协议都有完整的文档,你都可以去实现了。到了无人区,很多地方都需要我们去探索,去沉淀。 加菲猫

在今天 TCP/IP 处理所有 Internet 上的通信流. 在局域网上也可以运行 TCP/IP.

我们可以利用这一点, 并在诸如 FTP, IRC, e-mail, WWW 或其它任何 Internet 标准类型的通信. 要达到这样的目的, 需要使用包含在

Windows 中的一个 DLL, 也就是所谓的 WSOCK32.DLL 或相似的名字.

在使用 DLL 中的唯一问题, 直接的, 是我们将不得不控制和处理每一个函数的反应而且这将会造成额外的和不必要的开销.

Windows 已经有一个 ActiveX 叫做 WINSOCK.OCX. 它使用与我们所需要的相同的 DLL, 并已经用方法和事件控制和处理了整个反应, 使

它易于使用.

初识 ActiveX Winsock 控件

方法 说明

Accept(requestID) 该方法用于接收一个特写的连接. 它作为一个参数传递来处理请求.

Bind(LocalPort,LocalIP) 为 CDP 连接指定端口和本地 IP.

Close() 关闭服务器和客户之间的活动的连接.

GetData(Data,Type,maxLen) 用缓冲中的内容填充变量, 使其为空.

Listen() 对象等等连接.

PeekData(Data,Type,maxLen) 用缓冲中的内容填充变量, 但不清空缓冲.

SendData(Data) 发送数据到远程计算机.

事件 说明

Close 当远程计算机关闭当前连接时发生

Connect(Error) 与服务器的连接成功后.

ConnectionRequest(requestID) 当远程计算机发出一个请示时.

DataArrival(BytesTotal) 在从远程计算机上接收到新数据时发生.

Error(number, Description, Scode, Source, HelpFile, HelpContext, CancelDisplay) 当发生后台处理错误时.

SendComplete 数据发送完毕时发生

SendProgress(bytesSent, bytesRemaining) 在发送数据时.

属性 说明

BytesReceived 返回到缓冲中的字节数

LocaHostName 返回本地机器的名字

LocalIP 本地计算机的 IP 地址

LocalPort 数据传送的端口 (客户) 或代表一个连接 (服务器)

Name 对象名

Object 运行时自动创建, 仅在 VFP 中.

Protocol 使用的协议 TCP (0) 或 UDP (1)

属性 说明

RemoteHost 返回远程计算机的端口

RemoteHostIP 返回远程计算机 IP

RemotePort 返回远程计算机上的连接端口

SocketHandle 返回控件当前连接的句柄

State 返回控件的状态:

0 = 已关闭

1 = 打开

2 = 等待连接

3 = 正在连接

4 = 决定主机

5 = 主机已决定

6 = 正在连接

7 = 已连接

8 = 连接被远程计算机关闭

9 = 错误

在 VFP 中使用它

在 VFP 中使用 ActiveX Winsock 时你必须牢牢记住:

在运行任何 ActiveX 方法前, 必须添加 .F. 到应用程序的 AutoYeld 属性 (application.autoyield = .f.);

所有与 Windsock 相关的方法, 事件或属性必须加上 object 前缀(thisFORM.wsock1.OBJECT.connect)

VFP 不能处理字符串中的 CHR(0) 字符. 因此, 如果你要处理带有这些内容的数据就需要一字节一字节地读缓冲中接收到的数据. 例如,

就象连接到 Unix 服务器一样.

让我们看看示例 1 来开始测试我们的过程:

它由两个表单组成: 1 个客户表单和一个服务器表单. 它的功能是从客户传送一个文本信息到服务器. 另外服务器以小写方式返回相同的

信息.

请注意在两个表单的 INIT 方法中的命令 Application.AutoYield = .f.. 这意味着我们告诉 VFP 不处理方法中的每一个代码行, 让

ActiveX 自己控制它的事件. 在表单的 CLOSE 方法中我们包括了 Application.AutoYield = .t. 来允许 VFP 按一般方法控制事件.

在 SERVER 表单的 INIT 方法中有两个重要的命令:

thisFORM.sock1.object.LocalPort = 3001

使用以上命令, ActiveX Winsock 在端口 3001上接收连接.

thisFORM.sock1.object.Listen()

该命令告诉 Winsock to 等待连接. 从此时开始, 上面的命令中定义的端口将打开并等待一个连接.

当检测到连接时, 将触发 ConnectionRequest 事件, 发送这个新连接的句柄作为一个参数. 查看示例中的该方法的详细情况:

This.object.close()

确信没有其它未决的连接并停止等待.

This.object.Accept(requestid)

运行 Accept 方法, 发送连接句柄作为参数. 仅现在连接是设置了的.

现在, 让我们到 CLIENTE 表单, 在发送按钮的 CLICK 方法上:

lc_local_IP = thisFORM.sock1.object.LocalIP

我们用服务器的 IP 地址定义一个变量. 在此情况下, 我们从 Winsock 的 LocalIP 属性来获得当前计算机的地址.

lc_local_Port = 3001

我们定义了一个将连接到服务器端口的变量. 请确保该端口对于客户和服务器是相同的.

thisFORM.sock1.object.close()

确信没有未决的连接, 并不再等待另一个连接.

thisFORM.sock1.object.Connect(lc_local_IP,lc_local_Port)

用 IP 地址和端口为参数运行方法来连接服务器..

在这一点上, 我们可以看到一个循环. 它的主要功能是检查 Winsock 的状态和等待连接的确认.

定义一个期限是重要的, 否则此处的循环将有可能使系统崩溃.

另一个重要的东西是注意循环中的 inkey(0.1), 用于避免不必要的使用 CPU 资源.

在连接成功后并确认后, 我们可以看到以下命令:

thisFORM.sock1.object.SendData(trim(thisFORM.texto.value))

运行该方法来传送数据. 在我们的特写场合, 是发送文本框 TEXTO 中的内容.

在下一阶断, 我们再次看到一个与前面非常相似的循环. 它检查 ENVIADO_OK 变量的内容. 当数据完成后, Winsock 的 SendComplete 事

件将自动运行, 且它将包含一个 .T. 到变量中.

现在我们可以看到另一个等待服务器 (SERVIDOR) 回应的循环.

现在让我们回到 SERVIDOR 表单, 在 Winsock' 的 DataArrival 事件中. 该事件在每次从远程计算机上接收到数据时执行. 缓冲的字节

数作为参数给出.

LPARAMETERS bytestotal

lc_buffer = space(bytestotal)

现在我们用 Winsock 的缓冲大小定义一个变量作为缓存.

this.object.GetData(@lc_buffer)

我们运行 GetData 方法, 给它一个要接收缓冲中的数据的变量名引用 (@). 该方法也会清除 Winsock 的缓冲.

thisFORM.retorno.value = lc_buffer

在这一点上, 我们展示已被 GetData 方法填充了的变量的内容.

this.object.SendData(lower(lc_buffer))

作为一个对客户 (CLIENTE) 的回应我们以小写方式发送缓冲中的内容.

回到 CLIENTE 表单, 在 Winsock 的 DataArrival 方法中, 我们可以看到它获得了 Winsock 的缓冲中的内容并将它放入到文本框

RETORNO 中, 处理结束.

继续捍代码, 我们用 thisFORM.sock1.object.close() 命令关闭连接, 触发服务器 (SERVIDOR) 的 CLOSE 事件:

this.object.Close()

当前连接已经关闭. 无论是在 SERVIDOR 表单还是在 CLIENTE 表单中, 在 CLOSE 事件中的该命令是非常重要的, 否则 Winsock 将耗尽

机器的所有资源, 因为它会在有些地方进行循环.

this.object.Listen()

现在我们运行方法, 这样它可以等待下一个连接.

让我们到使用 Winsock 的示例 2:

创建一个聊天

我们通常希望在我们的程序内部进行聊天. 好吧, 该示例就是关于该功能的. 为了让它更简单些, 该资源以只在局域网中运行的方式创

建, 因为它使用一个表来保存一些重要的信息. 因此它不能在 Internet 连接上工作.

该功能以非常简单的方式运行. 表 USER_ONLINE.DBF 是关键. 在该表中有以下字段:

  字段 类型 大小 说明

USUARIO 字符 15 保存登录的用户名

IP_USER 字符 15 保存计算机的 IP

PORT_USER 数值 5 保存由计算机生成的端口

ON_CHAT 逻辑 定义用户是否在进行聊天

在运行 CHAT 表单集时, 出现的第一个表单是用户表单 (USUARIO). 在该表单上, 我们选择或输入想与之聊天的用户. 在得到确认后, 生

成的注册用 RLOCK 锁住, 因此, 没有其它终端可以再使用它. 然后, 让表单不可见并显示 ON_LINE 表单.

在 ON_LINE 表单的 TIMER1 中计时器 5 秒钟触发一次, 运行 USERS 方法. 最后的运行遍历表试图锁定每一个注册. 在它不能锁住一个

注册时, 就意味着该用户在线, 且必须放入列表框 LISTA 中.

准备好了! 在程序的这一点上, 我们可以控制用户访问并知道谁在线谁不在线.

双击列表框中的用户名, 程序将试图打开一个聊天, 在检查了用户真的在线后 (试着锁注册) 或检查 ON_CHAT = .T. 变量看其是否正在

与另一个用户聊天. 在聊天被设置后, 他会得到一个 IP 号并从表中选择用户端口并试着连接. 这就是 Winsock 什么时候参与进来的.

一但连接成功, 将打开一个新的表单: CHAT 表单. ON_CHAT 变量被设置为 .T. 因此在终端可以与另一个有关的终端进行聊天.

在这一点上, 我们的连接已经成功且 CHAT 窗口已经打开. 现在可以进行通信了.

在打开的 CHAT 表单上, 我们用 CommandButton 命令按钮来发送写到编辑框 MSG 中的文本. 该过程使用了 Send 方法.

要关闭当前的对话, 要做的所有事情就是关闭 CHAT 窗口. 另一个机器上的窗口也会自动关闭. 在这一点上, ON_CHAT 变量已经包含了

.F. 值, 并且可以接收新的聊天请求.

分析该示例中使用的各个方法是重要的, 因为所有便于理解 Winsock 动作的解释都在代码中.

Internet

所有 Internet 通信遵循一些预定的标准 (RFC) 如 HTTP, FTP, POP, SMTP, IRC, 等.

在已有的定义中, 默认情况下, 每一个服务将有一个命令组, 各命令组在接收到该命令后将有一个 reply-code 发送自服务器. 这回复通

信命令是否成功地接收了. 因此, 要使用任何采用 RFC 约定的应用程序, 我们必须预先知道它的命令和回复.

我建议你访问 www.networksorcery.com/enp/default0401.htm 网站来熟悉所有标准. 你也可以用其它搜索引擎来搜索 RFC 标准.

FTP 与 Winsock

在示例 3 中我们将看看它是如何连接到 FTP, 列出 FTP 上的文件名和从 FTP 下载文件的.

FTP 协议使用两个 Winsocks: 其中一个用于管理发送命令到服务器; 另一个接收数据 (文件, 目录等) 传送. 第二个 Winsock 是一个被

动连接, 意思是服务器要连接到它. 对于每一个数据传送它需要通知服务器将要连接的 IP 和端口. 这些数据将由我们的 Gera_Porta()

方法生成.

在命令按钮 Conectar 中我们设置连接. 重要地注意是在 Winsock 的 .Connect 方法被调用后我们必须运行 Wait("220") 方法. 该方法

的主要目的是等待服务器的回复, 其中的 220 意思是连接成功.

然后, 我们运行方法

.Enviar ("User " alltrim(thisFORM.user.value),"331")

该方法发送用户到服务器, 并等待来自服务器的 331 回应.

在用户名和口令被接收后, 运行方法 lista_dir("*.zip"). 该方法询问 FTP 服务器上的所有 *.zip 文件. 可能它找不到任何 zip 文

件, 程序将询问 *.exe.

现在我们有了一个服务器的文件清单, 我们将获取最后的文件并用 .Download(). 方法下载它. 在所有数据都接收完后, 服务器将发送一

个 226 回应, 通知进程结束. 但是, 缓冲仍然保持可用, 因此我们必须等待到下载的结束.

在下载完成后, 程序询问要发送到服务器的文件. 在 .upload() 方法中我们可以观察到代码. 这些代码用 32 Kbytes 缓冲发送文件.

认真查看上面示例中的每一个方法中的代码是重要的, 因为所有的说明都在其中.

用 Winsock 发送的接收 e-mail

在写程序时, 许多开发者需要从程序发送或接收 e-mail. 最流行的方案是通过 Outlook Express. 但有可能计算机中没有该 e-mail 工

具. 咋个办?

在示例 4 中我们将分析一个简单的允许从程序内发送 e-mail 的方法.

从 Conectar 命令按钮中, 运行方法 .Conecta_Pop(). 该方法连接到 POP 服务器交等待一个服务器的 OK 回应. 服务器处理身份鉴定,

发送用户名和口令, 并回到命令按钮.

现在调用 .checa_msg() 方法. 它发送 STAT 命令到服务器, 并等待服务器的 OK 信息. 稍过片刻, 收件箱中的 e-mail 被发送.

使用命令 RETR n, 其中 n 是希望的信息号, 我们必须处理下载各个信息(译者注: 一个信息就是一个邮件).

你可以向该方法传递一个 .t. 参数, 它将删除用 DELE n 命令删除信息 (再次重申 n 是要处理的信息号).

再次回到命令按钮, 我们可以看到 .Conecta_Smtp(). 方法被调用. 该方法连接到 SMTP 服务器并等待回应. 接着, 它用 HELO

nome_da_esta玢o 命令来处理 SMTP 服务器上的身份识别.

现在我们调用 .envia_msg() 方法. 在该方法中, 显示 e-mail 头并且我们必须发送 RSET 命令来开始发送 e-mail 到服务器. 各收件人

用 RCPT TO: endere鏾_email 命令发送. 服务器将不检查该地址.

在所有地址都发送后, 为了准备服务器接收 e-mail, 另一个命令 DATA 将被发送. 该过程以 8K 的包发送.

最后, 我们发送命令 CHR(13) CHR(10) "." CHR(13) CHR(10) 到服务器, 通知信息发送完毕.

看看要发送和接收一个 e-mail 有多容易吧?

一些 SMTP 服务器要求身份鉴定. 身份鉴定可以用两种方法进行:

作为一个用户用他的口令连接到 POP 服务器, 然后立即断开. (我们在检查新的邮件时已经这样做了).

用 UUCODE Base 64 加密口令识别你自己到 SMTP 服务器. 我们将不讨论这个细节, 因为我们必须写一个算法这样我们的示例将太复杂.

但是, 对于使用 Visual FoxPro 7 的开发者, 这只需用一个 VFP 自身的函数: STRCONV (dados,13) 或 STRCONV (dados,14).

我们建议你查看上面示例中的每一个方法, 因为所有解释都在其中.

结论

从以上的示例中, 我们可以注意到在 VFP 中使用 Winsock 是非常简单的任务. 我们所需要做的只是知道命令集和回应, 我们可以处理

Internet 标准 (RFC) 或象在聊天示例中一样开发一个唯一的标准.

我们也可以观察所有示例中的常用方法. 这意味着可以开发一个符合需要标准的单一的单化我们的更多的工作的类.

0 人点赞