勿骄勿燥,仍是要定下心学习,还有有些没定下心python
1.基础知识算法
2.tcp与udp协议编程
3.网络套接字json
4.基于c/s结构的服务器客户端的实验小程序
开始今日份总结设计模式
1.基础知识浏览器
现有的软件,绝大多数是基于C/S结构,那么就须要介绍网络编程,毕竟如今的绝大多数数据仍是在网络中传输。下面先说明一些网络的基础知识,不过对于从事网络工程的来讲只是很简单的基础知识,缓存
1.1 C/S架构安全
C/S架构中C指的是client(客户端软件),s指的是server(服务器端软件),而本章的主要学习目的是写一个基于C/S架构的软件,客户端软件与服务器端基于网络通讯。如今基本的C/S架构基本是下图这样:客户端与服务器基于网络传输互相传输数据。服务器
1.2 B/S架构
B/S架构中的B指的是Brower(浏览器),S指的是Server(服务器端),平常中的网页浏览也是B/S架构。(不是很了解)
1.3 OSI的七层协议
了解了C/S结构的大概构成,就说一下OSI七层协议,在美国军方发展ARPA网络以后,将他公布用于学术网络以后,就大爆发同样的发展了,因为网络协议的公开性,各家发展各自的标准,以致于各类网络之间并不能互通,国际ISO标准组织就颁发一套标准七层网络协议,七层网络协议有应用层,表示层,会话层,传输层,网络层,数据链路层,物理层
应用层(7) -------------------------------------------------- 提供应用程序间的通讯
表示层 (6)-------------------------------------------------- 处理数据格式以及数据加密等
会话层 (5)-------------------------------------------------- 创建,维护管理会话
传输层 (4)-------------------------------------------------- 创建主机端到端的链接
网络层 (3)-------------------------------------------------- 寻址以及路由选择
数据链路层(2)--------------------------------------------- 提供介质访问,链路管理等
物理层(1) -------------------------------------------------- 比特流传输
通常567整合为应用程序,1234为数据流层
1.4 经常使用的TCP/IP的五层协议
因为IOS标准组织制定标准时间长,在厂商中TCP/IP更容易理解,虽然有一些结构性的缺陷,可是TCP/IP已经成为名副其实的标准了
主要有应用层,传输层,网络层,数据链路层,物理层
应用层 -------------------------------------------------- 用户数据
传输层 -------------------------------------------------- TCP报头+上层数据
网络层 -------------------------------------------------- IP报头+上层数据
数据链路层 --------------------------------------------- LLC报头+上层数据+FCS MAC报头+上层数据+FCS
物理层 -------------------------------------------------- 0101的Bit
以上都是由上向下传输或者是由下向上传输
2. TCP与UDP
基于TCP/IP协议,主要有俩种传输形式,一种是TCP,一种UDP
TCP(传输控制协议):面向链接 重传机制 确认机制 流量控制 (保证可靠)
UDP:面向无链接 低开销 传输效率高,速度快
2.1 TCP的三次握手与四次挥手
TCP因为传输数据,要和对端要先创建通道才能够传输数据,因此被称之为可靠的传输协议,传输数据以前须要创建通道,等通道创建成功后,发送数据片断,每发送一个数据片断,发送一个确认码ack,发送端只有在收到ack确认码才会发送下一个数据片断,不然会从新发送未被确认数据片断。因为要确认的东西不少,因此TCP的报头有20字节。这样TCP传输就很占用传输带宽。
如下图片就是三次握手以及四次挥手的过程,这个会后面网络编程中较大联系
2.2 UDP协议
UDP协议因为不须要和对端确认通道以及对方是否存在,只须要知道对端是谁就能够,因此UDP也被称之为不可靠传输协议。UDP协议只负责传送,不负责数据是否到达,因此低开销,传输速率高,UDP头部只有8字节。
2.3端口基础
端口范围在0----65535(2*16)
知名端口号(0----1023,其余软件禁止使用),
注册端口号(1024----49151,通常用于软件注册,不过一些知名的端口仍是建议不使用)
随机端口号(49152----65535,通常用于客户端软件,随机端口)
3.基于c/s结构的服务器客户端的实验
3.1基础知识点-socket
对于上面网络基础了解后,咱们能够这么想之后咱们本身敲代码了,那我是否是就须要记住这些几层协议,传输层,网络层具体作什么,这个时候就须要一个新的模块了,socket,python中处理网络编程相关的问题,Socket是应用层与TCP/IP协议族通讯的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来讲,一组简单的接口就是所有。这样的话,用户就不须要再次了解下面具体怎么实施,只须要知道怎么操做socket就行了,结构如图。
3.2.1socket的套接字
family(socket家族)
socket type类型
(Only SOCK_STREAM and SOCK_DGRAM appear to be generally useful.)
服务器端套接字类型
用户端套接字类型
公用套接字类型
3.3 tcp创建链接的过程
4.实验
4.1实验一
目的:基于TCP创建一个简单的服务端与客户端,服务端可以接收客户端传输过来的值
服务器端 import socket sk = socket.socket()#实例化对象 sk.bind(('127.0.0.1',8500))#对象绑定 sk.listen(3)#服务端监听 print('loading.....') conn,addr = sk.accept() msg = conn.recv(1024) print(msg) conn.close() sk.close()
客户端 import socket sk = socket.socket() sk.connect(('127.0.0.1',8500))#客户端链接 sk.send(b'123') sk.close()
4.2实验二
目的:基于TCP实现客户端与服务器之间的能够退出的聊天程序
#服务器 import socket sk = socket.socket()#实例化对象 sk.bind(('127.0.0.1',8500))#对象绑定 sk.listen()#服务端监听 print('loading.....') conn,addr = sk.accept() print(addr) while True: msg = conn.recv(1024).decode() if msg =='q':break else: print(msg) msg1 = input('>>>').strip().encode() conn.send(msg1) if msg1 =='q':break conn.close() sk.close()
#客户端 import socket sk = socket.socket() sk.connect(('127.0.0.1',8500))#客户端链接 while True: msg = input('>>>').strip().encode() sk.send(msg) if msg =='q':break ret = sk.recv(1024).decode() if ret =='q':break else:print(ret) sk.close()
咱们来讲一下程序中系统的实现方法,对于咱们写的小程序,并非咱们直接创建链接,服务器端与客户端之间直接收发数据,真正的作法就是咱们把发送的数据交给操做系统,操做系统在调用物理硬件将数据发出,接收端也是发送信息交给操做系统让他从网卡这里获取收到的数据,传递给应用程序。
补充一个:服务器端接收数据中的conn是一个套接字对象,是一个基于TCP协议创建的一个连接
4.3实验三
目的:基于TCP实现简单的时间服务器
#服务器端 import time import socket sk = socket.socket() sk.bind(('127.0.0.1',8500)) sk.listen() conn,addr = sk.accept() data = conn.recv(1024).decode() time_date = time.strftime(data).encode() conn.send(time_date) conn.close() sk.close()
#客户端 import socket sk = socket.socket() sk.connect(('127.0.0.1',8500)) msg ='%Y.%m.%d %H:%M:%S' sk.send(msg.encode()) date=sk.recv(1024).decode() print(date) sk.close()
4.4实验
基于TCP测试一次性发送多个字符串
#服务端 import socket sk = socket.socket() sk.bind(('127.0.0.1',8500)) sk.listen() conn,addr = sk.accept() msg = conn.recv(1024).decode() print(msg) conn.close() sk.close()
#客户端 import socket sk = socket.socket() sk.connect(('127.0.0.1',8500)) sk.send(b'hello') sk.send(b'world') print('......') sk.close()
咱们会发现服务端收到了一个b’helloworld’这么一个bytes类型的字符串,这个现象就叫作黏包现象
先解释一下黏包的产生:
它的发生主要是由于socket缓冲区致使的,你的程序实际上无权直接操做网卡的,你操做网卡都是经过操做系统给用户程序暴露出来的接口,那每次你的程序要给远程发数据时,实际上是先把数据从用户态copy到内核态,这样的操做是耗资源和时间的,频繁的在内核态和用户态以前交换数据势必会致使发送效率下降, 所以socket 为提升传输效率,发送方每每要收集到足够多的数据后才发送一次数据给对方。若连续几回须要send的数据都不多,一般TCP socket 会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。
仍是看上图,发送端能够是一K一K地发送数据,而接收端的应用程序能够两K两K地提走数据,固然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个总体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,所以TCP协议是面向流的协议,这也是容易出现粘包问题的缘由。而UDP是面向消息的协议,每一个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不一样的。怎样定义消息呢?能够认为对方一次性write/send的数据为一个消息,须要明白的是当对方send一条信息的时候,不管底层怎样分段分片,TCP协议层会把构成整条消息的数据段排序完成后才呈如今内核缓冲区。
这里咱们说一下send与recv的区别
例如基于tcp的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束
所谓粘包问题主要仍是由于接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所形成的。
既然有问题,那就解决问题,既然为了解决这个问题,就会引入管道这个类
#服务端 import socket import struct sk = socket.socket() sk.bind(('127.0.0.1',8500)) sk.listen() conn,addr = sk.accept() while True: send_msg = input('>>>').strip().encode('utf-8') msg_len = struct.pack('i',len(send_msg)) conn.send(msg_len) conn.send(send_msg) conn.close() sk.close()
import socket import struct sk = socket.socket() sk.connect(('127.0.0.1',8500)) data =sk.recv(4) msg_len = int(struct.unpack('i',data)[0]) print(msg_len) recive_len = 0 msg =b'' while recive_len<msg_len: date2 = sk.recv(5) msg +=date2 recive_len+=5 print(msg.decode()) sk.close()
4.5实验
目的:基于TCP服务器端给客户端传文件,验证客户端接收的文件完整性,动态的显示接收进度
#服务器端 import socket import struct import json sk = socket.socket() sk.bind(('127.0.0.1',8500)) sk.listen() conn,addr = sk.accept() header ={'filename':r'D:\test.zip','file_size':922933359,'MD5':'6237eb2c55b34f87e856422896c1f440'} header_json = json.dumps(header)#将头文件字典转换为json模式 header_bytes = header_json.encode('utf-8')#将json文件转换为bytes类型 header_len = struct.pack('i',len(header_bytes))#将头文件从管道发送过去 conn.send(header_len) conn.send(header_bytes) with open(header['filename'],'rb')as f1: while True: contact = f1.read(1024) if contact: conn.send(contact) else: break print('文件传输完毕!') conn.close() sk.close()
#客户端 import socket import struct import json import hashlib import sys sk = socket.socket() sk.connect(('127.0.0.1',8500)) data =sk.recv(4)#接收头文件那四个字节 head_len = int(struct.unpack('i',data)[0]) head_bytes = sk.recv(head_len)#接收头文件 head_json = head_bytes.decode('utf-8')#将bytes类型的头文件解析成json格式 header = json.loads(head_json)#将json格式的文件反解成字典 def check_md5(file):#验证文件MD5 ret = hashlib.md5() with open(file,mode='rb')as f2: while True: contect = f2.read(1024)#读取1024字节 if contect: ret.update(contect) else: break return ret.hexdigest() receive_num =0 with open('test.zip','wb')as f1: while receive_num < header['file_size']: contact = sk.recv(1024) if contact: f1.write(contact) receive_num += 1024 float_rate =receive_num/header['file_size'] rate = round(float_rate * 100, 2) sys.stdout.write('\r已下载:\033[1;32m{0}%\033[0m'.format(rate))#动态显示接收进度! else: break print('文件下载成功!') num =check_md5('test.zip') if num ==header['MD5']: print('文件校验成功,文件完整') else: print('文件校验失败,文件不完整') sk.close()