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

目录

🐖前言

🌼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()

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Postman汉化教程

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 Postman汉化教程 前言 前言 Postman是一款支持http协议的接口调试与测试工具&#xff0c;其主要特点就是功能强大&#xff0c;使用简单且易用性好 。无论是开发人员进行接口…

护眼灯买什么样的好?好用又实惠的护眼台灯推荐

护眼台灯的光照一般比较均匀&#xff0c;相比普通台灯&#xff0c;一般具有防蓝光、防频闪等功能&#xff0c;能够提供一个健康舒适的学习、生活灯光环境&#xff0c;建议选购内置智能感光模式的护眼台灯&#xff0c;以确保灯光亮度一直处于均衡状态&#xff0c;让眼睛更轻松。…

【Spring】AOP实现原理

注册AOP代理创建器 在平时开发过程中&#xff0c;如果想开启AOP&#xff0c;一般会使用EnableAspectJAutoProxy注解&#xff0c;这样在启动时&#xff0c;它会向Spring容器注册一个代理创建器用于创建代理对象&#xff0c;AOP使用的是AnnotationAwareAspectJAutoProxyCreator&…

计算机考研精炼1000题:笔试面试必备攻略

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 ⭐️ 好书推荐 计算机考…

Qt5多线程<12>

1.多线程的简单实现 <1> 在头文件threaddlg.h声明用于界面显示所需的控件,其代码如下: #ifndef THREADDLG_H #define THREADDLG_H#include <QDialog> #include <QPushButton> #include "workthread.h" #define MAXSIZE 5class ThreadDlg : publ…

2023年的低代码:数字化、人工智能、趋势及未来展望

前言 正如许多专家预测的那样&#xff0c;低代码平台在2023年将展现更加强劲的势头。越来越多的企业正在纷纷转向低代码开发&#xff0c;他们希望能够快速开发内部应用程序&#xff0c;并在经济衰退可能出现的情况下保持灵活性。在这个大背景下&#xff0c;低代码平台为企业软件…

各大电商平台关于预制菜品种酸菜鱼销售量

# 导入需要的包 library(rvest) # 用于网页抓取 library(tidyverse) # 用于数据处理 library(stringr) # 用于字符串处理# 设置代理信息 proxy_host <- "www.duoip.cn" proxy_port <- 8000# 设置要爬取的网页 url <- "https://jshk.com.cn/products/sa…

【趣味随笔】YOLO的“进化史”极简版(YOLO v1-->YOLOP)

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

httpRequest库代码示例

python # 首先导入所需的库 library(httpRequest) # 设置主机名和端口号 proxy_host <- proxy_port <- # 使用httpRequest库的get函数下载图片 response <- httpRequest(", proxyHost proxy_host, proxyPort proxy_port) # 确保请求成功 if (response$sta…

bat脚本批量修改文件名称的方法,以及乱码问题解决

当有大量的文件&#xff0c;需要按照对应数据进行文件名称的修改时候&#xff0c;用bat脚本&#xff0c;是一个快速&#xff0c;并且简单的方法。 使用命令 “"ren "&A2&" "&D2&""” 其中A2为需要修改的单元格&#xff0c;D2为…

ORA-00257: Archiver error. Connect AS SYSDBA only until resolved错误解决

错误的原因&#xff1a;是因为服务器分配空间不足&#xff0c;数据库归档日志满导致系统数据库登陆失败。 解决办法&#xff1a;1.删除以前的日志 2.增大归档日志的容量 3.关闭归档模式 一、删除以前的容量 1.登录账号后&#xff0c;查看ORACLE_BASE目录 【oraclelocalhost~】$…

Spring基础(1):两个概念

最近看了点Spring的源码&#xff0c;于是来稍微扯一扯&#xff0c;希望能帮一部分培训班出身的朋友撕开一道口子&#xff0c;透透气。 广义上的Spring指的是Spring整个项目&#xff0c;包含SpringBoot、SpringCloud、SpringFramework、SpringData等等&#xff0c; 本系列文章…

设计模式之发布订阅、观察者模式

一、观察者模式 观察者模式定义了对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都将得到通知&#xff0c;并自动更新 观察者模式属于行为型模式&#xff0c;行为型模式关注的是对象之间的通讯&#xff0c;观察者模式就…

一个java文件的JVM之旅

准备 我是小C同学编写得一个java文件&#xff0c;如何实现我的功能呢&#xff1f;需要去JVM(Java Virtual Machine)这个地方旅行。 变身 我高高兴兴的来到JVM&#xff0c;想要开始JVM之旅&#xff0c;它确说&#xff1a;“现在的我还不能进去&#xff0c;需要做一次转换&#x…

LeetCode算法题解(回溯、难点)|LeetCode332. 重新安排行程

LeetCode332. 重新安排行程 题目链接&#xff1a;332. 重新安排行程 题目描述&#xff1a; 给你一份航线列表 tickets &#xff0c;其中 tickets[i] [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 所有这些机票都属于一个从 JFK&#xff08…

uboot启动linux kernel的流程

目录 前言流程图autoboot_commandrun_command_listdo_bootmdo_bootm_statesdo_bootm_linuxboot_prep_linuxboot_jump_linux 前言 本文在u-boot启动流程分析这篇文章的基础上&#xff0c;简要梳理uboot启动linux kernel的流程。 流程图 其中&#xff0c; autoboot_command位于…

Error creating bean with name ‘apiModelSpecificationReader‘ defined in URL

问题&#xff1a; 启动项目的时候&#xff0c;报错了 org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name apiModelSpecificationReader defined in URL [jar:file:/D:/.gradle/caches/modules-2/files-2.1/io.springfox/sp…

【亚马逊云科技产品测评】活动征文|10分钟拥有一台AWS Linux系统

前言 在数字化时代&#xff0c;AWS云服务扮演着至关重要的角色。AWS&#xff08;Amazon Web Services&#xff09;是亚马逊公司旗下的云计算服务平台&#xff0c;为全球各地的企业、组织和个人开发者提供了一系列广泛而深入的云服务。 在AWS云服务中&#xff0c;计算、存储、数…

selenium css定位

selenium-css定位 element_css driver.find_element(By.CSS_SELECTOR, css表达式)css定位说明 selenium中的css定位&#xff0c;实际是通过css选择器来定位到具体元素&#xff0c;css选择器来自于css语法 css定位优点 语法简洁对比其他定位方式&#xff0c;定位效率更快对…

「Java开发指南」如何用MyEclipse搭建Spring MVC应用程序?(二)

本教程将指导开发者如何生成一个可运行的Spring MVC客户应用程序&#xff0c;该应用程序实现域模型的CRUD应用程序模式。在本教程中&#xff0c;您将学习如何&#xff1a; 从数据库表的Scaffold到现有项目部署搭建的应用程序 使用Spring MVC搭建需要MyEclipse Spring或Bling授…