计网自顶向下(Web服务器+UDPping+邮件客户端)

news2024/11/26 0:36:33

目录

🐖前言

🌼Web服务器(作业1)

🌳过程

🌳解释

🔥代码

🌼UDPping程序(作业2)

🌳过程

🌳解释

Client

Server

整体逻辑

🔥代码

🌼邮件客户端(作业3)

🍍前置

🌳过程

🌳解释

(3) 邮件结束符

(4)base64库,b64encode(), encode(), decode()

(6) 接收信息

(8) SMTP命令

(9)encode() 和 decode()

🔥代码


🐖前言

19个Wireshark + 课后实验 + TCP/UDP,不包括很多可选练习,比如作业1(Web服务器),包含2个可选练习

(1)实现多线程服务器

(2)手写HTTP客户端代替浏览器来测试服务端请求

这部分等以后有时间了再看,先提高效率学完计网,搞定WebServer先,ddl更重要

以下是作业1,2,3,4的实验,属于应用层

源码地址👇

moranzcw/Computer-Networking-A-Top-Down-Approach-NOTES: 《计算机网络-自顶向下方法(原书第6版)》编程作业,Wireshark实验文档的翻译和解答。 (github.com)

🌼Web服务器(作业1)

🌳过程

Github代码用git bash,git clone到本地,导入vscode

打开cmd,ipconfig,找到INET1里,IPv4后的地址,我的是192.168.***.***

打开新的cmd,对应目录,运行Webserver代码

打开另一个新的cmd来模拟另一台主机,cmd里打开浏览器(注意,此时html代码和WebServer.py代码处于同一目录下)

然后浏览器出现

如果输入不存在的网页html

6789端口号是随便指定的,只要在端口范围内,并且没被占用即可

如何查看是否被占用

(1)Windows,打开Powershell,输入

Test-NetConnection -ComputerName localhost -Port 6789

TcpTestSucceeded : False,表示未被占用,可以使用

(2)cmd输入

netstat -ano

查看所有使用中的端口号

🌳解释

以下是关于代码的详细解释

from socket import *
serverSocket = socket(AF_INET, SOCK_STREAM)

导入了socket模块并创建了一个TCP套接字,使用的协议族是IPv4(AF_INET),传输方式是流(SOCK_STREAM)

serverSocket.bind(('', 6799)) 

 将TCP欢迎套接字绑定到指定的端口号6799和任何可用的IP地址。为空字符串意味着服务器将在本机所有可用的网络接口上监听进来的连接请求

connectionSocket, addr = serverSocket.accept()

(1) connectionSocket是一个代表与客户端建立的TCP连接的套接字,它用于在服务器和客户端之间进行通信

(2)addr是一个元组,包含了客户端的IP地址和端口号。可以使用addr[0]访问IP地址,使用addr[1]访问端口号。这个元组表示与连接套接字关联的客户端的网络地址

message = connectionSocket.recv(1024) 

 从连接套接字中接收报文,其长度不超过1024字节

filename = message.split()[1]

将请求报文拆分并提取出请求的文件名,具体👇 

 message.split()是将message字符串按空格拆分成一个字符串列表

message = "GET /hello.html HTTP/1.1\r\nHost: www.example.com\r\n\r\n"
message_list = message.split()
print(message_list)

输出

['GET', '/hello.html', 'HTTP/1.1', 'Host:', 'www.example.com']

filename = message.split()[1]提取了拆分后的第二个元素(即'/hello.html'),它是客户端请求的文件名。

[1]表示获取列表中的第二个元素(索引从0开始)。因此filename = message.split()[1]语句的作用是从拆分后的列表中提取请求的文件名

f = open(filename[1:])
outputdata = f.read()

 打开请求的文件并读取其内容。注意,这里filename[1:]是为了去掉请求报文中的"/"。

header = ' HTTP/1.1 200 OK\nConnection: close\nContent-Type: text/html\nContent-Length: %d\n\n' % (len(outputdata))
connectionSocket.send(header.encode())

设置HTTP响应头和内容,其中包括状态代码,内容类型,内容长度等信息。在此之后,它通过套接字将响应头发送给客户端。具体👇 

  • HTTP/1.1:这是HTTP协议的版本号,表示使用的是HTTP 1.1版本。
  • 200:这是状态代码,表示请求成功。这里使用的是200,表示服务器成功处理了客户端的请求并返回相应的内容。
  • OK:这是状态消息,与状态代码相对应,表示请求成功。
  • Connection: close:这是指定连接选项的字段,告诉客户端在响应之后关闭连接。这里使用close选项,表示服务器在发送完响应后关闭与客户端的连接。
  • Content-Type: text/html:这是指定响应内容类型的字段,告诉客户端接收到的是HTML类型的内容。在这里,服务器假设响应的内容为HTML文档。
  • Content-Length: %d:这是指定响应内容长度的字段,告诉客户端响应的实体主体内容的长度。%d是一个占位符,后面的(len(outputdata))将会填充实际的内容长度。
  • \n\n:这是两个换行符,表示头部信息结束,后面是实体主体内容
for i in range(0, len(outputdata)):
    connectionSocket.send(outputdata[i].encode())

 将请求的文件内容发送到套接字,具体👇

此处的 outputdata 对应字符串 HelloWorld.html

encode()是将这个字符转换为字节流的方法。encode()方法将Unicode字符串编码为字节序列,以便可以在网络上进行传输。

connectionSocket.close()

 关闭套接字来结束与客户端的通信

except IOError:
    header = ' HTTP/1.1 404 Not Found'
    connectionSocket.send(header.encode())
    connectionSocket.close()

🔥代码

#import socket module
from socket import *
serverSocket = socket(AF_INET, SOCK_STREAM)
# Prepare a server socket
# bind()的参数只有一个, 是ip和端口结合的套接字
serverSocket.bind(('', 6799)) # 将TCP欢迎套接字绑定到指定端口
serverSocket.listen(1) # 最大连接数1

while True:
    # Establish the connection
    print('Ready to serve')
    connectionSocket, addr = serverSocket.accept() # 接受客户端请求后,建立新的TCP连接套接字
    try:
        message = connectionSocket.recv(1024) # 获取客户发送的报文
        filename = message.split()[1]
        f = open(filename[1:])
        outputdata = f.read();
        # Send the content of the requested file to the client

        header = ' HTTP/1.1 200 OK\nConnection: close\nContent-Type: text/html\nContent-Length: %d\n\n' % (len(outputdata))
        connectionSocket.send(header.encode())

        for i in range(0, len(outputdata)):
            connectionSocket.send(outputdata[i].encode())
        connectionSocket.close()
    except IOError:
        # Send response message for file not found
        header = ' HTTP/1.1 404 Not Found'
        connectionSocket.send(header.encode())

        # Close client socket
        connectionSocket.close()
serverSocket.close()

🌼UDPping程序(作业2)

🌳过程

两个cmd模拟两台主机

server👇

另一台主机👇

🌳解释

Client

  1. from socket import *:导入socket模块,这个模块提供了网络通信所需的函数和类。
  2. import time:导入time模块,用于获取当前时间。
  3. serverName = '192.168.15.1':定义服务器地址,这里使用一个远程主机的IP地址。
  4. serverPort = 12000:定义服务器指定的端口号。
  5. clientSocket = socket(AF_INET, SOCK_DGRAM):创建UDP套接字,使用IPv4协议。
    • AF_INET表示使用IPv4地址族。
    • SOCK_DGRAM表示使用UDP协议。
  6. clientSocket.settimeout(1):设置套接字超时时间为1秒。
  7. for i in range(0, 10)::循环10次,发送10个ping消息。
  8. sendTime = time.time():获取当前时间作为发送时间。
  9. message = ('Ping %d %s' % (i+1, sendTime)).encode():生成包含序列号和发送时间的消息,并将其编码为字节串以便发送👇

'Ping %d %s' % (i+1, sendTime)表示将i+1和sendTime插入到字符串'Ping %d %s'中的%d和%s处,%d表示整数类型,%s表示字符类型(包括字符串)。

例如,当i=0和sendTime=1635558427.123456时,上述代码的结果是'Ping 1 1635558427.123456'

  1. try::尝试执行以下代码块。
  2. clientSocket.sendto(message, (serverName, serverPort)):将消息发送到服务器。
    • sendto()函数用于向特定地址发送UDP数据报。
  3. modifiedMessage, serverAddress = clientSocket.recvfrom(1024):接收服务器的响应消息,并同时获取服务器地址。
    • recvfrom()函数用于接收UDP数据报,返回接收到的数据和发送方的地址👇

(1)recvfrom()函数用于接收UDP数据报,并返回两个值:接收到的数据和发送方的地址。发送方的地址由IP地址和端口号的组合表示

(2)IP地址用于标识网络中的主机,而端口号则用于标识主机上运行的应用程序或服务

  1. rtt = time.time() - sendTime:计算往返时间(RTT)👇

rtt = round trip time  往返时间

  1. print('Sequence %d: Reply from %s RTT = %.3fs' % (i+1, serverName, rtt)):显示收到响应的信息,包括序列号、服务器地址和RTT👇

(1)"%.3fs" 是一个格式化字符串,用于将浮点数值插入到字符串中,并指定小数点后保留三位小数

(2)例如,一个浮点数值rtt为2.34567,则"%.3fs" % rtt 的结果将是"2.346s",保留了三位小数并转换为字符串类型

  1. except Exception as e::如果出现异常,则执行以下代码块。
  2. print('Sequence %d: Request timed out' % (i+1)):显示请求超时的信息。
  3. clientSocket.close():关闭套接字。

Server

  1. from socket import *:导入socket模块。
  2. import random:导入random模块,用于生成随机数。
  3. serverSocket = socket(AF_INET, SOCK_DGRAM):创建UDP套接字。
  4. serverSocket.bind(('', 12000)):将IP地址和端口号绑定到套接字上。
    • ''表示使用任意可用的IP地址。
  5. while True::无限循环,不断处理客户端的请求。
  6. rand = random.randint(0, 10):生成一个0到10之间的随机数。
  7. message, address = serverSocket.recvfrom(1024):接收客户端的请求消息,同时获取客户端的地址。
  8. message = message.upper():将接收到的消息转换为大写形式。
  9. if rand < 4::如果随机数小于4,模拟丢包,不回复客户端。
  10. continue:继续下一次循环。
  11. serverSocket.sendto(message, address):向客户端发送响应消息。
    • sendto()函数用于向特定地址发送UDP数据报

整体逻辑

  1. 客户端通过UDP套接字向服务器发送ping消息。
  2. 服务器接收到客户端的消息后,根据随机数决定是否丢弃该消息。
  3. 如果服务器不丢弃消息,则将消息转换为大写形式并回复客户端。
  4. 客户端收到服务器的响应后,计算往返时间(RTT)并显示结果。
  5. 如果在超时时间内未收到服务器的响应,则显示请求超时的消息。
  6. 循环10次,完成10个ping请求。
  7. 最后关闭客户端的套接字

🔥代码

Client

from socket import *
import time

serverName = '192.168.15.1' # 服务器地址,本例中使用一台远程主机
serverPort = 12000 # 服务器指定的端口
clientSocket = socket(AF_INET, SOCK_DGRAM) # 创建UDP套接字,使用IPv4协议
clientSocket.settimeout(1) # 设置套接字超时1秒

for i in range(0, 10):
    sendTime = time.time()
    message = ('Ping %d %s' % (i+1, sendTime)).encode() # 生成数据报,编码为tytes以便发送
    try:
        clientSocket.sendto(message, (serverName, serverPort)) # 将信息发送到服务器
        modifiedMessage, serverAddress = clientSocket.recvfrom(1024) # 从服务器接受信息,同时得到服务器地址
        rtt = time.time() - sendTime # 计算往返时间
        print('Sequence %d: Reply from %s    RTT = %.3fs' % (i+1, serverName, rtt)) # 显示信息
    except Exception as e:
        print('Sequence %d: Request timed out' % (i+1))
    
clientSocket.close() # 关闭套接字

Server

from socket import *
import random

# create a UDP socket
# notice the use of SOCK_DGRAM for UDP packets
serverSocket = socket(AF_INET, SOCK_DGRAM)
# Assign(分配) IP address and port number to socket
serverSocket.bind(('', 12000))

while True:
    # Generate random number(生成随机数) in the range of 0 to 10
    rand = random.randint(0, 10)
    # Receive the client packet along with the address it is coming from
    message, address = serverSocket.recvfrom(1024)
    # Capitalize(使大写) the message from the client
    message = message.upper()
    # If rand is less is than 4, we consider the packet lost and do not respond
    if rand < 4:
        continue
    # Otherwise, the server responds
    serverSocket.sendto(message, address)

🌼邮件客户端(作业3)

🍍前置

关于SMTP👇

介绍

当我们发送一封电子邮件时,SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)起着至关重要的作用。SMTP 是用于在网络上传输电子邮件的标准协议,它定义了电子邮件是如何被发送和接收的

重点

  1. 寄信流程: 当你发送一封电子邮件时,你的邮件客户端(如Gmail、Outlook等)会将这封邮件发送给你所使用的邮件服务器。这个过程就是通过 SMTP 完成的

  2. 转发邮件: SMTP 也用于将邮件从一个邮件服务器传输到另一个邮件服务器。例如,当你发送一封邮件给另一个域名的邮箱时,你的邮件服务器就会使用 SMTP 将邮件传输到对方的邮件服务器

  3. 端口: SMTP 默认使用端口25进行通信,但为了安全起见,常常也会使用加密端口如587或465。这些端口提供了加密和身份验证功能,以保护邮件传输过程中的数据安全

  4. 简单性: SMTP 的"Simple"部分意味着它的设计相对简单,易于理解和实现。这使得它成为电子邮件传输的基本协议    

关于MX记录👇

(1)邮件交换记录(Mail Exchange record)的缩写

在域名系统(DNS)中,MX记录指定了负责接收该域名电子邮件的邮件服务器。当某人发送电子邮件到一个特定的域名时,发件人的邮件服务器会查询目标域名的MX记录,以确定应该将邮件传递到哪个邮件服务器 

(2)包括两个部分:优先级邮件服务器地址

优先级指定了邮件服务器的优先顺序,当一个域名有多个MX记录时,优先级较高的邮件服务器会被优先选择。邮件服务器地址则是指定了接收该域名邮件的邮件服务器的地址

(3)举例

一个域名可能具有多个MX记录,每条记录对应一个邮件服务器,而这些邮件服务器可能是按照优先级排序的,这样就可以确保即使主要的邮件服务器不可用,电子邮件仍然可以被正确地发送到备用的邮件服务器上

补充理解👇

如何验证 Email 地址:SMTP 协议入门教程 - 阮一峰的网络日志 (ruanyifeng.com)

SMTP协议详解及工作过程_51CTO博客_smtp协议

🌳过程

(1)首先你需要一个163和一个qq邮箱

(2)登录163邮箱,可以先看看这个👇

(3)开启SMTP (Simple Mail Transfer Protocol)

具体操作👇

首页这里有个设置,点击

然后手机扫码发送短信授权

把这串字符作为代码中的密码即可 

(4)将代码中的发送 / 接受邮箱,用户名和授权码改为自己的

第一次出现以下BUG👇

正确应该是 235 Authentication successfully

而不是535 Error: authentication failed

然后一看,发现改错代码了👇

对应修改后,成功!👇

163邮箱👇(发)

qq邮箱👇(收)

🌳解释

(1)整体思路

  1. 导入模块:从Python标准库中导入了socketbase64模块,用于网络通信和Base64编码。

  2. 设置邮件内容相关信息:包括邮件主题、类型和内容,以及邮件结束符号。

  3. 选择邮件服务器:设置邮件服务器的地址为"smtp.163.com"。

  4. 设置发件人和收件人的邮箱地址。

  5. 对发件人的认证信息进行Base64编码,包括用户名和密码。

  6. 创建一个套接字(socket)clientSocket,并与邮件服务器建立TCP连接​​​​​​

  7. 通过套接字与邮件服务器进行交互:

    • 发送HELO命令并打印服务器响应。
    • 进行身份验证,包括发送用户名和密码,并接收服务器的响应进行验证。
  8. 发送邮件相关命令:

    • 发送MAIL FROMRCPT TO命令,用于指定发件人和收件人。
    • 发送DATA命令表示即将发送邮件内容。
    • 发送邮件内容,并以单个点作为结束标识。
    • 发送QUIT命令表示退出连接。
  9. 关闭套接字,断开连接

(2) 邮箱服务器SMTP地址

(3) 邮件结束符

endmsg = "\r\n.\r\n"  # 邮件结束符

在SMTP协议中,当发送完整的邮件内容后,需要使用"\r\n.\r\n"来表示邮件内容的结束。这个字符串告诉邮件服务器已经发送完所有邮件数据,服务器可以开始处理这封邮件了

"回车"(\r)指示打印头或光标返回到当前行的开头

"换行"(\n)指示将光标移动到下一行,并且可以包括回车操作以定位到下一行的开头

  • 在 Unix、Linux、macOS 等系统中,使用的是换行符(\n),即表示为一个字符。
  • 在 Windows 系统中,通常使用回车加换行的组合(\r\n)表示换行

实际的文本文件中,换行通常表示为回车加换行的组合(\r\n),这样可以确保在不同操作系统上都能正确地显示换行效果 

所以,"\r\n.\r\n" 用于结束邮件的发送,它包含两个换行和一个句点,用于标记邮件的结束 

(4)base64库,b64encode(), encode(), decode()

# Auth information (Encode with base64) (认证信息,使用base64编码)
username = base64.b64encode(fromaddress.encode()).decode()
password = base64.b64encode("*************".encode()).decode()

(一) 

fromaddress,邮箱地址的字符串,它包含用户名和域名

比如"user@example.com",其中"user"是邮箱的用户名,"example.com"是邮箱的域名

  • base64是一个Python标准库,提供了对Base64编码和解码的支持。
  • b64encodebase64库中的一个函数,用于对数据进行Base64编码

encode(): 这个方法用于将字符串编码为指定的编码格式,返回一个 bytes 对象

decode(): 这个方法用于将 bytes 对象解码为指定的字符串格式,返回一个字符串 

(二) 

a. encode()将字符串编码为字节对象

(因为字符串是由Unicode字符组成的,而计算机处理和传输数据时一般使用字节数据。因此,需要将字符串转换为字节对象以进行后续的处理和编码)

b.  b64encode()对字节对象进行base64编码

(Base64编码是将数据转换为只包含可打印ASCII字符的编码形式。它通常用于在文本协议中传输二进制数据)

c. decode()将编码后的结果解码为字符串

(将字节对象转换为Base64编码后,得到的是一个表示编码形式的字节序列。如果我们要将其作为字符串使用或展示,需要使用decode()方法将其解码为字符串形式)

(三) 

字符串通过字节编码、Base64编码和解码操作,获得的结果是一个与原来字符串内容相同的新字符串。但是它们的数据类型和编码形式是不同的 

(5)创建套接字,并与服务器建立TCP连接

# 创建套接字并与邮件服务器建立TCP连接
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((mailserver, 25)) # 邮箱地址和端口号

socket() 创建一个套接字对象 clientSocket

AF_INET 参数表示使用 IPv4 地址族,SOCK_STREAM 参数表示这是一个面向连接的 TCP 套接字

(mailserver, 25) 是一个元组,包含了要连接的目标服务器的地址和端口号

一旦调用了 connect 方法,客户端套接字就会尝试连接到指定的邮件服务器

(6) 接收信息

recv = clientSocket.recv(1024).decode()

使用 recv 方法从服务器端接收最多 1024 字节的数据,并将其解码为字符串

这个方法会阻塞程序,直到有数据到达或者连接关闭,如果没有数据到达,传输失败,print(recv)将不会输出任何结果 

换种解释就是👇

.recv 方法是在 Python 的 socket 编程中用于接收数据的方法。它用于从连接的另一端接收数据,其语法通常为 socket.recv(buffer_size)。其中,buffer_size 参数指定了一次性可以接收的最大数据量。调用 .recv 方法将会阻塞程序直到有数据到达,或者直到连接被关闭

(7) Python语法

if recv1[:3] != '250':
    print('250 reply not received from server.')

[:3] 表示前3个字符

(8) SMTP命令

"HELO" 等命令是 SMTP 协议规定的命令之一,用于客户端向服务器打招呼并标识自己。这个命令是固定的,不能随意更改

  1. MAIL FROM: sender@example.com\r\n 这是用于指定邮件的发件人地址的命令。在这个例子中,使用了字符串拼接来构造这个命令,并通过 clientSocket 发送到邮件服务器。

  2. RCPT TO: recipient@example.com\r\n 这是用于指定邮件的收件人地址的命令。类似地,这个命令也是用字符串拼接构造的,并发送到邮件服务器。

  3. DATA\r\n 这个命令告诉邮件服务器即将发送邮件数据。一旦收到这个命令,服务器会准备接收邮件内容。

  4. message = 'from:' + fromaddress + '\r\n' ... 这段代码构造了邮件的内容,包括发件人、收件人、主题、内容类型等信息,并将其转换成符合 SMTP 协议格式的字符串。

  5. clientSocket.sendall(message.encode()) 这里使用 sendall() 方法发送邮件内容的字符串到服务器。

  6. clientSocket.sendall(endmsg.encode()) endmsg 可能是表示邮件内容结束的标识,通过 sendall() 发送到服务器。

  7. recv = clientSocket.recv(1024).decode() 这里是接收服务器返回的响应消息,然后根据响应消息进行相应的处理,比如判断是否成功发送邮件

(9)encode() 和 decode()

发送数据👇 

send()方法是用来发送TCP数据的,而sendall()方法则会在必要时将所有数据发送完毕 

  • 每次调用send或者sendall发送数据之前都需要通过encode方法将字符串编码为字节对象,这是因为网络传输的数据必须是字节类型。
  • 调用encode方法后,会将字符串转换为特定的字节编码形式(如UTF-8),得到一个字节对象作为发送的数据

接受数据👇

  • 每次从套接字接收到数据后,获得的是字节对象,需要通过decode方法将字节对象解码成字符串,以便我们能够处理和理解这些数据。
  • 调用decode方法后,会根据指定的编码方式将字节对象解码为字符串

(10)前半部分多次 send(), sendall(), recv() 的作用

(一)客户端需要将邮件按照 SMTP 协议要求的格式逐步发送给服务器,以确保服务器能够正确地接收并处理邮件内容

(二)调用了 recv 方法来接收服务器的响应

客户端可以了解到是否有任何错误发生,以及服务器是否成功接收并处理了客户端发送的邮件内容

最后的几行message,作用是发送邮件信息,要求遵循SMTP协议规范 

(11)MAIL FROM 和 RCPT TO 命令

clientSocket.sendall(('MAIL FROM: <' + fromaddress + '>\r\n').encode())

clientSocket.sendall(('RCPT TO: <' + toaddress + '>\r\n').encode())

 < 和    在这个代码中是为了表示电子邮件地址的开始和结束符号。在SMTP协议中,使用<>来标识一个完整的电子邮件地址。在这段代码中,   和    用于包围 fromaddress 和 toaddress 变量,在电子邮件中指定发送人和收信人

🔥代码

当我尝试去掉代码中的部分send, sendall(), recv时,程序报错502,因为邮件服务端对认证方式有限制,需要按特定格式发送(也就是说,只保留开头部分和结尾message部分,去掉中间的多次认证,是不行的,任何一个命令,都不能少)

# 导入模块
from socket import *
import base64

# Mail content
subject = "宝贝"  # 标题
contenttype = "text/plain"  # 类型
msg = "想我了没"  # 内容
endmsg = "\r\n.\r\n"  # 邮件结束符

# Choose a mail server (SMTP服务器地址)
mailserver = "smtp.163.com"

# Sender and reciever (发件人和收件人)
fromaddress = "***********@163.com"
toaddress = "************@qq.com"

# Auth information (Encode with base64) (认证信息,使用base64编码)
# base64编码, 以便进行SMTP身份认证
username = base64.b64encode(fromaddress.encode()).decode()
password = base64.b64encode("*************".encode()).decode()

# Create socket called clientSocket and establish a TCP connection with mailserver
# 创建套接字并与邮件服务器建立TCP连接
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((mailserver, 25)) # 邮箱地址和端口号

recv = clientSocket.recv(1024).decode()
print(recv)
if recv[:3] != '220':
    print('220 reply not received from server.')

# Send HELO command and print server response.
# 发送HELO命令
heloCommand = 'HELO Alice\r\n'
clientSocket.send(heloCommand.encode()) # 发送命令
recv1 = clientSocket.recv(1024).decode() # 接收响应
print(recv1)
if recv1[:3] != '250':
    print('250 reply not received from server.')

# Auth (认证)
# 身份验证的头部信息
# 发送'AUTH LOGIN\r\n'到服务器
clientSocket.sendall('AUTH LOGIN\r\n'.encode())

# 从客户端socket接受消息,长度为1024字节,解码为字符串
recv = clientSocket.recv(1024).decode()
print(recv)
# 检查前3个字符
if (recv[:3] != '334'):
    print('334 reply not received from server')

# 用户名添加到消息末尾并发送给服务器
clientSocket.sendall((username + '\r\n').encode())

# 从客户端socket接收消息
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '334'):
    print('334 reply not received from server')

# 密码添加到消息末尾并发送给服务器
clientSocket.sendall((password + '\r\n').encode())

# 从客户端socket接收消息, 并解码为字符串
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '235'):
    print('235 reply not received from server')

# Send MAIL FROM command and print server response.
# 发送MAIL FROM命令
clientSocket.sendall(('MAIL FROM: <' + fromaddress + '>\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '250'):
    print('250 reply not received from server')

# Send RCPT TO command and print server response.
# 发送RCPT TO命令
clientSocket.sendall(('RCPT TO: <' + toaddress + '>\r\n').encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '250'):
    print('250 reply not received from server')

# Send DATA command
# 发送DATA命令
clientSocket.send('DATA\r\n'.encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '354'):
    print('354 reply not received from server')

# Send message data
# 发送邮件内容
message = 'from:' + fromaddress + '\r\n'
message += 'to:' + toaddress + '\r\n'
message += 'subject:' + subject + '\r\n'
message += 'Content-Type:' + contenttype + '\r\n'
message += '\r\n' + msg
clientSocket.sendall(message.encode())

# Message ends with a single period (邮件以单个点结束)
clientSocket.sendall(endmsg.encode())
recv = clientSocket.recv(1024).decode()
print(recv)
if (recv[:3] != '250'):
    print('250 reply not received from server')

# Send QUIT command and get server response
# 发送QUIT命令
clientSocket.sendall('QUIT\r\n'.encode())

# 关闭套接字 close connection
clientSocket.close()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1190938.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

粤嵌实训医疗项目(小组开发)--day05

目录 一、医生功能模块 ------------前端实现------------ ------------后端接口------------ 功能一&#xff1a;分页查询医生基础信息&#xff08;介绍MybatisPlus如何使用分页&#xff09; 功能二&#xff1a;根据搜索栏名称查找对应医生&#xff08;讲解自定义查询集&…

SRRC认证的必要性:保障电子产品质量安全的重要措施

随着电子产品的普及和应用&#xff0c;对电子产品的质量安全要求也越来越高。为了保障消费者的权益和安全&#xff0c;国家对电子产品进行了严格的监管和管理。其中&#xff0c;SRRC认证是保障电子产品质量安全的重要措施之一。 SRRC认证是指在我国境内生产、销售、使用的无线电…

(动手学习深度学习)第13章 计算机视觉---图像增广与微调

13.1 图像增广 总结 数据增广通过变形数据来获取多样性从而使得模型泛化性能更好常见图片增广包裹翻转、切割、变色。 图像增广代码实现

简单剖析程序的翻译过程!

本文旨在讲解一段源程序如何翻译成机器所能识别的二进制的命令的&#xff0c;希望通过本文&#xff0c;能使读者对一段程序的翻译过程有进一步的认识&#xff01; 这里首先要介绍的是一段程序从编写完成到执行需要经过以下几个步骤&#xff01; 1.预处理 首先讲到的是预处理&…

十八章总结

一.Swing概述 二.Swing常用窗体 1.JFrame窗体 创建一个不可见、具有标题的窗体&#xff0c;关键代码&#xff1a; JFrame jfnew JFrame("登陆系统"); Container containerjf.getContentPane(); 删除容器中的按钮&#xff0c;关键代码&#xff1a; container.remo…

Ubuntu22.04配置Go环境

Ubuntu上配置Go环境biCentOS简单多了&#xff0c;有两种方案&#xff0c;一种直接使用apt进行安装&#xff0c;一种自己从官网下载安装包进行安装。 1、使用apt直接安装 更新apt安装包&#xff0c;常规操作 apt update 然后看看apt自带的Go版本是多少 apt list golang 是1…

【C++】复杂的多继承及其缺陷(菱形继承)

本篇要分享的内容是C中多继承的缺陷&#xff1a;菱形继承。 以下为本篇目录 目录 1.多继承的缺陷与解决方法 2.虚继承的底层原理 3.虚继承底层原理的设计原因 1.多继承的缺陷与解决方法 首先观察下面的图片判断它是否为多继承 这实际上是一个单继承&#xff0c;单继承的特…

对象序列化

介绍 作用&#xff1a;以内存为基准&#xff0c;把内存中的对象存储到磁盘文件中去&#xff0c;称为对象序列化。 使用到的流是对象字节输出流&#xff1a;ObjectOutputStream。 ObjectOutputStream序列化方法 序列化对象的要求是对象必须实现序列化接口。 示例&#xff1…

面试算法常考题之-------逆波兰式合集

逆波兰式背景介绍 逆波兰式是一种特殊的数学表达式表示法&#xff0c;它的诞生背景可以追溯到20世纪30年代。当时&#xff0c;波兰数学家Jan Wjtowicz和Wacław Sierpiński提出了一种新的数学表达式表示法&#xff0c;这种表示法将运算符放在操作数之后&#xff0c;而不是传统…

Stable Diffusion webui 源码调试(二)

Stable Diffusion webui 源码调试&#xff08;二&#xff09; 个人模型主页&#xff1a;LibLibai stable-diffusion-webui 版本&#xff1a;v1.4.1 内容更新随机&#xff0c;看心情调试代码~ 分析StableDiffusionProcessingTxt2Img类中的sample函数 Sampler /work/stable-d…

xxx升学助考网登录参数跟栈分析

逆向参数分析&#xff1a; 思路&#xff1a; ​ 如果参数出现的次数比较少&#xff0c;完全可以使用全局搜索 ​ 如果参数出现比较多&#xff0c;建议使用跟栈 网站&#xff1a; 下面运行结果 import base64 # 解密 result base64.b64decode(aHR0cHM6Ly93ZWIuZXd0MzYwLm…

【经验模态分解】3.EMD模态分解算法设计与准备工作

/*** poject 经验模态分解及其衍生算法的研究及其在语音信号处理中的应用* file EMD模态分解算法设计与准备工作* author jUicE_g2R(qq:3406291309)* * language MATLAB* EDA Base on matlabR2022b* editor Obsidian&#xff08;黑曜石笔记软…

Day25力扣打卡

打卡记录 寻找旋转排序数组中的最小值&#xff08;二分&#xff09; 链接 由于是旋转排序数组&#xff0c;所以整个数组有两部分是递增的&#xff0c;选取右侧最后元素&#xff0c;即可将整个数组分为大于该元素和小于该元素&#xff0c;碰头地段即为最小值。 class Solutio…

Effective C++ 系列和 C++ Core Guidelines 如何选择?

Effective C 系列和 C Core Guidelines 如何选择&#xff1f; 如果一定要二选一&#xff0c;我会选择C Core Guidelines。因为它是开源的&#xff0c;有300多个贡献者&#xff0c;而且还在不断更新&#xff0c;意味着它归纳总结了最新的C实践经验。最近很多小伙伴找我&#xff…

系列二、Shiro的核心组件

一、核心组件 # 1、UsernamePasswordToken 封装了用户的登录信息&#xff0c;使用用户的登录信息来创建Token # 2、SecurityManager Shiro的核心组件&#xff0c;负责安全认证和授权 # 3、Subject Shiro的一个抽象概念&#xff0c;包含了用户信息 # 4、Realm 开发者自定义的模块…

AcWing99. 激光炸弹

题目 地图上有 N N N 个目标&#xff0c;用整数 X i , Y i X_i,Y_i Xi​,Yi​ 表示目标在地图上的位置&#xff0c;每个目标都有一个价值 W i W_i Wi​。 注意&#xff1a;不同目标可能在同一位置。 现在有一种新型的激光炸弹&#xff0c;可以摧毁一个包含 R R RR RR 个…

SpringBoot自动配置的原理篇,剖析自动配置原理;实现自定义启动类!附有代码及截图详细讲解

SpringBoot 自动配置 Condition Condition 是在Spring 4.0 增加的条件判断功能&#xff0c;通过这个可以功能可以实现选择性的创建 Bean 操作 思考&#xff1a;SpringBoot是如何知道要创建哪个Bean的&#xff1f;比如SpringBoot是如何知道要创建RedisTemplate的&#xff1f;…

前端-第一部分-HTML

一.初识HTML 1.1 HTML 简介 HTML 全称为 HyperText Mark-up Language&#xff0c;翻译为超文本标签语言&#xff0c;标签也称作标记或者元素。HTML 是目前网络上应用最为广泛的技术之一&#xff0c;也是构成网页文档的主要基石之一。HTML文本是由 HTML 标签组成的描述性文本&a…

在Rust中使用多线程并发运行代码

1.Rust线程实现理念 在大部分现代操作系统中&#xff0c;已执行程序的代码在一个 进程&#xff08;process&#xff09;中运行&#xff0c;操作系统则会负责管理多个进程。在程序内部&#xff0c;也可以拥有多个同时运行的独立部分。这些运行这些独立部分的功能被称为 线程&am…

【js逆向实战】某sakura动漫视频逆向

写在前面 再写一个逆向实战&#xff0c;后面写点爬虫程序来实现一下。 网站简介与逆向目标 经典的一个视频网站&#xff0c;大多数视频网站走的是M3U8协议&#xff0c;就是一个分段传输&#xff0c;其实这里就有两个分支。 通过传统的m3u8协议&#xff0c;我们可以直接进行分…