python 使用socket建立小型聊天室

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

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

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

服务端源码:

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

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慕慕慕慕公子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值