简单的python socket编程-聊天程序

简单的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()                                       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值