Day28--Python--网络通信协议 tcp与udp下的socket

时间:2022-05-14 14:54:16
昨日内容回顾:
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地址+端口: 就能确定一台电脑上的某个应用程序

View CS架构通信流程

网络通信协议
osi 七层协议(记住) 协议: 规定消息的格式
  

Day28--Python--网络通信协议 tcp与udp下的socket

物理层=>数据链路层=>网络层=>传输层=>会话层=>表示层=>应用层
物理层: 电信号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 五层

Day28--Python--网络通信协议 tcp与udp下的socket

        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来触发.
           流程图

Day28--Python--网络通信协议 tcp与udp下的socket

            SYN: Synchronize Sequence Numbers 同步序列编号
四次挥手: Four-Way Wavehand 终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开.在socket编程中,这一过程由客户端或服务端任一方执行
               close来触发.流程图如下:

Day28--Python--网络通信协议 tcp与udp下的socket

        udp协议: 用户数据报协议(User Datagram Protocol)可以理解为写信,知道地址就可以直接发送过去,传输速度快

            面向无连接,消息不可靠,传输速度快.消息是面向包的,有消息保护边界.

        socket是位于应用层和传输层之间,在抽象层.

    tcp和udp区别: 看代码

Day28--Python--网络通信协议 tcp与udp下的socket

# 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()
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()

View udp服务端

# 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()

View udp客户端_1

# 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()

View udp客户端_2

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()