服务端:
#作者:吴子豪
#服务器启动程序
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=23817host.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=repohost.close()print("SYSTEM: Server Is Closing Soon.......")saveservermsg.write("SYSTEM: Server has been closed\n")saveservermsg.close()#关闭文件glo["boole"]=0else: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:passelse: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 == '':passelif 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:passelif 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:passelse:pass#储存到日志saveservermsg.write('C_SYSTEM: User <{}> offline\n'.format(addr[N][0]))#删除该客户端编号clientol[int(N)]=None#断开此客户端连接Cli[N].close()#清空客户端信息Cli[N]=Noneaddr[N]=None#变为空套接字,等待下一个客户端的接入breakelse: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:passelse:pass# 储存到日志saveservermsg.write('C_SYSTEM: User <{}> offline\n'.format(addr[N][0]))# 删除该客户端编号clientol[int(N)] = None# 关闭此客户端套接字Cli[N].close()# 清空客户端信息Cli[N] = Noneaddr[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>":breakelse:print(msgs.decode())except:client1.close()g["check"]=0print("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":breakexcept:pass
ckline=threading.Thread(target=check,args=())#向服务器反馈连接情况def sendmsg():#向服务器发送消息while g["check"]:try:g["msg"]=input("")except:passtry:if g["msg"] == "dc":client1.send(msg.encode())client1.close()g["check"] = 0print("SYSTEM: Client offline")time.sleep(5)breakelse: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 == '':passelif 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":breakexcept:pass
ckline=threading.Thread(target=check,args=())#向服务器反馈连接情况def gettext(self):text = inputmsg.get()try:g["msg"]=textexcept:passtry: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"] = 0onlines.configure(state='normal')onlines.insert(tk.END, 'Vtalk ' + 'Off' + '\n\n')onlines.update()onlines.configure(state='disabled')top.destroy()try:exit()except:passexcept:g["check"] = 0onlines.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();
}