昨日内容回顾: 1. CS架构 服务端客户端架构 软件CS架构: 京东,淘宝,QQ,微信,暴风影音,快播 硬件CS架构: 打印机 服务端: 提供服务的 客户端: 享受服务的 BS架构: 浏览器和服务端: 谷歌, 360, IE 2. 网络通信当中遇到的名词 硬件 网卡: 接收电信号 mac地址: 网卡的唯一标识,全球唯一,6位-分16进制 广播: 信息发给所有人 单播: 单独发给某个人或某个设备 广播风暴: 不安全,容易拥堵网络 IP地址: 划分广播域 IPv4: 4个点分10进制 192.168.15.113 IPv6: 6个冒号分十六进制 集线器: 将所有电脑连接起来 交换机: 升级版集线器 DHCP协议: 自动分配IP地址 [ 动态主机配置协议(Dynamic host configuration protocol)] 划分广播域=>IP网段 192.168.15.0 - 192.168.15.255属于同一子网 子网掩码: 计算目标IP地址是否是同一网段 同一网段的: 广播发送 不同网段的: 发送给路由器 路由器(内网的作用): 管理局域网 找外部网路的设备: 域名: www.jd.com => IP地址 DNS服务器: 记录着所有的域名和它对应的那台服务器的IP地址的对应关系,理解为一个字典.{'www.jd.com':192.168.15.12} (因为IP地址不好记,所以出现了域名) DNS:域名服务器(Domain Name System) 网关: 在路由器这儿,把关拟对外的请求 路由器: 网关:外网的IP地址 NAT技术: 网络地址转换:将你的IP地址转换为网关的IP地址,才能把你的消息发送出去. 访问外网的时候还要发送自己的IP地址,但是内网的IP地址不能在公网使用,所以使用NAT技术将自己的IP地址转化为网关的IP地址,然后发送消息 Network Address Translation 网络地址转换 外网,又称为公网,网关的IP地址又称为外网IP地址或者公网IP地址 路由器(外网的作用): 转发消息 路由协议: 计算一个路由转发消息的最优路径,然后进行路由转发 外网的服务器会有端口号 192.168.15.12:8000 后面是端口号, 访问这个服务器 端口: 标识电脑上某个应用程序 通过IP地址+端口: 就能确定一台电脑上的某个应用程序
网络通信协议
osi 七层协议(记住) 协议: 规定消息的格式
物理层=>数据链路层=>网络层=>传输层=>会话层=>表示层=>应用层
物理层: 电信号010101低压高压
数据链路层: 帧头+ 以太网协议头+IP协议头+tcp/udp协议头+http协议头+数据+帧尾
网络层: 帧头+IP协议头+tcp/udp协议头+http协议头+数据+帧尾
传输层: 帧头+tcp/udp协议+http协议头+数据+帧尾
会话层: 建立一套自动接收和发送信息,自动网络寻址的机制
表示层: 解决不同系统之间的通信问题
应用层: 帧头+http协议+数据+帧尾
数据中包括目标信息和冗余信息
帧头: 一个标识,数据的开头
帧尾: 标识,数据的结尾
保证信息的完整
协议: 规定消息的格式
每加一层协议,会加协议头规定格式
以太网协议ethernet:
一组电信号构成一个数据包,叫做'帧'.
每一数据帧分成:报头head和数据data两部分
head | data
head 包含(固定18个字节)
发送者/源地址,6个字节 源mac地址
接受者/目标地址,6个字节 目标mac地址
数据类型,6个字节
data包含(最短46字节,最长1500字节)
数据包的具体内容:
head长度+data长度=最短64字节,最长1518字节,超过最大限制就分片发送
--------------------------------------------------------------------------------------------------------------------------------------------------
tcp/ip 五层, tcp/ip 四层
tcp/ip 五层
arp协议: 通过IP地址找MAC地址 arp地址解析协议(Address Resolution Protocol)
tcp协议(重要): 传输控制协议(Transmission Control Protocol)
面向连接,消息可靠,对udp来讲传输速度慢,消息是面向流的,无消息保护边界
每一条TCP连接都有两个端点,这种端点叫套接字(socket), 它的定义为端口号拼接到IP地址即构成了套接字.
例如,若IP地址为192.3.4.16 而端口号为80, 那么得到的套接字为: 192.3.4.16:80 传输消息可靠,不会丢.
当应用程序希望通过 TCP 与另一个应用程序通信时,它会发送一个通信请求。这个请求必须被送到一个确切的地址。
在双方“握手”之后,TCP 将在两个应用程序之间建立一个全双工 (full-duplex,双方都可以收发消息) 的通信。
这个全双工的通信将占用两个计算机之间的通信线路,直到它被一方或双方关闭为止。
ACK: 命令正确应答;确认字符(acknowledgement character)
三次握手与四次挥手
三次握手: Three-Way Handshake 即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包已确认连接的建立.在socket编程中,这一过程由客户端执行connect来触发.
流程图
SYN: Synchronize Sequence Numbers 同步序列编号
四次挥手: Four-Way Wavehand 终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开.在socket编程中,这一过程由客户端或服务端任一方执行
close来触发.流程图如下:
udp协议: 用户数据报协议(User Datagram Protocol)可以理解为写信,知道地址就可以直接发送过去,传输速度快
面向无连接,消息不可靠,传输速度快.消息是面向包的,有消息保护边界.
socket是位于应用层和传输层之间,在抽象层.
tcp和udp区别: 看代码
# udp服务端
import socket
server = socket.socket(type=socket.SOCK_DGRAM) # datagram 数据报
ip_port = ('192.168.15.87', 8001)
server.bind(ip_port)
while 1:
print('准备接收消息...')
msg, addr = server.recvfrom(1024)
print(msg.decode('utf-8'))
if msg == 'bye':
break
server.close()
# udp 客户端 可以同时打开多个客户端,所有客户端发送的消息服务端都可以接收到
import socket
while 1:
client = socket.socket(type=socket.SOCK_DGRAM)
server_ip_port = ('192.168.15.87', 8001)
msg = input('发送信息:')
client.sendto(msg.encode('utf-8'), server_ip_port)
if msg == 'bye':
break
client.close()
udp 练习题
模拟QQ聊天
# udp服务端 import socket BUFSIZE = 1024 server = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('192.168.15.87', 8001) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(ip_port) while 1: print('接收消息中...') msg, addr = server.recvfrom(BUFSIZE) print('来自[%s:%s]的一条消息:%s' % (addr[0], addr[1], msg.decode('utf-8'))) answer = input('回复消息,按Q退出程序:').encode('utf-8') server.sendto(answer, addr) if answer.upper() == 'Q': break server.close()
# udp 客户端 1 import socket BUFSIZE = 1024 client = socket.socket(type=socket.SOCK_DGRAM) dic = { 'mike': ('192.168.15.87', 8001), 'jack': ('192.168.15.87', 8001), 'alex': ('192.168.15.87', 8001), 'rose': ('192.168.15.87', 8001) } while 1: print('在线联系人: mike, jack, alex, rose') name = input('输入要聊天的对象,按Q退出:').strip() if name.upper() == 'Q': flag = 0 break while 1: msg = input('请输入消息,按Q退出:') if msg.upper() == 'Q': break if not msg or not name or name not in dic: continue client.sendto(msg.encode('utf-8'), dic[name]) print('等待回复中...') answer, addr = client.recvfrom(BUFSIZE) print('收到一条来自[%s]的消息: %s' % (name, answer.decode('utf-8'))) client.close()
# udp 客户端 2 import socket BUFSIZE = 1024 client = socket.socket(type=socket.SOCK_DGRAM) dic = { 'mike': ('192.168.15.87', 8001), 'jack': ('192.168.15.87', 8001), 'alex': ('192.168.15.87', 8001), 'rose': ('192.168.15.87', 8001) } while 1: print('在线联系人: mike, jack, alex, rose') role = input('请选择你要聊天的对象,按Q退出程序:').strip() if role.upper() == 'Q': break for k in dic.keys(): if role == k: while 1: msg = input('请输入聊天内容,按Q结束聊天:') if msg.upper() == 'Q': break elif msg.strip() == '': continue else: client.sendto(msg.encode('utf-8'), dic[role]) answer, addr = client.recvfrom(BUFSIZE) print('来自[%s]的一条消息: %s' % (k, answer.decode('utf-8'))) else: print('未找到该联系人.') client.close()
type = SOCK_STREAM 流式套接字 (TCP协议)
type = SOCK_DGRAM 数据报套接字 (UDP协议)
family
基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
基于网络类型的套接字家族
套接字家族的名字:AF_INET
(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我们只使用AF_INET)
练习题
# 3.udp协议下的socket聊天工具(类10086) # 1. 服务端 # - 接收客户端发送的信息并作出回复。 # - 检查是否有某些指定关键字并回复消息,如果发送过来的消息中还有sb字符串,那么将sb替换成alexsb,然后和你要输入的内容组合起来发送给客户端。 # 2. 多个客户端 # - 客户端向服务端发送信息 # 服务端 import socket server = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('192.168.15.87', 8003) server.bind(ip_port) while 1: msg, addr = server.recvfrom(1024) msg = msg.decode('utf-8') if 'sb' in msg: msg = msg.replace('sb', 'alexsb') print(msg) content = input('请回复:').strip() server.sendto(content.encode('utf-8'), addr) if content == 'bye': break server.close()
# 客户端 import socket client = socket.socket(type=socket.SOCK_DGRAM) server_ip_port = ('192.168.15.87', 8003) while 1: content = input('请输入:').strip() client.sendto(content.encode('utf-8'), server_ip_port) if content == 'bye': break msg, addr = client.recvfrom(1024) print(msg.decode('utf-8')) if msg == 'bye': break client.close()