计网自顶向下(Web服务器+UDPping+邮件客户端)-CSDN博客
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
目录
4base64库b64encode(), encode(), decode()
前言
19个Wireshark + 课后实验 + TCP/UDP不包括很多可选练习比如作业1Web服务器包含2个可选练习
1实现多线程服务器
2手写HTTP客户端代替浏览器来测试服务端请求
这部分等以后有时间了再看先提高效率学完计网搞定WebServer先ddl更重要
以下是作业1234的实验属于应用层
源码地址
Web服务器(作业1)
过程
Github代码用git bashgit clone到本地导入vscode
打开cmdipconfig找到INET1里IPv4后的地址我的是192.168.***.***
打开新的cmd对应目录运行Webserver代码
打开另一个新的cmd来模拟另一台主机cmd里打开浏览器注意此时html代码和WebServer.py代码处于同一目录下
然后浏览器出现
如果输入不存在的网页html
6789端口号是随便指定的只要在端口范围内并且没被占用即可
如何查看是否被占用
1Windows打开Powershell输入
Test-NetConnection -ComputerName localhost -Port 6789
TcpTestSucceeded : False表示未被占用可以使用
2cmd输入
netstat -ano
查看所有使用中的端口号
解释
以下是关于代码的详细解释
from socket import *
serverSocket = socket(AF_INET, SOCK_STREAM)
导入了socket模块并创建了一个TCP套接字使用的协议族是IPv4AF_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
from socket import *
导入socket模块这个模块提供了网络通信所需的函数和类。import time
导入time模块用于获取当前时间。serverName = '192.168.15.1'
定义服务器地址这里使用一个远程主机的IP地址。serverPort = 12000
定义服务器指定的端口号。clientSocket = socket(AF_INET, SOCK_DGRAM)
创建UDP套接字使用IPv4协议。
AF_INET
表示使用IPv4地址族。SOCK_DGRAM
表示使用UDP协议。clientSocket.settimeout(1)
设置套接字超时时间为1秒。for i in range(0, 10):
循环10次发送10个ping消息。sendTime = time.time()
获取当前时间作为发送时间。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'
try:
尝试执行以下代码块。clientSocket.sendto(message, (serverName, serverPort))
将消息发送到服务器。
sendto()
函数用于向特定地址发送UDP数据报。modifiedMessage, serverAddress = clientSocket.recvfrom(1024)
接收服务器的响应消息并同时获取服务器地址。
recvfrom()
函数用于接收UDP数据报返回接收到的数据和发送方的地址
1recvfrom()
函数用于接收UDP数据报并返回两个值接收到的数据和发送方的地址。发送方的地址由IP地址和端口号的组合表示
2
IP地址用于标识网络中的主机而端口号则用于标识主机上运行的应用程序或服务
rtt = time.time() - sendTime
计算往返时间RTT
rtt = round trip time 往返时间
print('Sequence %d: Reply from %s RTT = %.3fs' % (i+1, serverName, rtt))
显示收到响应的信息包括序列号、服务器地址和RTT
1"%.3fs" 是一个格式化字符串用于将浮点数值插入到字符串中并指定小数点后保留三位小数
2例如一个浮点数值rtt为2.34567则"%.3fs" % rtt 的结果将是"2.346s"保留了三位小数并转换为字符串类型
except Exception as e:
如果出现异常则执行以下代码块。print('Sequence %d: Request timed out' % (i+1))
显示请求超时的信息。clientSocket.close()
关闭套接字。
Server
from socket import *
导入socket模块。import random
导入random模块用于生成随机数。serverSocket = socket(AF_INET, SOCK_DGRAM)
创建UDP套接字。serverSocket.bind(('', 12000))
将IP地址和端口号绑定到套接字上。
''
表示使用任意可用的IP地址。while True:
无限循环不断处理客户端的请求。rand = random.randint(0, 10)
生成一个0到10之间的随机数。message, address = serverSocket.recvfrom(1024)
接收客户端的请求消息同时获取客户端的地址。message = message.upper()
将接收到的消息转换为大写形式。if rand < 4:
如果随机数小于4模拟丢包不回复客户端。continue
继续下一次循环。serverSocket.sendto(message, address)
向客户端发送响应消息。
sendto()
函数用于向特定地址发送UDP数据报
整体逻辑
- 客户端通过UDP套接字向服务器发送ping消息。
- 服务器接收到客户端的消息后根据随机数决定是否丢弃该消息。
- 如果服务器不丢弃消息则将消息转换为大写形式并回复客户端。
- 客户端收到服务器的响应后计算往返时间RTT并显示结果。
- 如果在超时时间内未收到服务器的响应则显示请求超时的消息。
- 循环10次完成10个ping请求。
- 最后关闭客户端的套接字
代码
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
介绍
当我们发送一封电子邮件时SMTPSimple Mail Transfer Protocol简单邮件传输协议起着至关重要的作用。SMTP 是用于在网络上传输电子邮件的标准协议它定义了电子邮件是如何被发送和接收的
重点
寄信流程 当你发送一封电子邮件时你的邮件客户端如Gmail、Outlook等会将这封邮件发送给你所使用的邮件服务器。这个过程就是通过 SMTP 完成的
转发邮件 SMTP 也用于将邮件从一个邮件服务器传输到另一个邮件服务器。例如当你发送一封邮件给另一个域名的邮箱时你的邮件服务器就会使用 SMTP 将邮件传输到对方的邮件服务器
端口 SMTP 默认使用端口25进行通信但为了安全起见常常也会使用加密端口如587或465。这些端口提供了加密和身份验证功能以保护邮件传输过程中的数据安全
简单性 SMTP 的"Simple"部分意味着它的设计相对简单易于理解和实现。这使得它成为电子邮件传输的基本协议
关于MX记录
1邮件交换记录Mail Exchange record的缩写
在域名系统DNS中MX记录指定了负责接收该域名电子邮件的邮件服务器。当某人发送电子邮件到一个特定的域名时发件人的邮件服务器会查询目标域名的MX记录以确定应该将邮件传递到哪个邮件服务器
2包括两个部分优先级和邮件服务器地址
优先级指定了邮件服务器的优先顺序当一个域名有多个MX记录时优先级较高的邮件服务器会被优先选择。邮件服务器地址则是指定了接收该域名邮件的邮件服务器的地址
3举例
一个域名可能具有多个MX记录每条记录对应一个邮件服务器而这些邮件服务器可能是按照优先级排序的这样就可以确保即使主要的邮件服务器不可用电子邮件仍然可以被正确地发送到备用的邮件服务器上
补充理解
如何验证 Email 地址SMTP 协议入门教程 - 阮一峰的网络日志 (ruanyifeng.com)
过程
1首先你需要一个163和一个qq邮箱
2登录163邮箱可以先看看这个
3开启SMTP (Simple Mail Transfer Protocol)
具体操作
首页这里有个设置点击
然后手机扫码发送短信授权
把这串字符作为代码中的密码即可
4将代码中的发送 / 接受邮箱用户名和授权码改为自己的
第一次出现以下BUG
正确应该是 235 Authentication successfully
而不是535 Error: authentication failed
然后一看发现改错代码了
对应修改后成功
163邮箱发
qq邮箱收
解释
1整体思路
导入模块从Python标准库中导入了
socket
和base64
模块用于网络通信和Base64编码。设置邮件内容相关信息包括邮件主题、类型和内容以及邮件结束符号。
选择邮件服务器设置邮件服务器的地址为"smtp.163.com"。
设置发件人和收件人的邮箱地址。
对发件人的认证信息进行Base64编码包括用户名和密码。
创建一个套接字socket
clientSocket
并与邮件服务器建立TCP连接通过套接字与邮件服务器进行交互
- 发送
HELO
命令并打印服务器响应。- 进行身份验证包括发送用户名和密码并接收服务器的响应进行验证。
发送邮件相关命令
- 发送
MAIL FROM
和RCPT TO
命令用于指定发件人和收件人。- 发送
DATA
命令表示即将发送邮件内容。- 发送邮件内容并以单个点作为结束标识。
- 发送
QUIT
命令表示退出连接。关闭套接字断开连接
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" 用于结束邮件的发送它包含两个换行和一个句点用于标记邮件的结束
4base64库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编码和解码的支持。b64encode
是base64
库中的一个函数用于对数据进行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 协议规定的命令之一用于客户端向服务器打招呼并标识自己。这个命令是固定的不能随意更改
MAIL FROM: sender@example.com\r\n 这是用于指定邮件的发件人地址的命令。在这个例子中使用了字符串拼接来构造这个命令并通过 clientSocket 发送到邮件服务器。
RCPT TO: recipient@example.com\r\n 这是用于指定邮件的收件人地址的命令。类似地这个命令也是用字符串拼接构造的并发送到邮件服务器。
DATA\r\n 这个命令告诉邮件服务器即将发送邮件数据。一旦收到这个命令服务器会准备接收邮件内容。
message = 'from:' + fromaddress + '\r\n' ... 这段代码构造了邮件的内容包括发件人、收件人、主题、内容类型等信息并将其转换成符合 SMTP 协议格式的字符串。
clientSocket.sendall(message.encode()) 这里使用 sendall() 方法发送邮件内容的字符串到服务器。
clientSocket.sendall(endmsg.encode()) endmsg 可能是表示邮件内容结束的标识通过 sendall() 发送到服务器。
recv = clientSocket.recv(1024).decode() 这里是接收服务器返回的响应消息然后根据响应消息进行相应的处理比如判断是否成功发送邮件
9encode() 和 decode()
发送数据
send()
方法是用来发送TCP数据的而sendall()
方法则会在必要时将所有数据发送完毕
- 每次调用
send
或者sendall
发送数据之前都需要通过encode
方法将字符串编码为字节对象这是因为网络传输的数据必须是字节类型。- 调用
encode
方法后会将字符串转换为特定的字节编码形式如UTF-8得到一个字节对象作为发送的数据
接受数据
- 每次从套接字接收到数据后获得的是字节对象需要通过
decode
方法将字节对象解码成字符串以便我们能够处理和理解这些数据。- 调用
decode
方法后会根据指定的编码方式将字节对象解码为字符串
10前半部分多次 send(), sendall(), recv() 的作用
一客户端需要将邮件按照 SMTP 协议要求的格式逐步发送给服务器以确保服务器能够正确地接收并处理邮件内容
二调用了
recv
方法来接收服务器的响应客户端可以了解到是否有任何错误发生以及服务器是否成功接收并处理了客户端发送的邮件内容
最后的几行message作用是发送邮件信息要求遵循SMTP协议规范
11MAIL 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()