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小程序
做实验前谨记一个传大文件需要先传一个字典过去,让对方知道
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
day19-网络编程基础(二)的更多相关文章
- 第十章:Python の 网络编程基础(二)
本課主題 Python中的作用域补充 socketserver 源码 线程的介绍和操作实战 进程的介绍和操作实战 协程的介绍和操作实战 本周作业 Python中的作用域补充 Python世界里沒有块级 ...
- iOS开发网络篇—网络编程基础
iOS开发网络篇—网络编程基础 一.为什么要学习网络编程 1.简单说明 在移动互联网时代,移动应用的特征有: (1)几乎所有应用都需要用到网络,比如QQ.微博.网易新闻.优酷.百度地图 (2)只有通过 ...
- iOS开发网络篇—网络编程基础(一)
一.为什么要学习网络编程 1.简单说明 在移动互联网时代,移动应用的特征有: (1)几乎所有应用都需要用到网络,比如QQ.微博.网易新闻.优酷.百度地图 (2)只有通过网络跟外界进行数据交互.数据更新 ...
- Java网络编程基础(Netty预备知识)
今天在家休息,闲来无事,写篇博客,陶冶下情操~~~ =================我是分割线================ 最近在重新学习Java网络编程基础,以便后续进行Netty的学习. 整 ...
- 脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?
1.引言 本文接上篇<脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手>,继续脑残式的网络编程知识学习 ^_^. 套接字socket是大多数程序员都非常熟悉的概念,它是计算机 ...
- [转帖]脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?
脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么? http://www.52im.net/thread-1732-1-1.html 1.引言 本文接上篇<脑残式网 ...
- python全栈开发从入门到放弃之socket网络编程基础
网络编程基础 一 客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构 互联网中处处是C/S架构 如黄色网站是服务端,你的浏览器是客户端(B/S架构也是C/S架构的一种) 腾讯作为服务 ...
- 网络编程基础之C/S架构和TCP/IP协议
一.何谓C/S架构 C指的是client(客户端软件),S指的是Server(服务端软件),既然我们的的标题是网络编程基础, 那我们就一起来学习怎样写一个C/S架构的软件,实现服务端与客户端软件基于网 ...
- 第5章 Linux网络编程基础
第5章 Linux网络编程基础 5.1 socket地址与API 一.理解字节序 主机字节序一般为小端字节序.网络字节序一般为大端字节序.当格式化的数据在两台使用了不同字节序的主机之间直接传递时,接收 ...
- linux 网络编程 基础
网络编程基础 套接字编程需要指定套接字地址作为参数,不同的协议族有不同的地址结构,比如以太网其结构为sockaddr_in. 通用套接字: struct sockaddr { sa_family_t ...
随机推荐
- Spring Boot(十)Logback和Log4j2集成与日志发展史
一.简介 Java知名的日志有很多,比如:JUL.Log4j.JCL.SLF4J.Logback.Log4j2,那么这些日志框架之间有着怎样的关系?诞生的原因又是解决什么问题?下面一起来看. 1.1 ...
- AppBoxFuture(三): 分而治之
系统数据量达到一定程度后必将采用分库分表的方式来提高系统性能,但传统的分库分表方式也必将带来更高的开发复杂程度.新一代的NewSql及NoSql数据库由于天生的分布式存储基因,既保证了能够横向扩展 ...
- Spring Boot 2.x(十三):你不知道的PageHelper
PageHelper 说起PageHelper,使用过Mybatis的朋友可能不是很陌生,作为一款国人开发的分页插件,它基本上满足了我们的日常需求.但是,我想去官方文档看看这个东西配合Spring B ...
- Perl获取主机名、用户、组、网络信息
获取主机名.用户.组.网络信息相关函数 首先是获取主机名的方式,Perl提供了Sys::Hostname模块,可以查询当前的主机名: use Sys::Hostname; print hostname ...
- .Net语言 APP开发平台——Smobiler学习日志:在手机应用开发中如何快速调用电话拨打功能
样式一 一.目标样式 我们要实现上图中的效果,需要如下的操作: 1.从工具栏上的”Smobiler Components”拖动一个PhoneButton控件到窗体界面上 2.修改PhoneButton ...
- js如何使用正则表达式实现过滤HTML标签?(/<[^<>]+>/g)
js如何使用正则表达式实现过滤HTML标签?(/<[^<>]+>/g) 一.总结 js进阶正则表达式实现过滤HTML标签(<>标签中不能包含标签实现过滤HTML标签 ...
- 31.C++-虚函数之构造函数与析构函数分析
1.构造函数不能为虚函数 当我们将构造函数定义为虚函数时,会直接报错: 首先回忆下以前学的virtual虚函数概念: 如果类定义了虚函数,创建对象时,则会分配内存空间,并且为该父类以及其所有子类的内存 ...
- 内部类访问局部变量为什么必须要用final修饰
内部类访问局部变量为什么必须要用final修饰 看了大概五六篇博客, 讲的内容都差不多, 讲的内容也都很对, 但我觉得有些跑题了 略叙一下 String s = "hello"; ...
- Linux基础:CentOS安装python3.7
1.下载python3 wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tgz 2.解压 [root@mycentos ~]# ta ...
- iOS----------使用cocoapods遇到的问题
-bash: /usr/local/bin/pod: /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby: bad ...