网络编程
一、网络模型
1.osi 七层网络模型
OSI模型,即开放式通信系统互联参考模型,是国际标准化组织(ISO)提出的一个试图使各种计算机在世界范围内互连为网络的标准框架,简称OSI。这是一种事实上被TCP/IP 4层模型淘汰的协议,在当今世界上没有大规模使用
具体七层 | 数据格式 | 功能连接方式 | 典型设备 |
---|---|---|---|
应用层 | 用户的应用程序和网络之间的接口 | 计算机 | |
表示层 | 协商数据交换格式 | ||
会话层 | 允许用户使用简单易记的名称建立连接 | ||
传输层 | 提供终端到终端的可靠连接 | pc机、手机 | |
网络层 | 使用权数据路由经过大型网络 | 路由器 | |
数据联络层 | 决定访问网络介质的方式 | 交换机 | |
物理层 | 将数据转换为可通过物理介质传送的电子信号 | 光纤、同轴电缆 |
2.TCP/IP网络模型
TCP/IP 是基于 TCP 和 IP 这两个最初的协议之上的不同的通信协议的大的集合
OSI | 功能 | TCP/IP |
---|---|---|
应用层 | 应用层HTTP、FTP、DNS、SMTP | |
传输层 | 传输层TCP、UDP | |
网络互联层 | 网络层IP | |
网络接入层(主机网络层) | 物理层、数据联络层 |
3.TCP协议(重要、面试、研发的)非常复杂
TCP是一种面向连接的,可靠的,给予字节流的传输层通讯协议
TCP需要先建立连接,然后传输,在传输的时候,还会做校验,如果有错误,重新传
TCP是因特网中的传输层协议,使用三次握手协议建立连接。当主动方发出SYN连接请求后,等待对方回答SYN+ACK,并最终对对方的 SYN 执行 ACK 确认,三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了
总结:三次握手,四次挥手
互联网传输数据主要用到两个协议,TCP、UDP
UDP是一种无连接的,不可靠的,基于字节流的传输层通讯协议
类比:TCP是打电话,接通才能说话;UDP是发电报,直接发,不管收
4.IP(网络之间互连的协议)
在因特网中,它是能使连接到网上的所有计算机网络实现相互通信的一套规则,规定了计算机在因特网上进行通信时应当遵守的规则。任何厂家生产的计算机系统,只要遵守IP协议就可以与因特网互连互通。
IP地址(IP Address)
基本概念
IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。
Internet上的每台主机(Host)都有一个唯一的IP地址。IP协议就是使用这个地址在主机之间传递信息,这是Internet 能够运行的基础。IP地址是一个32位的二进制数,被分割为4个“8位二进制数”,IP地址通常用“点分十进制”表示成(a.b.c.d)的形式,其中,a,b,c,d都是0~255之间的十进制整数。
IP地址是一种在Internet上的给主机编址的方式,也称为网际协议地址。常见的IP地址,分为IPv4与IPv6两大类。IPV4就是有4段数字,每一段最大不超过255,2011年2月3日IPv4位地址分配完毕,为了扩大地址空间,拟通过IPv6重新定义地址空间。IPv6采用128位地址长度。
互联网上的IP地址统一由一个叫“ICANN”(Internet Corporation for Assigned Names and Numbers,互联网赋名和编号公司)的组织来管理。
区别方法
公有地址(Public address)由Inter NIC(Internet Network Information Center因特网信息中心)负责。这些IP地址分配给注册并向Inter NIC提出申请的组织机构。通过它直接访问因特网。
私有地址(Private address)属于非注册地址,专门为组织机构内部使用。
分类介绍
每个IP地址包括两个标识码(ID),即网络ID和主机ID,同一个物理网络上的所有主机都使用同一个网络ID,网络上的一个主机有一个主机ID与其对应。Internet委员会定义了5种IP地址类型以适合不同容量的网络,即A类~E类。
其中A、B、C3类(如下表格)由InternetNIC在全球范围内统一分配,D、E类为特殊地址。
网络类别 | 最大网络数 | IP地址范围 | 最大主机数 | 私有IP地址范围 |
---|---|---|---|---|
A | 126(2^7-2) | 1.0.0.0–127.255.255.255 | 16777214 | 10.0.0.0–10.255.255.255 |
B | 16384(2^14) | 128.0.0.0–191.255.255.255 | 65534 | 172.16.0.0–172.31.255.255 |
C | 2097152(2^21) | 192.0.0.0–223.255.255.255 | 254 | 192.168.0.0–192.168.255.255 |
5.端口(port)
基本概念
如果把IP地址比作一间房子 ,端口就是出入这间房子的门。真正的房子只有几个门,但是一个IP地址的端口 可以有65536(即:2^16)个,端口是通过端口号来标记的,端口号只有整数,范围是从0 到65535(2^16-1)。本地操作系统会给那些有需求的进程分配协议端口。
分类
1.周知端口:从0到1023,操作系统给系统服务固定分配的端口。通常这些端口的通讯明确表明了某种服务的协议。例如:80端口实际上总是HTTP通讯。
2.注册端口:从1024到49151,分配给用户进程的端口。也就是说有许多服务绑定于这些端口,这些端口同样用于许多其它目的。例如:许多系统处理动态端口从1024左右开始。
3.动态和/或私有端口:从49152到65535,一般不固定分配,由操作系统,动态分配
总结
IP用来表示终端,端口就是用来区别程序,注意一个端口只能同时被一个程序监听
端口号及功能
端口号 | 功能 |
---|---|
22 | ssh远程登录协议规定的默认监听端口 |
8000 | Django运行命令监听端口 |
3306 | MySQL |
6379 | Redis |
二、套接字(socket)
1.基本概念
TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket)或插口,套接字是对TCP、UDP的封装,为了简化技术难度
套接字用(IP地址:端口号)表示,它是网络通信过程中端点的抽象表示,包含进行网络通信必需的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口
2.Python中使用socket
创建套接字
# 创建套接字
from socket import * # 导入socket模块
tcp_sock = socket(family=AF_INET, type=SOCK_STREAM)
udp_sock = socket(family=AF_INET, type=SOCK_DGRAM)
# family是地址簇,默认AF_INET type 指定是tcp还是udp
创建一个TCP服务器
# 创建一个tcp server
from socket import socket # 导入模块
# 1.创建套接字
tcp_server = socket() # 默认就是tcp
# 2.绑定监听端口和ip
tcp_server.bind(('',8888)) #('', 端口号)
# 3.开始监听
tcp_server.listen(5) #5表示排队(最多排五个)
# 4.等待连接
print('开始等待')
client,addr = tcp_server.accept() # 阻塞的,直到有连接进来
print('有连接进来 它的地址是:', addr)
while True:
# 5.接受数据
res = client.recv(1024)
if res == b'':
client.close()
break
print('接收到数据:', res.decode(encoding='utf-8'))
# 6. 发送数据
client.send(res)
创建一个TCP客户端
# 创建一个TCP客户端
from socket import socket
# 1.创建socket
client = socket()
# 2.连接服务端
client.connect(('127.0.0.1',8888)) #('IP',端口号)
while True:
# 发送信息
client.send(input('请输入信息:').encode())
# 接收信息
data = client.recv(1024) # 单位:1024个字节
print(data.decode(encoding='utf-8'))
socket爬取百度图片
import socket
import re
# 创建一个客户端
client = socket.socket()
# 连接地址
client.connect(('image.baidu.com', 80))
# 构造报文
request = 'GET /search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=index&fr=&hs=0&xthttps=111111&sf=1&fmq=&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&word=动漫&oq=动漫&rsp=-1 HTTP/1.0\r\nHost: image.baidu.com\r\n\r\n'
# 发送请求消息
client.send(request.encode())
# 循环接收网站返回的数据
res = b''
data = client.recv(1024)
while data:
res += data
data = client.recv(1024)
# 关闭socket
client.close()
# 图片首页地址
url_root = 'image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=index&fr=&hs=0&xthttps=111111&sf=1&fmq=&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&word=动漫&oq=动漫&rsp=-1'
# "hoverURL":"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2535548369,3399272173&fm=26&gp=0.jpg"
image_url_all = re.findall(r'"hoverURL":"(.*?jpg|jpeg|gif|png)"', res.decode(), re.S)
n = 0
for image_url in image_url_all:
n += 1
# 切割出host和path
host = image_url.split('//')[-1].split('/', maxsplit=1)[0]
path = image_url.split('//')[-1].split('/', maxsplit=1)[1]
# 再创建一个客户端
client_image = socket.socket()
# 连接到图片地址
client_image.connect((host, 80))
# 构造图片地址的报文
request_image = 'GET /{} HTTP/1.0\r\nHost: {}\r\nReferer: {}\r\n\r\n'.format(path, host, url_root)
client_image.send(request_image.encode())
res = b''
data_image = client_image.recv(1024)
while data_image:
res += data_image
data_image = client_image.recv(1024)
print(res)
# 提取出图片信息
content_image = re.findall(b'\r\n\r\n(.*)', res, re.S)
# 写入文件
with open(r'../socket-text/image/{}.jpg'.format(n), 'wb') as f:
f.write(content_image[0])
上面代码的简单封装
import socket
import re
def socket_get_content(hosts, paths, url_root):
client = socket.socket()
client.connect((hosts, 80))
request = 'GET /{} HTTP/1.0\r\nHost: {}\r\nReferer:{}\r\n\r\n'.format(paths, hosts, url_root)
client.send(request.encode())
res = b''
data = client.recv(1024)
while data:
res += data
data = client.recv(1024)
client.close()
return res
image_url_all = socket_get_content('image.baidu.com',
'search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=index&fr=&hs=0'
'&xthttps=111111&sf=1&fmq=&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height='
'&face=0&istype=2&ie=utf-8&word=动漫&oq=动漫&rsp=-1',
'')
# "hoverURL":"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2535548369,3399272173&fm=26&gp=0.jpg"
image_urls = re.findall(r'"hoverURL":"(.*?jpg|jpeg|gif|png)"', image_url_all.decode(), re.S)
image_name = 0
for image_url in image_urls:
image_name += 1
host = image_url.split('//')[-1].split('/', maxsplit=1)[0]
path = image_url.split('//')[-1].split('/', maxsplit=1)[1]
image_contents = socket_get_content(host,
path,
'image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1'
'&fm=index&fr=&hs=0&xthttps=111111&sf=1&fmq=&pv=&ic=0&nc=1&z=&se=1&showtab=0'
'&fb=0&width=&height=&face=0&istype=2&ie=utf-8&word=动漫&oq=动漫&rsp=-1')
content_image = re.findall(b'\r\n\r\n(.*)', image_contents, re.S)
with open(r'image/{}.jpg'.format(image_name), 'wb') as f:
f.write(content_image[0])