服务端:
#作者:吴子豪
#服务器启动程序
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();
}