python 使用socket建立小型聊天室

2023-11-17

一个聊天室,由两个部分组成。服务端和客户端。服务端接收客户端发来的消息,并将接收到的消息发送给其他客户端。客户端负责发送消息到服务端,并接收来自服务端发送的来自其他客户端的消息。

示例图(服务端和客户端):

 这是属于一个群聊的聊天室,服务端会把每一条消息发送给所有客户端。当有人连接服务端或退出聊天时,所有在线的客户端都会收到提醒。

服务端源码:

# encoding: utf-8
import socket
import threading
import time
import sys
import json
import configparser as conf
import redis

# redis.StrictRedis
r = redis.Redis(host='127.0.0.1', port=6379, db=1, decode_responses=True)

cf = conf.ConfigParser()
cf.read('Server_Socket.ini')
socket_host = cf.get("socket", "host")
socket_port = cf.get("socket", "port")

clients = set()
clients_lock = threading.Lock()

global data_type


def socket_service():
    global data_type
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind((socket_host, int(socket_port)))
        s.listen(100)
    except socket.error as msg:
        print(msg)
        sys.exit(1)
    print('等待客户端连接')
    while True:
        conn, addr = s.accept()
        try:
            data_type = json.loads(conn.recv(1024).decode("utf8"))
        except Exception as e:
            print(e)

        WelcomeClient = {"UserSum": 0, "text": "欢迎加入慕公子的聊天室!", "type": "client"}
        conn.send(bytes(json.dumps(WelcomeClient), "UTF-8"))
        if r.exists(data_type['Name']) == 0:
            r.set(str(addr), data_type['Name'])
            r.set(data_type['Name'], str(addr))
            if data_type['type'] == 'server':
                with clients_lock:
                    clients.add(conn)
                    for c in clients:
                        if data_type['type'] == "server":
                            try:
                                sums = len(clients)
                                dataClient = {"UserSum": sums, "text": data_type['text'], "type": "change"}
                                c.send(bytes(json.dumps(dataClient), "UTF-8"))
                            except ConnectionResetError as e:
                                print(e)
                t = threading.Thread(target=deal_data, args=(conn, addr))
                t.start()
        elif r.exists(data_type['Name']) == 1:
            sums = len(clients)
            data1Client = {"UserSum": sums, "text": "名字重复,换一个名字吧。", "type": "client"}
            conn.send(bytes(json.dumps(data1Client), "UTF-8"))


def deal_data(conn, addr):
    print(f'有新的客户端连接:{conn}-{addr}')
    try:
        while True:
            data = conn.recv(1024).decode("utf8")
            msg = json.loads(data)
            print(f"{addr}-{msg['Name']} 发送的消息:{msg['text']}")
            time.sleep(0.3)
            if data == 'exit' or not data:
                print(f'{addr} connection close')
                conn.send(bytes('Connection closed!'), 'UTF-8')
                break
            with clients_lock:
                for c in clients:
                    if msg['type'] == "client":
                        sums = len(clients)
                        dataClient = {"UserSum": sums, "text": f"{msg['Name']} 发送的消息:{msg['text']}", "type": "client"}
                        text = bytes(json.dumps(dataClient), "UTF-8")
                        c.send(text)
    except ConnectionResetError as e:
        clients.remove(conn)
        UserName = r.get(str(addr))
        clSums = len(clients)
        with clients_lock:
            for c in clients:
                changeClient = {"UserSum": clSums, "text": f"{UserName},退出聊天。", "type": "change"}
                text = bytes(json.dumps(changeClient), "UTF-8")
                c.send(text)
        print(f"{e}--{addr}--{UserName},退出聊天。")

        r.delete(str(addr))
        r.delete(UserName)
    except json.decoder.JSONDecodeError as e:
        print(e)
    conn.close()


if __name__ == '__main__':
    socket_service()

客户端代码:

import socket

import tkinter as tk
import tkinter.font as tkFont
from tkinter.filedialog import askdirectory
import tkinter.messagebox
import tkinter.filedialog
from tkinter import ttk
import threading as thr
from tkinter import scrolledtext
import os
import json
import configparser as conf

window = tk.Tk()
window.title('MuChat')
# 获取屏幕宽高
sw = window.winfo_screenwidth()
sh = window.winfo_screenheight()
# 设置屏幕的宽和高
ww = 370
wh = 470
x = (sw - ww) / 2
y = (sh - wh) / 2
# 根据屏幕宽高来让程序居中
window.geometry("%dx%d+%d+%d" % (ww, wh, x, y))
fontStyle = tkFont.Font(family="Lucida Grande", size=15)
tk.Label(window, text="MuChat", font=fontStyle).pack()
# 设置frame容器
frame = tk.Frame(window, padx=3, pady=3)
frame.pack()
frame_left = tk.Frame(frame)
frame_right = tk.Frame(frame)
frame_left.pack(side='left')
frame_right.pack(side='right')



global s


def SumChange(sumCa):
    global sumLa
    sumLa = tk.Label(window, text=f"当前在线{int(sumCa)}人", font=fontStyle)
    sumLa.pack()


def socket_client():
    cf = conf.ConfigParser()
    cf.read('Client_Socket.ini')
    socket_host = cf.get("socket", "host")
    socket_port = cf.get("socket", "port")

    global s
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((socket_host, int(socket_port)))
        print(s)
        data_json = {"Name": yourName.get(), "text": f"欢迎{yourName.get()}的加入!", "type": "server"}
        data = json.dumps(data_json).encode()
        s.send(data)
    except socket.error as msg:
        print(msg)
    while True:
        message = s.recv(1024).decode('utf-8')  # ConnectionAbortedError
        # print(message)
        jsonData = json.loads(message)
        print(jsonData)
        if jsonData:
            scr.insert(tk.END, f"{jsonData['text']}\n\n")
            scr.see(tk.END)
            scr.update()
        if jsonData['type'] == 'change':
            sumLa.pack_forget()
            SumChange(jsonData['UserSum'])


def thrSocket():
    en.focus_set()
    soc = thr.Thread(target=socket_client, daemon=True, args=())
    soc.start()


yourName = tk.StringVar()
tk.Entry(frame_left, textvariable=yourName, width=30).pack(padx=5, pady=10)
tk.Button(frame_right, text='连接服务端', font=fontStyle, width=18, height=1, command=lambda: thrSocket()).pack(padx=3,
                                                                                                           pady=3)


def send_socket_client():
    global s
    if (not yourMsg.get()) | (yourMsg.get() == ' '):
        tkinter.messagebox.showinfo(title='提示', message="你还未输入内容!")
        return
    data_json = {"Name": yourName.get(), "text": yourMsg.get(), "type": "client"}
    yourMsg.set('')
    data = json.dumps(data_json).encode()
    s.send(data)


def SendSocket():
    soc = thr.Thread(target=send_socket_client, daemon=True, args=())
    soc.start()


def socket_cloase():
    s.close()


def ReSend(e):
    SendSocket()


def socket_ini():
    os.startfile("Client_Socket.ini")


yourMsg = tk.StringVar()
en = tk.Entry(frame_left, textvariable=yourMsg, width=30)
en.pack(padx=5, pady=10)
en.bind('<Return>', ReSend)

tk.Button(frame_right, text='发送消息', font=fontStyle, width=18, height=1, command=lambda: SendSocket()).pack(padx=3,
                                                                                                           pady=3)
fontStyleRadio = tkFont.Font(family="Lucida Grande", size=13)

tk.Button(frame_left, text='编辑配置文件', font=fontStyle, width=18, height=1, command=lambda: socket_ini()).pack(padx=3,
                                                                                                            pady=3)

tk.Button(frame_right, text='关闭连接', font=fontStyle, width=18, height=1, command=lambda: socket_cloase()).pack(padx=3,
                                                                                                              pady=3)
scr = scrolledtext.ScrolledText(window, width=30, height=13, font=("宋体", 13))
scr.pack()
sumLa = tk.Label(window, text=f"当前在线0人", font=fontStyle)
sumLa.pack()
window.mainloop()

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python 使用socket建立小型聊天室 的相关文章

随机推荐

  • Go语言的中“...”省略号总结

    1 数组中的 省略号 在数组的定义中 如果在数组长度的位置出现 省略号 则表示数组的长度是根据初始化值的个数来计算 因此 q int 1 2 3 等同于var q 3 int 3 int 1 2 3 按我目前获得的信息来看 q int 1
  • 静态链表系列操作

    静态链表 include
  • Qt调用外部程序

    一 调用系统默认应用打开文件 使用QDesktopServices的openUrl 成员 这个函数是跨平台的 Qt会根据不同的系统平台调用默认的程序打开指定文件 QUrl存放制定的路径 使用非常简便 示例代码如下 QString fileN
  • Linux系统查看磁盘空间占用情况的几个常用命令

    Linux系统下 需要使用命令查看磁盘空间占用情况 下面将这些常用命令进行整理 以作备忘 一 查看磁盘物理分区信息 使用如下命令查看磁盘分区信息 cat proc partitions 二 查看磁盘分区占用情况 使用如下命令查看磁盘分区占用
  • 代码检查与代码走查

    代码检查与代码走查是基于人工测试的白盒测试方法 目的 找出错误 而不是改正错误 是测试而不是调试 优点 能够精确地定位的错误 降低调试成本 可以成批的发现错误 而计算机测试往往是逐个发现错误并改正 注意 代码检查 代码走查 基于计算机的测试
  • 算法工程师福利:超实用技术路线图

    对于不同级别的算法工程师技能要求 我们大致可以分成以下几个层级 初级 可以在一些指导和协助下独立完成开发任务 具体到算法方面 需要你对于工具框架 建模技术 业务特性等方面有一定的了解 可以独立实现一些算法项目上的需求 中级 可以基本独立完成
  • USB+HOST+FATFS

    转载https blog csdn net zcshoucsdn article details 78944536
  • Mysql 5.6 双主配置 自动同步脚本

    最近有项目应用到了 mysql 双主结构 现在贴出来共享 mysql 版本 5 6 11 操作系统版本 rhel 6 2 Master 的 my cnf 配置 只贴M M 结构部分 log bin fabian server id 1 bi
  • 【Deep Learning】Hinton. Reducing the Dimensionality of Data with Neural Networks Reading Note

    2006年 机器学习泰斗 多伦多大学计算机系教授Geoffery Hinton在Science发表文章 提出基于深度信念网络 Deep Belief Networks DBN 可使用非监督的逐层贪心训练算法 为训练深度神经网络带来了希望 如
  • Houdini中全景摄像机shader立体左右眼成像方法

    熟悉Houdini Shader部分的同学应该多多少少也了解camera自身也可以设定自己的shader 其中polar panoramic shader 能够非常方便的为艺术家渲染360全景视角的cg画面 但是这样渲染出来的画面只是单眼所
  • md5加密

    crypto模块 crypto模块 封装了一系列密码学相关的功能 下载该模块 npm install save crypto 使用 crypto createHash md5 update 加密数据 digest hex 示例
  • 线程的状态与切换

    Java中的线程的生命周期大体可分为5种状态 1 新建 初始化 NEW 新创建了一个线程对象 2 可运行 RUNNABLE 线程对象创建后 其他线程 比如main线程 调用了该对象的start 方法 该状态的线程位于可运行线程池中 等待被线
  • react入门,适合新手小白!

    1 1官方网站 中文官网 React 官方中文文档 用于构建用户界面的 JavaScript 库 2 react简介 1 2 1 React是什么 主要是帮助咱们操作界面 也就是操作视图呈现页面 1 2 2 为什么要学React 易于学习
  • Hive(完整版)

    Hive 1 基本概念 Hive本质上是基于 Hadoop 的一个数据仓库工具 可以将结构化的数据文件映射为一张表 并 提供类 SQL 查询功能 通俗一点就是Hive相当于一个hadoop的客户端 利用hdfs存储数据 利用mapreduc
  • 【CV】第 10 章:使用 R-CNN、SSD 和 R-FCN 进行目标检测

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • JAVA计算摘要,例如MD5和SHA-256

    摘要有什么用 1 保证数据的完整性 例如你发送一个100M的文件给你的B 但是你不知道B收到的是否是完整的文件 此时你首先使用摘要算法 如MD5 计算了一个固定长度的摘要 将这个摘要和文件一起发送给B B接收完文件之后 同样使用MD5计算摘
  • Android 使用SwipeRefreshLayout实现RecyclerVeiw的下拉刷新和上拉加载

    博主前些天发现了一个巨牛的人工智能学习网站 通俗易懂 风趣幽默 忍不住也分享一下给大家 点击跳转到网站 实现下拉刷新和上拉加载的完整代码如下 一 布局文件代码如下 主界面main xml代码
  • PLSQL性能优化方法

    转载自http www itfarmer com cn 1 选择最有效率的表名顺序 只在基于规则的优化器中有效 ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名 FROM子句中写在最后的表 基础表driving table 将
  • stm32f407+cjson的避坑

    1 添加cjson库文件后 编译工程文件 报错 提示 CJSON test c 461 error 268 declaration may not appear after executable statement in block 如下图
  • python 使用socket建立小型聊天室

    一个聊天室 由两个部分组成 服务端和客户端 服务端接收客户端发来的消息 并将接收到的消息发送给其他客户端 客户端负责发送消息到服务端 并接收来自服务端发送的来自其他客户端的消息 示例图 服务端和客户端 这是属于一个群聊的聊天室 服务端会把每