Vtalk局域网聊天室 基于Python

服务端:

#作者:吴子豪
#服务器启动程序
import os
import threading,socket,time,datetime,sys

#每次运行都清空日志
saveservermsg=open("#Serverlog.hst", 'w')
saveservermsg.close()

#服务器配置
host=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
hostname=socket.gethostname()

#port=int(input("SYSTEM: Port(1024~65535):"))
port=23817

host.bind((hostname,port))

#获取客户端个数
num_of_clients=int(input("SYSTEM: Clients(total):"))
num_of_clients+=1
print("SYSTEM: Server Running...")
#服务器同时监听客户端总数
host.listen(num_of_clients)
#创建客户端预估总数列表
clients=list(range(0,num_of_clients))#将range object转换为列表以修改其中元素
#实际在线客户端列表
clientol=[]

#定义服务器行为
addr={None:None}
Cli={None:None}#客户端编号字典

print("SYSTEM: \"dc\" To Shut Down Server")
boole=1#若boole为1则服务器保持运行

#全局变量 用于统一各线程
glo=globals()

#将服务器log重定向写入文件
saveservermsg=open("#Serverlog.hst", 'a')#将日志输入到文件

def serverclos():#控制服务器关闭
    while glo["boole"]:
        cmd=str(input())
        if cmd=="dc":
            repo=open("#ServerReport.err",'w')
            sys.stderr=repo
            host.close()
            print("SYSTEM: Server Is Closing Soon.......")
            saveservermsg.write("SYSTEM: Server has been closed\n")
            saveservermsg.close()#关闭文件
            glo["boole"]=0
        else:
            pass
servcls=threading.Thread(target=serverclos,args=())#该线程负责获取服务器命令

def printtime():#打印当前系统时间
    time.sleep(3)
    while glo["boole"]:
        now_time = datetime.datetime.now().strftime('%D %T %A')
        saveservermsg.write("\n")
        saveservermsg.write(now_time)
        saveservermsg.write("\n")
        saveservermsg.write("-------------------------\n")
        print(now_time)
        time.sleep(30)
pritime=threading.Thread(target=printtime,args=())#该线程负责打印时间 30秒一次




def connect(N):#主线程

    while glo["boole"]:
        Cli[N],addr[N]=host.accept()#等待与客户端建立连接
        clientol.append(N)
        print('C_SYSTEM: User <{}> online,using port:{}'.format(addr[N][0],addr[N][1]))#获取第N号客户端的ipv4和端口

        #告知其他在线客户端 此客户端上线
        for index in clientol:
            if index != N:
                if Cli[index]!=None:
                    onlinemsg="Server: User<{}> Online".format(addr[N][0])
                    Cli[index].send(onlinemsg.encode())
                else:
                    pass
            else:
                pass

        #同步到日志
        saveservermsg.write('SYSTEM: User <{}> online,using port:{}\n'.format(addr[N][0],addr[N][1]))
        Cli[N].send(b"Server:Client Connected")#对客户端进行初次连接反馈
        while glo["boole"]:#获取第N号客户端发送的多条讯息
            try:
                #若客户端被强制关闭则自动销毁套接字
                msg=Cli[N].recv(4096)
                if msg == '':
                    pass
                elif msg == b"><01~>0":#检查客户端是否在线
                    print('SYSTEM: Client No.{}-<{}> still online'.format(N,addr[N][0]))
                    try:
                        saveservermsg.write('SYSTEM: Client No.{}-<{}> still online\n'.format(N,addr[N][0]))
                    except:
                        pass
                elif msg == b"Server Is Under Listening":
                    print("ServerSupport: " + msg.decode())
                    saveservermsg.write("ServerSupport: " + msg.decode())
                    saveservermsg.write(msg.decode())
                    saveservermsg.write("\n")
                elif msg == b"dc":#客户端下线
                    print('C_SYSTEM: User <{}> offline'.format(addr[N][0]))#以C_开头表示这是可以传输给客户端的信息

                    #反馈给客户端 让客户端接收消息线程停止
                    Cli[N].send(b"stop_client-True>")

                    #告知所有其他客户端该客户端下线
                    for index in clientol:
                        if index != N:
                            if Cli[index] != None:
                                onlinemsg = "Server: User<{}> Offline".format(addr[N][0])
                                Cli[index].send(onlinemsg.encode())
                            else:
                                pass
                        else:
                            pass

                    #储存到日志
                    saveservermsg.write('C_SYSTEM: User <{}> offline\n'.format(addr[N][0]))
                    #删除该客户端编号
                    clientol[int(N)]=None

                    #断开此客户端连接
                    Cli[N].close()
                    #清空客户端信息
                    Cli[N]=None
                    addr[N]=None
                    #变为空套接字,等待下一个客户端的接入
                    break
                else:
                    print("Client No.{}-<{}>:".format(N,addr[N][0]),msg.decode())#接收第N号客户端的讯息
                    #将信息同步到所有客户端
                    for index in clientol:
                        if Cli[index] != None:
                            onlinemsg = "Client No.{}-<{}>:".format(N,addr[N][0])
                            #Cli[index].send(onlinemsg.encode()+msg)#控制台版本
                            Cli[index].send('{}'.format(addr[N][0]).encode()+msg)#GUI版本
                        else:
                            pass
                    #将信息储存到日志
                    saveservermsg.write("Client No.{}-<{}>:".format(N,addr[N][0]))
                    saveservermsg.write(msg.decode())
                    saveservermsg.write("\n")

            #客户端非正常关闭
            except:
                print("SYSTEM: Client No.{}-<{}> unexpected lost connection".format(N,addr[N][0]))
                try:
                    saveservermsg.write("SYSTEM: Client No.{}-<{}> unexpected lost connection\n".format(N,addr[N][0]))
                except:
                    pass
                # 告知所有其他客户端该客户端下线
                for index in clientol:
                    if index != N:
                        if Cli[index] != None:
                            onlinemsg = "Server: User<{}> Offline".format(addr[N][0])
                            Cli[index].send(onlinemsg.encode())
                        else:
                            pass
                    else:
                        pass
                # 储存到日志
                saveservermsg.write('C_SYSTEM: User <{}> offline\n'.format(addr[N][0]))
                # 删除该客户端编号
                clientol[int(N)] = None
                # 关闭此客户端套接字
                Cli[N].close()
                # 清空客户端信息
                Cli[N] = None
                addr[N] = None
                # 变为空套接字,等待下一个客户端的接入
                break


#根据客户端数量创建线程列表
thr=[]
for n in clients:
    thr.append(threading.Thread(target=connect,args=(str(n),)))#对空线程列表添加线程

#服务器开始运行
for n in clients:
    thr[n].start()
    #thr[n].join()
pritime.start()
servcls.start()

客户端(CLI):

#作者:吴子豪
import socket,tkinter,threading,sys,time
#指定目标服务器
client1=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
hostname="V-PIG"#服务器名

#port=int(input("SYSTEM: Port(1024~65535):"))
port=23817

#发起连接申请
client1.connect_ex((hostname,port))

print("Server: Input \"dc\" to Disconnect")

g=globals()
ckeck=1
msg=''

#接收服务器消息
def recvmsg():
    while g['check']:
        try:
            msgs=client1.recv(1024)
            if msgs == '':
                pass
            #客户端结束时接收消息线程需要服务器的反馈来结束
            elif msgs == b"stop_client-True>":
                break
            else:
                print(msgs.decode())
        except:
            client1.close()
            g["check"]=0
            print("SYSTEM: Lost Connection")
            print("SYSTEM: Press \"Enter\" to exit")
            time.sleep(5)
            break
revcmsgs=threading.Thread(target=recvmsg,args=())#该线程负责收取服务器消息

def check():
    while g['check']:
        time.sleep(10)
        try:
            client1.send(b"><01~>0")
            if g["msg"] =="dc":
                break
        except:
            pass
ckline=threading.Thread(target=check,args=())#向服务器反馈连接情况

def sendmsg():#向服务器发送消息
    while g["check"]:
        try:
            g["msg"]=input("")
        except:
            pass
        try:
            if g["msg"] == "dc":
                client1.send(msg.encode())
                client1.close()
                g["check"] = 0
                print("SYSTEM: Client offline")
                time.sleep(5)
                break
            else:
                client1.send(msg.encode())
        except:
            pass
sdmsg = threading.Thread(target=sendmsg, args=())

revcmsgs.start()
sdmsg.start()
ckline.start()

客户端(GUI):

客户端的窗口图标文件要保存在和客户端同路径下的ico路径中

客户端图标:     

#作者:吴子豪
import time
import tkinter as tk

#创建窗口对象
top=tk.Tk()
#窗口命名为app名
top.title("Vtalk Beta(Offline)")
#添加窗口图标 可注释
top.iconphoto(True, tk.PhotoImage(file='ico/microvtalk.png'))
top.geometry("1200x521")
top.configure(bg='black')
top.resizable(width=False, height=False)

#创建文本输入框
inputmsg = tk.Entry(top,
                    width=104,
                    bg='black',
                    fg='white',
                    highlightcolor='green',
                    highlightthickness=2,
                    font=(11)
                    )
inputmsg.grid(column=1,row=2)


#创建文本框滚动条
#scroll=tk.Scrollbar(top)
#scroll.grid(column=2,row=1)


#创建文本框
tip = tk.Text(
    bg="black",
    #anchor='nw',
    cursor='circle',
    font=(8),
    fg='white',
    height=20,
    width=104,
    #justify="left",
    relief='sunken',
    #text='',
    #yscrollcommand=scroll.set,
    )
#scroll.config(command = tip.yview)

#用户名列表
tip.grid(column=1,row=1)
onlines = tk.Text(
    bg="black",
    cursor='arrow',
    font=(5),
    fg='green',
    height=20,
    width=15,
    relief='flat',
    )
onlines.grid(column=2,row=1)

#上面是图形界面

#程序主体--客户端

import socket,threading,sys
#指定目标服务器
client1=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
gethostname=open("HOST_NAME.txt",'r')
hostname=gethostname.read()
gethostname.close()

#hostname="V-PIG"#服务器名
#hostname="Y2K"#服务器名


#port=int(input("SYSTEM: Port(1024~65535):"))
port=23817#固定端口

g=globals()
ckeck=1
msg=''
username=''
#锁定所有text栏目
onlines.configure(state='disabled')
tip.configure(state='disabled')

#接收服务器消息
def recvmsg():
    while g['check']:
        try:
            msgs=client1.recv(1024)
            if msgs == '':
                pass
            elif msgs == b"Server:Client Connected":
                top.title("Vtalk Beta(Online)")
                onlines.configure(state='normal')
                onlines.insert(tk.END, 'Vtalk '+'On' + '\n\n')
                onlines.update()
                onlines.configure(state='disabled')
            #客户端结束时接收消息线程需要服务器的反馈来结束
            elif msgs == b"stop_client-True>":
                break
            #输出在线用户动态
            elif msgs.decode()[:12] == "Server: User":
                onlines.configure(state='normal')
                onlines.insert(tk.END, msgs.decode()[12:]+ '\n\n')
                onlines.update()
                onlines.configure(state='disabled')

            else:
                output=msgs.decode()
                tip.configure(state='normal')
                tip.insert(tk.END, output + '\n')
                tip.update()
                tip.configure(state='disabled')
        except:
            top.title("Vtalk Beta(Offline)")
            onlines.configure(state='normal')
            onlines.insert(tk.END, 'Bad challenge'+ '\n\n')
            onlines.update()
            onlines.configure(state='disabled')
            break
revcmsgs=threading.Thread(target=recvmsg,args=())#该线程负责收取服务器消息

def check():
    while g['check']:
        time.sleep(10)
        try:
            client1.send(b"><01~>0")
            if g["msg"] =="dc":
                break
        except:
            pass
ckline=threading.Thread(target=check,args=())#向服务器反馈连接情况

def gettext(self):
        text = inputmsg.get()
        try:
            g["msg"]=text
        except:
            pass
        try:
            client1.send(b'('+g["username"].encode()+b'): '+text.encode())
        except:
            onlines.configure(state='normal')
            onlines.insert(tk.END, 'Bad challenge' + '\n\n')
            onlines.update()
            onlines.configure(state='disabled')
        inputmsg.delete(0, 'end')
        inputmsg.update()
#回车发送消息
inputmsg.bind('<Return>',gettext)

#创建下方信息栏
info = tk.Text(
        bg='black',
        fg='green',
        font=(2),
        width=104,
        height=2,
        relief='flat',
        )
info.grid(column=1,row=3)


def geton():
    g['username'] = name.get()#获取用户名
    # 更新信息栏
    info.configure(state='normal')
    info.delete(1.0, 5.10)
    info.insert(tk.END, 'Current Client Host: {}'.format(socket.gethostname()))
    info.insert(tk.END, ' ' * 45 + 'Current Username: ' + '\"' + g['username'] + '\" ')
    info.insert(tk.END, '\nVtalk Beta Version By Vortex' + ' ' * 43 + 'Contact Me:2310108909@qq.com')
    info.update()
    info.configure(state='disabled')

    # 发起连接申请
    '''
    client1.connect_ex((hostname, port))
    revcmsgs.start()
    ckline.start()
    '''

def getoff():
    try:
        client1.send(b'dc')
        client1.close()
        g["check"] = 0
        onlines.configure(state='normal')
        onlines.insert(tk.END, 'Vtalk ' + 'Off' + '\n\n')
        onlines.update()
        onlines.configure(state='disabled')
        top.destroy()
        try:
            exit()
        except:
            pass
    except:
        g["check"] = 0
        onlines.configure(state='normal')
        onlines.insert(tk.END, 'Vtalk ' + 'Off' + '\n\n')
        onlines.update()
        onlines.configure(state='disabled')
        top.destroy()
        try:
            exit()
        except:
            pass

#创建用于下线的按钮
off = tk.Button()
off.config(text='Quit',
               fg='black',
               bg='green',
               width=14,
               #font=('ROGFonts-Regular',11),
                height=2,
               font=(11),
           command=getoff

               )
off.grid(column=2,row=3)
#创建用于重命名的按钮
on = tk.Button()
on.config(text='Rename',
            fg='black',
            bg='green',
            width=14,
            #font=('ROGFonts-Regular',11),
            font=(11),
          command=geton
            )
on.grid(column=2,row=2)
#用户名框
name = tk.Entry(
    bg="black",
    font=(11),
    fg='white',
    #height=2,
    width=14,
    relief='flat',
    highlightcolor='green',
    highlightthickness=2,
    )
name.grid(column=2,row=4)

#生成随机用户名
import random
name.insert(tk.END, "User.{}".format(random.randint(0,1000)))
name.update()
g['username'] = name.get()#获取默认用户名
#更新信息栏
info.configure(state='normal')
info.delete(1.0,5.10)
info.insert(tk.END, 'Current Client Host: {}'.format(socket.gethostname()))
info.insert(tk.END, ' '*45+'Current Username: ' + '\"' + g['username'] + '\" ')
info.insert(tk.END, '\nVtalk Beta Version By Vortex'+' '*43+'Contact Me:2310108909@qq.com')
info.update()
info.configure(state='disabled')
#自动连接
try:
    client1.connect_ex((hostname, port))
except:
    pass
revcmsgs.start()
ckline.start()
#运行窗口
tk.mainloop()

客户端需要读入一个HOST_NAME.txt文件来获取主机名

获取服务主机:

//作者:吴子豪
#include<iostream>
#include<fstream>
#include<Windows.h> 
#include<conio.h>
using namespace std;
int main()
{
	ofstream in;
	in.open("HOST_NAME.txt");
	in<<" ";
	in.close();
	ofstream out;
	string hostname=" ";
	cout<<"-----------------------Vtalk-----------------------\n请输入服务主机名\n服务主机:";
	cin>>hostname;
	out.open("HOST_NAME.txt");
	out<<hostname;
	out.close();
	cout<<"<按任意键继续>";
	getch();
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值