简单的python socket编程,实现登陆注册、聊天、文件发送等功能
服务端
import socket
import threading,time
import os
import struct
import sys
from queue import Queue
def deal(conn,addr,queue):
logined=0
content=''
global online #在线人名单
global ipdict #在线人字典{id:ip}
while True:
conn.send("signup or login?".encode("utf-8"))
switch=conn.recv(BUFSIZE)
if(switch == b'signup'):#新用户注册
conn.send("username:".encode('utf-8'))
username=conn.recv(BUFSIZE)#.decode('utf-8')
conn.send('password:'.encode('utf-8'))
password=conn.recv(BUFSIZE).decode('utf-8')
if(os.path.exists(username)):
#conn.send("username existed".encode('utf-8'))
continue
fp=open(username,"w")
fp.write(password)
elif(switch == b'login'):#登陆功能
conn.send("username:".encode('utf-8'))
username=conn.recv(BUFSIZE)#.decode('utf-8')
conn.send('password:'.encode('utf-8'))
password=conn.recv(BUFSIZE).decode('utf-8')
if(os.path.exists(username)==False):
#conn.send("username unexisted".encode('utf-8'))
continue
fp=open(username,"r")
check=fp.read()
if check==password:#登陆且密码正确时才能跳出while循环
logined=1
conn.send("login successfully!".encode('utf-8')+username)
break;
usr=username.decode('utf-8')
print("用户%s已登录成功" %usr)
online.append(usr)#记录在线用户列表
ipdict[usr]=addr#记录在线用户字典
while logined:
msg=conn.recv(BUFSIZE)#登陆后不断收发消息
msg=msg.decode('utf-8')
if not queue.empty():#接收队列消息(用于线程间通信)
content=queue.get()
contents=content.split(':')
if contents[1]!=usr:#若该消息不是发给该用户,将其放回队列中
queue.put(content)
else:#否则将消息发给该用户
conn.send((contents[0]+"!"+contents[3]+":"+contents[2]).encode('utf-8'))
else:
pass#队列为空时pass
if msg.split(':')[0]=='f':#收到‘f’时用于客户端的消息刷新
continue
elif msg.split(':')[0]=='o':#收到‘o’时将在线字典发给用户
onl=" ".join(online)
conn.send(str(ipdict).encode('utf-8'))
print(str(ipdict))
elif msg=='exit':
online.remove(usr)
del ipdict[usr] #用户退出后,从在线字典中删除
print(usr+"已离开")
else:
if len(msg) == 0:break
msgs=msg.split(':') #格式msg:to:content:from
if msgs[0]=='msg'or msgs[0]=='file':#只将msg,file消息入队列,其他消息扔掉
queue.put(msg)
print(msgs[0:])
conn.close()
def main():#主函数
global ipdict
ip_port = ('127.0.0.1',9998) #port
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #建立tcp socket
s.bind(ip_port) #绑定
s.listen(5) #监听
msgqueue=Queue(maxsize=1000)#消息队列
while True: #accpet循环,与多个客户端建立连接
conn,addr=s.accept()
print('已与ip:%s连接成功' %addr[0])
t=threading.Thread(target=deal,args=(conn,addr[0],msgqueue))#子线程处理客户端
t.start()
s.close() #服务器关闭
BUFSIZE=1024
online=[]
ipdict={}
content=''
if (__name__=="__main__"):
main()
客户端
import socket
import threading,time
import struct
import sys
import os
import time
def freshing(socke,str):#0.5秒刷新一次消息
while True:
time.sleep(0.5)
socke.send(str.encode('utf-8'))
def recv_file(port):#文件接收:开启端口接收
iport=('127.0.0.1',port)
soc=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.bind(iport)
soc.listen(5)
connection, address = soc.accept()
try:
file_info_size = struct.calcsize('128sl')
buf = connection.recv(file_info_size)
if buf:
file_name, file_size = struct.unpack('128sl', buf)#编码128string和int
file_name = file_name.decode().strip('\00')
file_new_dir = os.path.join('receive')
# print(file_name, file_new_dir)
if not os.path.exists(file_new_dir):
os.makedirs(file_new_dir)
file_new_name = os.path.join(file_new_dir, file_name)
received_size = 0 #读取现有的该文件的大小(断点续传)
if os.path.exists(file_new_name):
received_size = os.path.getsize(file_new_name)
connection.send(str(received_size).encode())
w_file = open(file_new_name, 'ab')
print("start receiving file:", file_name)
while not received_size == file_size:
r_data = connection.recv(10240)
received_size += len(r_data)#记录已接收的大小
w_file.write(r_data)
w_file.close()
print("接收完成!\n")
connection.close()#关闭文件连接
except Exception as e:
print(e)
def send_file(ip,port,file_name):#文件发送:连接到对方的tcp端口
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("等待对方接收,发送将于五秒后开始\n")
time.sleep(5)#等待一段时间等对方的端口开启再连接
sock.connect((ip, port))
# 定义文件头信息;
file_head = struct.pack('128sl', os.path.basename(file_name).encode(),
os.stat(file_name).st_size)#pack:128字符串和long int
sock.send(file_head)
received_size = int(sock.recv(2014).decode())#对方已有大小(断点续传)
read_file = open(file_name, "rb")
read_file.seek(received_size) #下一次读取从index开始
while True:
# time.sleep(1)
file_data = read_file.read(10240)
if not file_data:
break
sock.send(file_data)
read_file.close()
sock.close()#关闭用于文件发送的套接字
def sending(s):#发送消息函数
global myname
time.sleep(0.5)
while True:
msg=input('>>:').strip()
if len(msg)==0:
continue
elif msg=="exit":
s.send("exit".encode('utf-8'))#用户退出
s.close
exit(0)
elif msg.split(':')[0]=='file':#file:towho:destination ip:filename
msg=msg+":"+myname #登陆后,消息附带自己的id
s.send(msg.encode('utf-8'))
send_file(msg.split(':')[2],9995,msg.split(':')[3])
else:
if myname=="": #未登录时
s.send(msg.encode('utf-8'))
else:
msg=msg+":"+myname #登陆后,消息附带自己的id
s.send(msg.encode('utf-8'))
ip_port=('127.0.0.1',9998) #port
BUFSIZE=1024
myname=""
login=0
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#socket套接字
s.connect_ex(ip_port) #连接
t=threading.Thread(target=sending,args=(s,))#创建子线程不断发送消息
t.start()
while True: #消息接收循环
feedback=s.recv(BUFSIZE) #recv接收
recv=feedback.decode('utf-8')
recvs=recv.split('!')
if(recvs[0]=='login successfully'): #当登陆成功时
print(recvs[0])
myname=recvs[1]
login=1
print("您已成功登陆:"+myname+"\n发送o查看在线的人:他的ip地址\n聊天方式为 msg:对谁说话:要发送的消息\n发文件方式 file:发送对象:对象的ip地址:文件名\n发送f刷新消息\n发送exit退出")
fresh=threading.Thread(target=freshing,args=(s,'f'))
fresh.start() #创建消息刷新线程,对服务端进行刷新
elif(recvs[0]=='msg'): #接收到msg消息时,在屏幕显示消息
print(recvs[1])
elif(recvs[0]=='file'): #接收到file消息时,准备调用recv_file接收文件
print("即将接收来自%s的文件" %recv[1])
recv_file(9995)
else: #其他消息(如在线查询,登陆注册等)直接在屏幕显示
print(recv)
s.close()