python 使用socket建立小型聊天室

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

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

python 使用socket建立小型聊天室_第1张图片

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

服务端源码:

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

你可能感兴趣的:(Python的那些便捷工具,网络,python,websocket)