diff --git a/.wakatime-project b/.wakatime-project new file mode 100644 index 0000000..ca06bea --- /dev/null +++ b/.wakatime-project @@ -0,0 +1 @@ +Flat Mountain 18 \ No newline at end of file diff --git a/client.zip b/client.zip deleted file mode 100644 index fd8fd00..0000000 Binary files a/client.zip and /dev/null differ diff --git a/client/__init__.py b/client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/client/client.py b/client/client.py index f9d7217..3450a0c 100644 --- a/client/client.py +++ b/client/client.py @@ -11,8 +11,10 @@ class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self): super(MainWindow, self).__init__() self.setupUi(self) + # 设置主窗口界面 self.btnSend.clicked.connect(self.btnSend_onclicked) self.btnConnect.clicked.connect(self.btnConnect_onclicked) + # 绑定按钮点击事件 self.statusbar.showMessage("Disconnected") self.socket: socket = None self.isConnected = False @@ -20,6 +22,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): def closeEvent(self, event): if self.isConnected: self.btnConnect.click() + # 在关闭窗口的时候正常断开连接 self.close() def btnConnect_onclicked(self): @@ -29,22 +32,27 @@ class MainWindow(QMainWindow, Ui_MainWindow): if sp in [-1, 0, len(server_addr) - 1]: QMessageBox.critical(self, "Error", "Invalid address") return + # 检查是否是一个合理的host:port组合 addr, port = server_addr[:sp], server_addr[sp + 1:] try: port = int(port) except: QMessageBox.critical(self, "Error", "Invalid port") return + # 检查port是否为整数 try: self.socket = socket(AF_INET, SOCK_STREAM) self.socket.connect((addr, port)) except: QMessageBox.critical(self, "Error", "Connect failed") return + # 尝试连接服务器 self.btnConnect.setText('Disconnect') self.statusbar.showMessage("Connected") self.isConnected = True + # 设置状态 threading.Thread(target=self.recvmsg_loop).start() + # 开始监听线程 else: self.socket.close() self.btnConnect.setText('Connect') @@ -52,14 +60,17 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.isConnected = False def btnSend_onclicked(self): + # 将消息发向服务器 msg = ':'.join([self.txtIDInput.text(), self.txtNewMsg.toPlainText()]) self.txtNewMsg.clear() self.socket.send(msg.encode()) def recvmsg_loop(self): while True: + # 如未连接则结束此线程 if not self.isConnected: return + # 接收信息 msg = self.socket.recv(1 << 20).decode() print(msg) sp1 = msg.find(':') @@ -68,11 +79,15 @@ class MainWindow(QMainWindow, Ui_MainWindow): msg[sp2 + 1:]) self.txtList.append(str(datetime.now()) + " " + msg) self.txtList.moveCursor(QTextCursor.End) + # 呈现并将文本框滚到最后 if __name__ == "__main__": + # 开启HiDPI支持 QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) app = QApplication(sys.argv) mw = MainWindow() mw.show() + # 创建窗口并显示 app.exec() + # 开始运行 diff --git a/mail/mail.py b/mail/mail.py index c4f1c73..3b3fed9 100644 --- a/mail/mail.py +++ b/mail/mail.py @@ -18,6 +18,7 @@ def static_vars(**kwargs): return decorate +# 检查邮件地址是否合法 @static_vars( matcher=re.compile(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)")) def validate_email(email: str): @@ -28,11 +29,9 @@ class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self): super(MainWindow, self).__init__() self.setupUi(self) + # 设置Ui self.btnSendMail.clicked.connect(self.send_mail) - self.txtFrom.textChanged.connect(self.mail_changed) - - def mail_changed(self): - pass + # 绑定点击事件 def send_mail(self): servaddr = self.txtServer.text() @@ -41,30 +40,38 @@ class MainWindow(QMainWindow, Ui_MainWindow): receiver = self.txtTo.text() subjects = self.txtSubject.text() contents = self.txtContent.toPlainText() + # 获取必要信息 if not validate_email(username) or not validate_email(receiver): QMessageBox.critical(self, "Invalid address", "Invalid From or To") return + # 检查发件人与收件人邮箱是否合法 lw = LogWindow( (servaddr, username, password, receiver, subjects, contents)) lw.exec() + # 启动发送窗口 class LogWindow(QDialog, Ui_Dialog): def __init__(self, data): super(LogWindow, self).__init__() self.setupUi(self) + # 设置Ui threading.Thread(target=self.do_update_status, args=(data, )).start() + # 启动实际发送线程 def do_update_status(self, data): (servaddr, username, password, receiver, subjects, contents) = data ss = socket(AF_INET, SOCK_STREAM) + # 接收数据 try: ss.connect((servaddr, 25)) + # 尝试连接 except Exception: QMessageBox.critical(self, "Connection Failed", "Failed connecting smtp server") return + # 与服务端单次交互及显示到界面 def comm(msg: str): msg += "\r\n" ss.sendall(msg.encode()) @@ -72,15 +79,15 @@ class LogWindow(QDialog, Ui_Dialog): self.textBrowser.append("S: " + ss.recv(1 << 20).decode()) self.textBrowser.moveCursor(QTextCursor.End) - comm("HELO " + servaddr) - comm("AUTH LOGIN") - comm(b64encode(username.encode()).decode()) - comm(b64encode(password.encode()).decode()) - comm("MAIL FROM:<{}>".format(username)) - comm("RCPT TO:<{}>".format(receiver)) - comm("DATA") - comm('\r\n'.join(["Subject: " + subjects, "", contents, "."])) - comm("QUIT") + comm("HELO " + servaddr) # HELO + comm("AUTH LOGIN") # 现在的SMTP服务器要认证了 + comm(b64encode(username.encode()).decode()) # 发送用户名 + comm(b64encode(password.encode()).decode()) # 发送密码 + comm("MAIL FROM:<{}>".format(username)) # 发件人 + comm("RCPT TO:<{}>".format(receiver)) # 收件人 + comm("DATA") # 开始实际数据 + comm('\r\n'.join(["Subject: " + subjects, "", contents, "."])) # 主题与正文 + comm("QUIT") # 结束 if __name__ == "__main__": diff --git a/server/server.py b/server/server.py index 2245054..7ce8a1b 100644 --- a/server/server.py +++ b/server/server.py @@ -1,11 +1,14 @@ from gevent import monkey, spawn monkey.patch_all() +# 引入协程库并替换python的各种内部实现 from socket import * from typing import * +# 当前连接 conns: Set[Tuple[socket, Any]] = set() +# 将消息发至所有客户端 def boardcast(message: str): print(message) for conn, addr in conns: @@ -13,6 +16,7 @@ def boardcast(message: str): conn.send(message.encode()) +# 处理一个新的传入连接 def process_connection(conn: socket, addr): conns.add((conn, addr)) boardcast('server:global:{} connected.'.format(addr)) @@ -22,6 +26,7 @@ def process_connection(conn: socket, addr): if not res_raw: raise except Exception: + # 异常处理:移除当前连接并通知剩余用户 conns.remove((conn, addr)) boardcast('server:global:{} disconnected.'.format(addr)) return @@ -35,4 +40,5 @@ if __name__ == "__main__": s.listen(1 << 16) while True: conn, addr = s.accept() + # 启动一个新的协程来处理连接 spawn(process_connection, conn, addr)