python socket 多线程传输数据

一个python写的p2p 文件传输和数据交流的程序

  • 使用了socket套接字的知识
  • threading多线程的知识

本代码上传已上传github

地址:https://github.com/lokey-t/p2p-socket-

花了三天时间从零开始学习python写出来的的,要是有不规范的地方还请见谅
不得不说python确实挺容易的,上手很轻松

话不多说上源码

import socket
import os
import json
import hashlib
import threading
IP = socket.gethostbyname(socket.gethostname())#获取本机ip
PORT_TCP = 9999#默认端口号
PORT_UDP = 9998#默认端口号
LISTEN=3#tcp同时可连接的数量
def md5(path):
    with open(path, 'rb') as file:
        #必须以二进制的方式填入md5函数
        file_byte = file.read()
        fmd5 = hashlib.md5(file_byte).hexdigest()
        #hexdigest()以16进制返回,默认返回的是tuple,包含各种信息
    return fmd5
def send_message(ip,port):#tcp send
    sk = socket.socket()
    sk.connect((ip,port))
    while True:
        print("to",(ip,port))
        msg=input("-->")
        sk.sendall(msg.encode("utf-8"))
        #tcp传输是sendall,udp不一样
        if msg=="over":
            print("---" , (ip, port) , "has been exit---")
            break
    sk.close()
def send_file(ip,port):
    date = {}#创建字典
    sk = socket.socket()
    sk.connect((ip, port))
    print("to", (ip, port))
    path = input("-->input:path")
    date['name'] = path.split('\\')[-1]#用split以\\为分割,并以-1取最后一个作为文件名
    date['size'] = os.path.getsize(path)#os的获取文件大小
    date['md5']= md5(path)
    json_string = "000data00#"+json.dumps(date)#添加数据头,方便识别为数据传输,json.dump转换为json字符串
    sk.sendall(json_string.encode('utf-8'))
    sk.recv(1024)#收到确认信息,开始传输数据
    size=0
    while True:
        with open(path, 'rb') as file:#二进制打开文件并只读
            while size < date['size']:
                fileDate = file.read(1024)
                sk.send(fileDate)
                size += 1024
        if sk.recv(1024).decode("utf-8")=="successed":#等待接收方检查哈希值并决定是否重发
            break
        print("file is broken!! retarying")
    sk.close()

def udp_send_message(ip,port):#用作广播
    s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)#AF_INET:ipv4通信 SOCK_DGRAM:UDP数据报
    address=(ip,port)
    if str(ip)[-3:]=="255":
        print("to",address)
        msg=input("-->")
        sendmsg="#broadcast"+msg
        s.sendto(sendmsg.encode("utf-8"),address)
    else:
        print("to", address)
        msg = input("-->")
        sendmsg = "#message##" + msg
        s.sendto(sendmsg.encode("utf-8"), address)

class udp_receive(threading.Thread):#继承threading用做多线程
    def run(self):#创建线程后自动运行run(self)中的内容
        while over_flag:#over flag 但由于中断,不可实现直接退出循环,使用“daemon=True”的方法强制退出
            data,address=s.recvfrom(1024)
            if data.decode("utf-8")[0:10]=="#broadcast":
                print("Broadcast from",address,"-->",data.decode("utf-8")[10:])
                continue
            elif data.decode("utf-8")[0:10]=="#message##":
                print("udp message from",address,"-->",data.decode("utf-8")[10:])
                continue
            print("!error udp data :"+"from",address,"-->"+data.decode("utf-8"))
    # print("udp listen process exit successes!")
            # msg="received0#"+data
            # s.sendto(msg.encode("utf-8"),address)

class tcp_connect(threading.Thread):
    my_lock = threading.Lock()#进程同步,对addr和conn可能会导致混乱,获取进程锁
    def run(self):
        self.my_lock.acquire()#请求锁
        temp_addr=addr
        temp_conn=conn
        print("---", temp_addr, "has connected you---")
        self.my_lock.release()#释放锁
        rec = temp_conn.recv(1024).decode("utf-8")
        if rec == "over":#收到over结束连接
            print("---",temp_addr, "has been exit---")
            # temp_conn.close()
        elif rec[0:10] == "000data00#":#data标记头,开始准备接受数据
            jsonobj = json.loads(rec[10:])#接收数据用json转换成字典
            print("from", temp_addr, "receiveing data", jsonobj)
            # os.mknod("./jsonobj['name']")
            if not os.path.exists("./recv_file"):#创建目录
                os.makedirs("./recv_file")
            msg = 'ready'
            temp_conn.sendall(msg.encode('utf-8'))#发送准备好接收的信号
            size = 0#当前数据大小
            sizeValue = int(jsonobj['size'])#接受数据的大小
            while True:
                with open("./recv_file/" + jsonobj['name'], 'wb+') as file:#二进制打开并且可读写,若已经有了,就直接覆盖
                    while size < sizeValue:
                        value = sizeValue - size
                        if value > 1024:
                            getdate = temp_conn.recv(1024)
                        else:
                            getdate = temp_conn.recv(value)
                        file.write(getdate)
                        file_length = len(getdate)#解决了一个很大的问题,每次接受的数据包并不一定为1024,1024只是最大大小,很容易文件没有接收完就被认定已经接受完,导致文件只有一半
                        size += file_length
                print("test md5 number")
                if md5(".//recv_file//" + jsonobj['name']) != jsonobj['md5']:#用提供的值对文件进行md5uibi
                    msg = "hash_wrong"
                    temp_conn.sendall(msg.encode("utf-8"))
                    print("file has broken!! retrying")
                    continue
                else:
                    msg = "successed"
                    temp_conn.sendall(msg.encode("utf-8"))
                    print('receive successfully!')
                    break
            print('over')
            # continue
        else:
            print("from", temp_addr, "-->" + rec)#若接受的是其他数据就直直接显示
            # continue
        temp_conn.close()

class tcp_connect_control(threading.Thread):#用以控制tcp的监听,检测到连接就再开启一个新线程交流
    def run(self):
        global  conn
        global  addr
        while over_flag:
            conn, addr = SK.accept()
            tcp_process = tcp_connect()#用class tcp_connect创建对象
            tcp_process.daemon=True#该参数设置这个进程如果退出后不检测进程是否结束,直接退出
            tcp_process.start()
        # print("tcp listen process exit successes!")



#开始程序
over_flag=1#已废弃,用daemon=True的方法强制退出
print("your ---ip:|",IP,"|---tcp port:|",PORT_TCP,"|---udp port:|",PORT_UDP,"|\n")
select=input("'1'=>change ip  | '2'=>change tcp port | '3'=>chang udp port \n other to exit")
if select=="1" :
    input_msg=input("input ip you want chang=>")
    IP=input_msg
elif select=="2" :
    input_msg=input("input tcp port you want chang=>")
    PORT_TCP=int(input_msg)#port类型为int,转换类型
elif select=="3" :
    input_msg=input("input udp port you want chang=>")
    PORT_UDP=int(input_msg)

s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)#同上
s.bind((IP,PORT_UDP))#必须为tuple格式
udp_process=udp_receive()
udp_process.daemon=True#该参数设置这个程序徐如果退出后不检测进程是否结束,直接退出
udp_process.start()
print("Successfully created udp listen")

SK=socket.socket()
SK.bind((IP,PORT_TCP))
SK.listen(LISTEN)
conn=socket.socket()#新建全局变量,用于进程中的函数传递,传递的是已连接的对象,用于tcp的进程
addr=(1,1)#全局变量,初始化为tuple类型,用于进程中传递参数,传递的是已经连接的对象的ip和端口号,用于tcp的进程
tcp_connect_control_procss=tcp_connect_control()
tcp_connect_control_procss.daemon=True#该参数设置这个程序徐如果退出后不检测进程是否结束,直接退出
tcp_connect_control_procss.start()
print("Successfully created tcp listen")
while True:
    select=input("input '1'=>udp_broadcast '2'=>tcp_send_file '3'=>tcp_send_message '0'=>exit\n")
    if select=="1" or select=="2" or select=="3":
        input_ip = input("input ip you want to send=>")
        input_port = input("input port you want to send=>")
    elif select=="0":
        over_flag = 0
        print("waiting process exit")
        break
    else:
        continue
    int_port=int(input_port)
    if select == "1":
        udp_send_message(input_ip, int_port)
    elif select == "2":
        send_file(input_ip, int_port)
    elif select == "3":
        send_message(input_ip, int_port)
exit()



转载请标明出处

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值