socket网络程序设计实验三,客户端与服务器端一对一聊天(基于python3.7、PyQt5)

上一篇文章socket网络程序设计实验二简单演示了socket编程的大致框架,这次我们加强一下功能,实现客户端与服务器端一对一聊天

本次实验的一个难点是多线程和PyQt界面的实时刷新并显示接收到的信息

老规矩,先上图!

socket网络程序设计实验三,客户端与服务器端一对一聊天(基于python3.7、PyQt5)_第1张图片

一、设计客户端和服务器端界面

和前两篇文章一样,先用Py Designer分别设计服务器和客户端的界面ui,并分别保存为server_4.ui和client_4.ui。
可以参考我的这样布局摆放:

服务器端:

socket网络程序设计实验三,客户端与服务器端一对一聊天(基于python3.7、PyQt5)_第2张图片

客户端:

socket网络程序设计实验三,客户端与服务器端一对一聊天(基于python3.7、PyQt5)_第3张图片

二、Py UIC转化为py文件并补全代码

找到刚才保存的两个ui文件,右键,-> Extarl tools -> Py UIC,就会帮我们自动生成.py文件了,在两个.py文件代码的最后加上:

if __name__ == '__main__':
    app = QApplication(sys.argv)
    MainWindow = QMainWindow()
    ui = Ui_Dialog()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

前面加上:

import sys
import time
import socket
import threading  # 多线程
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QApplication, QFileDialog
from PyQt5.QtCore import QThread, pyqtSignal, QObject

到此为止,可以试运行了,会出现界面,但是现在是没有任何功能的

三、增加代码实现功能

这里因为客户端和服务器端差别较大,我们分开实现客户端和服务器端的功能

(一)服务器端

编程思路:

PyQt界面的显示就是一个主线程,因此要实现socket的并发运行,肯定要创建新进程。

先给类加个初始化方法吧,定义一下类内成员变量:

    def __init__(self):
        self.s = socket.socket()
        self.c = None
        self.msg_send = ''
        self.msg_rec = ''

当点击监听按钮时,启动新线程来监听socket;

    def listen_button(self):        # 监听按钮
        add1 = self.lineEdit.text()
        add2 = self.lineEdit_2.text()
        self.s.bind((add1, int(add2)))
        self.s.listen()
        print('正在监听。。。')
        self.textBrowser.setText('开始监听。。。')
        t1 = threading.Thread(target=self.accept_socket)
        t1.start()

当接收到客户端请求连接时,启动新线程来循环接收消息

    def accept_socket(self):     # 接收socket连接
        while 1:
            print('等待连接中。。。')
            self.c, addr = self.s.accept()
            print('已连接')
            break
        t2 = threading.Thread(target=self.rec_msg)
        t2.start()

    def rec_msg(self):      # 接受信息
        while 1:
            print('等待接受消息中。。。')
            msg = self.c.recv(1024).decode('GB2312')
            print('收到消息', msg)
            self.msg_rec += msg
            print(self.msg_rec)
            # time.sleep(2)

定义断开按钮:

    def break_button(self):     # 断开按钮
        self.s.close()

定义发送按钮:

    def send_button(self):      # 发送按钮
        MSG = '[' + time.ctime() + ']:' +self.lineEdit_3.text() + '\n'
        self.c.send(MSG.encode('GB2312'))
        print('已发送:', MSG)
        self.msg_send += MSG
        self.textBrowser.setText(self.msg_send) # 更新显示的消息
        self.lineEdit_3.setText('') # 发送后清空内容

再将按钮的对应事件关联起来,在retranslateUi方法最后加上:

        self.lineEdit.setText("127.0.0.1")
        self.lineEdit_2.setText("21567")
        self.pushButton_3.clicked.connect(self.send_button)
        self.pushButton_4.clicked.connect(self.listen_button)
        self.pushButton_5.clicked.connect(self.break_button)
        self.pushButton_3.setShortcut(QtCore.Qt.Key_Return)  # 设置回车键快捷发送

Ok,目前位置就已经完成了基本的功能,但是有一点,就是接收到的消息的显示问题,
把self.textBrowser_2.setText(self.msg_rec)放在rec_msg方法中,程序会在接受消息的时候卡死,因为PyQt常规情况下,只有点击按钮才会做出相应发生改变,我们总不能为了接收消息,设置一个刷新键一直点吧。。
百度了一下需要用到PyQt5的QObject和QThread,直接看代码,
新建一个类,继承QObject:

class BackendThread(QObject):   # 用来实时更新显示收到的消息
    update_date = pyqtSignal(str)  # 定义信号类型

    # 定义方法
    def run(self):
        while True:
            self.update_date.emit('')
            time.sleep(1)	# 设置间隔时间

然后在retranslateUi方法最下面加上:

		# 调用刚才的自定义类
        self.backend = BackendThread()
        self.backend.update_date.connect(self.handleDisplay)    # 连接信号事件
        self.thread = QThread()     # 创建进程
        self.backend.moveToThread(self.thread)
        self.thread.started.connect(self.backend.run)
        self.thread.start()

定义handleDisplay方法,显示收到的消息:

    def handleDisplay(self):      # 显示收到的信息
        self.textBrowser_2.setText(self.msg_rec)

完成!

服务器端就可以了,接下来再实现客户端

(二)客户端

编程思路:

有了服务器端的经验,客户端就照着做就好了
初始化方法:

    def __init__(self):
        self.s = socket.socket()
        self.msg_send = ''
        self.msg_rec = ''

定义连接按钮的方法:

    def connect_button(self):       # 连接按钮
        add1 = self.lineEdit.text()
        add2 = self.lineEdit_2.text()
        self.s.connect((add1, int(add2)))
        print('已连接到服务器')
        self.textBrowser.setText('连接成功~')
        t = threading.Thread(target=self.rec_msg)
        t.start()

定义断开按钮方法:

    def break_button(self):     # 断开按钮
        self.s.close()
        print('已关闭服务器')

定义发送按钮方法:

    def send_button(self):      # 发送按钮
        MSG = '[' + time.ctime() + ']:' +self.lineEdit_3.text() + '\n'
        self.s.send(MSG.encode('GB2312'))
        print('已发送:', MSG)
        self.msg_send += MSG
        self.textBrowser.setText(self.msg_send)
        self.lineEdit_3.setText('')

定义接收消息方法:

    def rec_msg(self):      # 接受消息
        while 1:
            print('等待接受消息中。。。')
            msg = self.s.recv(1024).decode('GB2312')
            print('收到消息', msg)
            self.msg_rec += msg
            print(self.msg_rec)

定义实时显示接受的消息方法:

    def handleDisplay(self):      # 显示收到的消息
        self.textBrowser_2.setText(self.msg_rec)

然后导入服务器端代码中我们自定义的类:

from test4.server_4 import BackendThread    # 导入自定义类

最后在retranslateUi方法最后加上:

self.lineEdit.setText("127.0.0.1")
        self.lineEdit_2.setText("21567")
        self.pushButton_3.clicked.connect(self.send_button)
        self.pushButton_4.clicked.connect(self.connect_button)
        self.pushButton_5.clicked.connect(self.break_button)
        self.pushButton_3.setShortcut(QtCore.Qt.Key_Return)  # 回车键发送

        # 调用自定义类,同server_4
        self.backend = BackendThread()
        self.backend.update_date.connect(self.handleDisplay)
        self.thread = QThread()     # 创建进程
        self.backend.moveToThread(self.thread)
        self.thread.started.connect(self.backend.run)
        self.thread.start()

至此,客户端也完成了

运行效果:

服务器端和客户端一起运行,先服务器监听,然后客户端连接,就可以互发信息了
socket网络程序设计实验三,客户端与服务器端一对一聊天(基于python3.7、PyQt5)_第4张图片
socket网络程序设计实验三,客户端与服务器端一对一聊天(基于python3.7、PyQt5)_第5张图片
socket网络程序设计实验三,客户端与服务器端一对一聊天(基于python3.7、PyQt5)_第6张图片

完整代码已上传在我的github上:https://github.com/LiePy/socket.git

你可能感兴趣的:(python,socket网络程序设计,python,PyQt)