day19-网络编程基础(二)

今天没有很多概念性的东西,主要是方法性的东西以及编程的一些方法吧

今日份目录

1.UDP传输的特点以及实验

2.UTP与UDP传输的区别

3.基于tcp的low版带验证功能的FTP小程序

4.基于socketserver的多人聊天

5.验证客户端的完整性

6.socket的阻塞模型与非阻塞模型

 

 

开始今日份整理

1.UDP传输的特点以及实验

UDP与TCP都是传输层中的重要传输协议,下面说一下UDP的特点

1.1 UDP传输特点

无连接的,面向数据包,不可靠的,快速的,不需要accept/connect 也没有握手

1.2 基于UDP的多人聊天

#服务端
import socket

sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind(('127.0.0.1',8500))

while True:
    msg,addr = sk.recvfrom(1024)
    print(msg.decode())
    send_message = input('>>>').strip()
    sk.sendto(send_message.encode(),(addr))

sk.close()
#客户端
import socket

sk = socket.socket(type= socket.SOCK_DGRAM)
while True:
    msg = input('>>>').strip()
    send_msg ='test1:'+ msg
    sk.sendto(send_msg.encode(),('127.0.0.1',8500))
    msg,addr =sk.recvfrom(1024)
    print(msg.decode())

sk.close()

多个

客户端只要多拷贝几份client代码就可以了,就会发现在服务器端出现很多用户发过来的信息,由于是在命令行界面,在input界面会阻塞,所以并不会马上看到多人发来的信息。在图形化界面就不会这样了

2.UTP与UDP传输的区别

UTP的特性

  • 面向连接的 可靠的 全双工的 流式传输
  • 面向连接 :同一时刻只能和一个客户端通信
  • 三次握手、四次挥手
  • 可靠的 :数据不丢失、慢
  • 全双工 :能够双向通信
  • 流式传输 :粘包 无边界

UDP的特性

  • 无连接的 面向数据包 不可靠的 快速的
  • 无连接的 :不需要accept/connect 也没有握手
  • 面向数据包的 :不会粘包
  • 不可靠的 :没有自动回复的机制
  • 快速的 :没有那些复杂的计算、保证数据传输的机制

我们从上面看到,tcp为什么会黏包的原因:本地端不知道发送端具体发送了多长的字符串,解决办法就是自定义协议,规定在传输之前先要传输的大小先行发送过去。在对方来接收规定长度的文件。

3.基于tcp的low版带验证功能的FTP小程序

做实验前谨记一个传大文件需要先传一个字典过去,让对方知道

P5GZ(@DX2BOV{Q2YYE86U[5

3.1实验一

目的:服务器以及客户端互传文件并动态校验MD5

#服务端,比较low

import socket
import struct
import json
import hashlib
import os

sk = socket.socket()#网络传输基本配置
sk.bind(('127.0.0.1',8500))#本地端口
sk.listen()
conn,addr = sk.accept()

def download(file_dic):#客户端上传
    print(3)
    while True:
        file_name = file_dic['file_name']#获取文件名
        file_size = file_dic['file_size']#获取文件大小
        ret = file_write(file_size,file_name)#收到的文件的MD5值
        print(ret)
        send_header(ret)#发送给客户端,服务器端的MD5值
        print(addr,'接收成功!')
        break

def upload(file_dic):#客户端下载
    while True:
        isfile_dict = {}
        file_name = file_dic['file_name']
        if os.path.isfile(file_name):
            isfile_dict['flag'] =1
            send_header(isfile_dict)
            send_file_dic ={}
            send_file_dic['file_name'] = file_name
            send_file_dic['file_size'] = os.path.getsize(file_name)
            print(send_file_dic)
            send_header(send_file_dic)     #发送头文件
            ret =file_read(file_name)#传输具体的文件获取MD5值
            send_header(ret)  # 发送给客户端,服务器端的MD5值
            print(addr,'发送成功!')
            break
        else:
            isfile_dict['flag'] = 0
            send_header(isfile_dict)
            break



def get_header(date):#获取头文件
    header_json = date.decode()
    header_dic = json.loads(header_json)
    return header_dic

def send_header(obj):#发送头文件
    header_json = json.dumps(obj)  # 头文件的变为str类型
    header_bytes = header_json.encode()  # 头文件变为bytes类型
    header_len = struct.pack('i', len(header_bytes))  # 计算头文件的长度
    conn.send(header_len)  # 发送头文件的长度
    conn.send(header_bytes)  # 发送头文件

def file_write(file_size,filename):#文件写入,同时得到写入后的文件的MD5值
    recevive_num = 0
    has = hashlib.md5()
    with open(filename,'wb')as f2:
        while recevive_num< file_size:
            contact = conn.recv(1024)
            if contact:
                f2.write(contact)
                recevive_num += len(contact)
                has.update(contact)
            else:
                break
        return {'MD5':has.hexdigest()}


def file_read(filename):#文件读取,同时得到他的MD5值
    has = hashlib.md5()
    with open(filename,'rb')as f1:
        while True:
            contact = f1.read(1024)
            conn.send(contact)
            if contact:
                has.update(contact)
            else:
                break
        return {'MD5':has.hexdigest()}

def run():
    print(addr)
    while True:
        file_len = conn.recv(4)[0]
        file_bytes = conn.recv(file_len)
        file_dic =get_header(file_bytes)
        print(file_dic)
        if file_dic['operation']== 'download':
            upload(file_dic)
        elif file_dic['operation'] == 'upload':
            download(file_dic)


run()

#客户端

import socket
import struct
import json
import hashlib
import os
import sys

header_dic ={}
menu=[
    ('上传文件',),
    ('下载文件',)
]


def __upload():
    while True:
        file_get = input('请输入你要上传文件的绝对路径>>>').strip()
        if os.path.isfile(file_get):
            file_name = os.path.basename(file_get)#获取文件名
            header_dic['file_name']=file_name
            file_size = os.path.getsize(file_get)#获取文件大小
            header_dic['file_size'] = file_size
            header_dic['operation'] = 'upload'
            send_header(header_dic)#发送文件的头文件
            ret =file_read(file_get,file_size)#传输具体的文件获取MD5值
            MD5_len= sk.recv(4)[0]
            MD5_bytes = sk.recv(MD5_len)
            MD5_dict = get_header(MD5_bytes)
            if ret['MD5'] == MD5_dict['MD5']:
                print('上传成功!')
                break
            else:
                print('上传失败!')
                break
        else:
            print('你输入的文件路径不存在,请重新确认!')


def __download():
    while True:
        file_get = input('请输入你要下载的文件的文件名>>>').strip()
        header_dic['file_name'] = file_get
        header_dic['operation'] = 'download'
        send_header(header_dic)#发送一个头文件给服务器
        header_len = sk.recv(4)[0]#接收头文件的长度
        header_bytes = sk.recv(header_len)#接收头文件的bytes类型
        file_dic = get_header(header_bytes)#解包,获得头文件
        if file_dic['flag'] == 0:
            print('你要下载的文件不存在,请重新选择......')
        else:
            print('文件存在')
            recv_file_header =sk.recv(4)[0]
            recv_file_header_bytes = sk.recv(recv_file_header)
            recv_file_dict = get_header(recv_file_header_bytes)
            file_size = recv_file_dict['file_size']
            file_name = recv_file_dict['file_name']
            ret = file_write(file_size,file_name)
            MD5_len = sk.recv(4)[0]#获得MD5字典的长度
            MD5_bytes = sk.recv(MD5_len)
            MD5_dict = get_header(MD5_bytes)#获取MD5字典的内容
            if ret['MD5'] == MD5_dict['MD5']:
                print('文件校验成功!')
                break
            else:
                print('你接收的文件损坏,无法匹配!')
                break


def send_header(obj):#发送头文件
    header_json = json.dumps(obj)  # 头文件的变为str类型
    header_bytes = header_json.encode()  # 头文件变为bytes类型
    header_len = struct.pack('i', len(header_bytes))  # 计算头文件的长度
    sk.send(header_len)  # 发送头文件的长度
    sk.send(header_bytes)  # 发送头文件


def get_header(date):#获取头文件
    header_json = date.decode()
    header_dic = json.loads(header_json)
    return header_dic


def file_read(filename,file_size):#文件读取,同时得到他的MD5值
    has = hashlib.md5()
    recevive_num = 0
    with open(filename,'rb')as f1:
        while True:
            contact = f1.read(1024)
            if contact:
                sk.send(contact)
                has.update(contact)
                float_rate = int(recevive_num / file_size)
                rate = round(float_rate * 100, 2)
                sys.stdout.write('\r已下载:\033[1;32m{0}%\033[0m'.format(rate))
            else:
                break
        return {'MD5':has.hexdigest()}


def file_write(file_size,filename):#文件写入,同时得到写入后的文件的MD5值
    recevive_num = 0
    has = hashlib.md5()
    with open(filename,'wb')as f2:
        while recevive_num < file_size:
            contact = sk.recv(1024)
            if contact:
                f2.write(contact)
                recevive_num += len(contact)
                has.update(contact)
                float_rate = float(recevive_num / file_size)
                rate = round(float_rate * 100, 2)
                sys.stdout.write('\r已下载:\033[1;32m{}%\033[0m'.format(rate))

            else:
                break
        print(2)
        return {'MD5':has.hexdigest()}


if __name__ == '__main__':
    sk = socket.socket()  # 网络传输基本配置
    sk.connect(('127.0.0.1', 8500))  # 远端端口
    while True:
        for i,j in enumerate(menu,1):
            print(i,j[0])
        choice = int(input('请输入你想要实现的功能>>>').strip())
        if choice == 1:
            __upload()
        elif choice == 2:
            __download()

4.基于socketserver的多人聊天

#服务端
import socketserver

class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
        conn = self.request
        while True:
            date = conn.recv(1024).decode()
            print(date)
            msg = input('>>>').encode()
            conn.send(msg)

server = socketserver.ThreadingTCPServer(('127.0.0.1',8500),Myserver)
server.serve_forever()
#客户端
import socket

sk = socket.socket()
sk.connect(('127.0.0.1',8500))
while True:
        msg ='one' + input('>>>').strip()
        sk.send(msg.encode())
        data = sk.recv(1024).decode()
        print(data)

sk.close()

5.验证客户端的完整性

如果你想在分布式系统中实现一个简单的客户端链接认证功能,又不像SSL那么复杂,那么利用hmac+加盐的方式来实现

#服务端
import os
import hmac
import socket

sk = socket.socket()
sk.bind(('127.0.0.1',8500))
sk.listen()

def auth(conn):
    secret_key = b'alexsb'
    rand_b = os.urandom(32)
    conn.send(rand_b)
    obj = hmac.new(secret_key, rand_b)
    res1 = obj.digest()
    res2 = conn.recv(1024)
    cmp_res = hmac.compare_digest(res1, res2)
    return cmp_res

conn,addr = sk.accept()

res = auth(conn)
if res :
    print('正常客户端')
    conn.send(b'hello')
else:
    conn.close()
conn.close()
sk.listen()
#客户端
import hmac
import socket

sk = socket.socket()
sk.connect(('127.0.0.1',8500))

def auth(sk):
    secret_key = b'alexsb'
    rand_b =sk.recv(32)
    obj =  hmac.new(secret_key,rand_b)
    res2 = obj.digest()
    sk.send(res2)

auth(sk)
msg = sk.recv(1024)
print(msg)

sk.close()

6.socket的阻塞模型与非阻塞模型

只是涉及很浅显的一点阻塞模型

# 阻塞模型
    # accept
    # recv
    # recvfrom

# 非阻塞
    # accept
    # recv
    # recvfrom

import socket

# sk = socket.socket()
# sk.setblocking(False)
# sk.bind(('127.0.0.1',9000))
# sk.listen()
# while True:
#     try:
#         sk.accept()
#     except BlockingIOError:
#         pass
posted @ 2019-01-24 16:52  柴犬砍柴  阅读(235)  评论(0编辑  收藏  举报