2018.8.7
目录
day01回顾
1,osi七层模型---》四层 五层
2,三次握手,四次挥手
3,tcp和udp区别
网络概念:主机 端口 ip 协议
- 服务端:localhost / 127.0.0.1
- 客户端:只是在本机启动客户端 用127.0.0.1访问
- 服务端:0.0.0.0
- 客户端:可以在本机用127.0.0.1、172.60.50.181访问,
局域网内用 172.60.50.181
- 服务端:172.60.50.181
- 客户端:局域网内用 172.60.50.181访问
套接字 socket
- 流式套接字 --》 tcp 可靠地 有连接
- 数据报套接字 --》udp 不可靠 无连接
tcp服务端
socket --> bind --> listen --> accept --> recv/send -->close
day02
recv() 特征
- *如果连接的另一端断开连接,则recv立即返回空字符串
- recv是从接受缓冲区取出内容,当缓冲区为空则阻塞
- recv如果一次接受不完缓冲区内容,下次会继续接收
send() 特征
- *如果发送的另外一段不存在则会产生pipe Broken
- recv是向发送缓冲区发送内容,当缓冲区为满阻塞
网络收发缓冲区
- 在内存中开辟的区域,用作发送和接受的缓冲
- 协调数据的收发(接受和处理)速度
- 减少和磁盘的交互
sendall(data)
- 功能:tcp套接字发送消息
- 参数:同send
- 返回值:如果发送成功返回None
tcp粘包
- 产生原因:
tcp传输以字节流的方式发送消息,消息之间没有边界
发送比接收的速度快,此时如果发送和接收速度不匹配就会产生粘包 - 影响:对每次发送的内容是一个独立的意思需要单独识别,如果发送的内容本身就是连续的整体,此时不需要处理粘包
- 如何处理
1,每次发送后加一个结尾标志
2,发送一个数据结构
3,每次发送有一个间隔
基于udp的服务端编程
- 创建套接字
sockfd = socket(AF_INET,SOCK_DGRAM) - 绑定地址
sockfd.bind() - 消息的收发
- 接收
- 格式:data,addr = sockfd.recvfrom(buffersize)
- 功能:接收UDP消息
- 参数:每次最多接收消息的大小,字节
- 返回值:data接收到的内容
addr 消息发送方的地址 - recvfrom 一次接收一个数据包,如果数据包超出了一次能够接收的大小则会丢失没有收到的内容
- 发送
- 格式sockfd.sendto(data,addr)
- 功能 : udp发送消息
- 参数 : data 要发送的内容 bytes
addr 消息要发送给谁(目标地址) - 返回值 : 发送的字节数
- 接收
-
关闭套接字
sockfd.close()
服务器:#utip.py from socket import * HOST = '0.0.0.0' PORT = 8888 ADDR = (HOST,PORT) #创建套接字 sockfd = socket(AF_INET,SOCK_DGRAM) #绑定地址 sockfd.bind(ADDR) #消息收发 while True: data,addr = sockfd.recvfrom(1024) print('Receive from %s:%s' %(addr,data.decode())) sockfd.sendto('收到消息'.encode(),addr) sockfd.close()
客户端:
#namen.py from socket import * import sys #命令行输入服务器地址 if len(sys.argv) < 3: print(''' argv is error !! start as python3 udp_client.py 127.0.0.1 8888 ''') raise HOST = sys.argv[1] PORT = int(sys.argv[2]) ADDR = (HOST,PORT) #创建套接字 sockfd = socket(AF_INET,SOCK_DGRAM) while True: data = input('消息: ') if not data: break sockfd.sendto(data.encode(),ADDR) data,addr = sockfd.recvfrom(1024) print('从服务器收到:',data.decode()) sockfd.close()
运行结果:
cookie:
- import sys
- sys.argv
- 功能 : 获取来自命令行的参数,形成一个列表
- argv[0]是命令本身 其他内容默认以空格分割放到列表中
示例:
import sys
print(sys.argv)
python3 namen.py
['namen.py']
python3 namen.py 1 2 hello
['namen.py', '1', '2', 'hello']
python3 namen.py 1 2 'hello 1' 2
['namen.py', '1', '2', 'hello 1', '2']
tcp流式套接字和udp数据报套接字的区别
- 流式套接字采用字节流方式进行传输,而数据报套接字使用数据报形式传输数据
- tcp套接字会产生粘包,udp不会
- tcp编程可以保证消息的完整性,udp则不能保证
- tcp需要listen accept udp 不用
- tcp消息的发送接收使用recv send sengdall, udp使用recvfrom sendto
要求:能够说出tcp,udp协议的区别和编程实现上的差异
socket模块和套接字属性
(s为套接字)
套接字属性
s.type:表示套接字类型
s.family:地址类型
套接字属性方法
s.fileno() #(s表示一个套接字对象)
- 功能:获取套接字的文件描述符
- 文件描述符:每一个IO事件操作系统都会分配一个不同的正整数与之匹配,该整数即为此IO的文件描述符
一般是从3开始,默认0 ,1,2在下面3个套接字 - sys.stdin 0
- sys.stdout 1
- sys.stderr 2
s.getsockname()
- 功能:获取套接字绑定的地址
s.getpeername()
- 功能:使用accept生成的套接字调用,获取该套接字对应的客户端地址
s.setsockopt(level,optname,value)
- 功能:设置套接字选项
- 参数:level设置选项的类型 SOL_SOCKET
可选值还有:IPPROTO_TCP IPPROTP_IP
optname: 选项类型中的子选项
value :设置的值
e.g.:s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.getsockopt(level,optname)
- 功能 : 获取套接字选项值
- 参数: level : 获取选项的类型 SOL_SOCKET
- IPPROTP_TCP IPPROTO_IP
- optname: 选项类型中的子选项
- 返回值 : 获取到的值
SQL_SOCKET对应的子选项:
一些套接字示例:from socket import * s = socket() #设置端口可立即重用 s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) print(s.getsockopt(SOL_SOCKET,SO_REUSEADDR)) #获取套接字类型 print(s.type) #获取地址类型 print(s.family) #文件描述符 print(s.fileno()) s.bind(('172.60.50.181',9999)) #获取绑定地址 print(s.getsockname()) s.listen(5) c,addr = s.accept() print(c.getpeername()) print("addr:",addr) data = c.recv(1024) print(data) c.close() s.close()
udp套接字应用---》广播
- 一点发送多点接收
- 目标地址 :广播地址 一个网段内最大的地址
172.60.50.255 - 广播风暴:占用大量的带宽造成网络拥塞
- 目标地址 :广播地址 一个网段内最大的地址
- format()
- 功能 : 字符串属性函数 用来组合字符串
format()示意:
#format()示例
In [3]: s = "{} is a {}".format('Tom','boy')
In [4]: s
Out[4]: 'Tom is a boy'
In [5]: s = "{1} is a {0}".format('Tom','boy')
In [6]: s
Out[6]: 'boy is a Tom'
下面是广播示例:(广播接收端)
from socket import *
#创建数据报套接字
s = socket(AF_INET,SOCK_DGRAM)
#设置套接字可以接收广播
s.setsockopt(SOL_SOCKET,SO_BROADCAST,1)
#绑定端口
s.bind(('',9999))
while True:
try:
msg,addr = s.recvfrom(1024)
print("从{}获取信息:{}".\
format(addr,msg.decode()))
except (KeyboardInterrupt,SyntaxError):
raise
except Exception as e:
print(e)
s.close()
广播发送端:
from socket import *
from time import sleep
#设置广播地址 "<broadcast>"
dest = ('172.60.50.255',9229)
s = socket(AF_INET,SOCK_DGRAM)
#设置能够发送广播
s.setsockopt(SOL_SOCKET,SO_BROADCAST,1)
while True:
sleep(2)
s.sendto("今日立秋".encode(),dest)
s.close()
tcp应用--》http传输:
- http协议--》超文本传输协议
- 用途:网站中浏览器获取网页的过程
编写基于http协议的数据传输
将想要获取的内容,以http协议的格式发送给服务端,服务端根据格式进行解析获取到真实需求,将结果以http协议的格式回复给客户端 - 特点:
- 应用层协议,传输层使用tcp服务
- 简单,灵活,可以使用多种编程语言操作
- 无状态的协议,即不记录用户的输入内容
- http1.1 ---》http2.0 技术成熟度和稳定性,http1.1 支持持久连接
http请求(Request)
- 请求格式:
- 请求行 具体的请求类别和请求内容
GET /index.html HTTP/1.1 请求类别 请求内容 协议版本
请求类别:
请求名 作用 GET 获取网络资源(通过地址栏发起来的请求是get请求) POST 提交一定的附加数据,得到返回结果 HEAD 获取响应头 PUT 更新服务器资源 DELETE 删除服务器资源 CONNECT TRACE 用于测试 OPTIONS 获取服务器性能(跨域先会发一次,简称:嗅探) - 请求头 :对请求的具体描述
Accept: text/html
每一个键值对占一行,描述了一个特定信息
- 空行
- 请求体 :具体的参数或者提交的内容
get参数或者post提交的内容
- 请求行 具体的请求类别和请求内容
http响应(response)
- 响应格式
- 响应行 :反馈具体的响应情况
响应码:HTTP/1.1 200 OK 协议版本 响应码 附加信息
响应码 作用 1xx 提示信息,表示请求已经接受 2xx 响应成功 3xx 响应需要重定向 4xx 客户端错误 5xx 服务端错误 常见响应码 作用 200 成功 404 请求内容不存在 401 没有访问权限 500 服务器发生未知错误 503 服务器暂时不能执行 - 响应头
Accept-Ranges: bytes
以键值对方式给出响应信息的具体描述
每个键占一行 - 空行
- 响应体 :将客户端请求内容进行返回
- 响应行 :反馈具体的响应情况
要求:
- 直到http作用
- 掌握http协议,请求和响应的格式以及每一部分做什么
- 掌握http协议中请求的基本类型和作用
- 直到http协议响应码的类型和表达含义
- 了解通过http协议请求网页的流程
作业:编写一个程序,完成一个文本文件(图片)在服务端和客户端之间的传输要求使用tcp套接字
解析:
from socket import *
from time import sleep
s = socket()
s.bind(('0.0.0.0',8888))
s.listen(5)
c,addr = s.accept()
print("Connect from ",addr)
f = open('img.jpg','rb')
#将文件名称告知对方
c.send('img.jpg'.encode())
sleep(0.1)
while True:
data = f.read(1024)
if not data:
break
c.send(data)
sleep(0.1)
c.send('##'.encode())
data = c.recv(1024)
print(data.decode())
f.close()
c.close()
s.close()
from socket import *
s = socket()
s.connect(('172.60.50.181',8888))
filename = s.recv(1024).decode()
f = open('/home/tarena/'+filename,'wb')
while True:
data = s.recv(1024)
if data == b'##':
break
f.write(data)
s.send("接收完成".encode())
f.close()
s.close()