Python开发【第八篇】: 网络编程
内容概要
- 楔子
- 软件开发架构
- 网络基础
- 套接字(socket)
- 粘包
- socketserver模块
一. 楔子
现在有两个python文件a.py和b.py,分别运行,这两个程序之间需要传递一个数据,要怎么做呢?
可以创建一个文件,把a.py想要传递的内容写到文件中,然后b.py从这个文件中读取。
但是a.py和b.py分别在不同电脑上的时候,怎么办?
类似的机制有计算机网盘,qq等等。我们可以在我们的电脑上和别人聊天,可以在自己的电脑上向网盘中上传、下载内容。这些都是两个程序在通信。(客户端和服务端的交互)
二. 软件开发架构
两个程序之间通讯的应用大致可以分为两种:
第一种是应用类:qq、微信、网盘、优酷这一类是属于需要安装的桌面应用
第二种是web类:比如百度、知乎、博客园等使用浏览器访问就可以直接使用的应用
这些应用的本质其实都是两个程序之间的通讯。而这两个分类又对应了两个软件开发的架构~
1. C/S架构
C/S:Client与Server,客户端和服务器端架构,这种架构也是从用户层面(物理层面)来划分的。
这里的客户端一般泛指客户端应用程序EXE,程序需要先安装后,才能运行在用户的电脑上,对用户的电脑操作系统环境依赖较大。
2. B/S架构
B/S即:Browser与Server,中文意思:浏览器端与服务器端架构,这种架构是从用户层面来划分的。
Browser浏览器,其实也是一种Client客户端,只是这个客户端不需要大家去安装什么应用程序,只需在浏览器上通过HTTP请求服务器端相关的资源(网页资源),客户端Browser浏览器就能进行增删改查。
三. 网络基础
1.OSI七层模型
七层模型_百度百科
2.socket概念
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式。 门面模式_百度百科
它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
其实站在你的角度上看,socket就是一个模块。我们通过调用模块中已经实现的方法建立两个进程之间的连接和通信。
也有人将socket说成ip+port,因为ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序。
所以我们只要确立了ip和port就能找到一个应用程序,并且使用socket模块来与之通信。
站在你的角度看socket
3. 套接字(socket)发展史
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。
套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。
socket_百度百科
基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
基于网络类型的套接字家族
套接字家族的名字:AF_INET
(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个。
python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
4. tcp协议和udp协议
TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。
UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
四. 套接字(socket)初使用
基于TCP协议的socket
#!/usr/bin/env python3
# _*_ coding:utf- _*_
import socket
sk = socket.socket()
sk.bind(("127.0.0.1",)) # 把IP绑定到套接字
sk.listen() # 监听链接 conn,addr = sk.accept() # 接收客户端链接 client_data = conn.recv() # 接收客户端信息
print(client_data) #打印客户端信息息 conn.send(b"haha") # 向客户端发送信息 conn.close() # 关闭这次的连接
sk.close() # 关闭服务器socket,相当于停止了服务
server
#!/usr/bin/env python3
# _*_ coding:utf- _*_
import socket sk = socket.socket() # 创建客户端套接字
sk.connect(("127.0.0.1",)) # 连接服务器
sk.send(b"Hi server, I am a client") # 向服务端发送信息
server_data = sk.recv() # 接收信息
print(server_data) # 打印服务端信息 sk.close() # 关闭客户端套接字
client
执行效果:
服务端,正在等待客户端的连接。
# 查看tcp状态, 已经有一个在监听了。
客户端,连接服务端
[root@mongodb ~]# python client.py
server_data
五. 粘包
1. 粘包现象
import socket
sk = socket.socket()
sk.bind(("127.0.0.1",))
sk.listen() conn,_ = sk.accept()
conn.send(b"hello")
conn.send(b"lishichao")
server
import socket sk = socket.socket()
sk.connect(("127.0.0.1",)) msg = sk.recv()
print(msg) msg2 = sk.recv()
print(msg2) sk.close()
client
执行结果:
b'hello'
b'lishichao'
以上是我们想要的两条数据,但是如果有延迟,结果就不是我们想要的了。
# 修改client端:
import time
import socket sk = socket.socket()
sk.connect(("127.0.0.1",))
time.sleep(0.1) # 延迟了0.
msg = sk.recv()
print(msg)
msg2 = sk.recv()
print(msg2) sk.close()
client
执行结果:两条数据粘到一块了。
b'hellolishichao'
b''
2. 粘包的原因
3.解决粘包
问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。
struct模块
该模块可以把一个类型,如数字,转成固定长度的bytes
import struct
ret = struct.pack("i",)
print(ret,len(ret))
# 执行结果: 将数字转成bytes,长度为4
# b'?B\x0f\x00' # 将bytes结果转回数字
res = struct.unpack("i",ret)[] # 取元素0
print(res)
#执行结果: 结果是元祖
# (,)
在接收端接收真正的信息之前,先接收数据的长度,然后按照长度来接收余下的数据
利用struct模块完成了自定义协议的严谨模式
import socket
import struct
sk = socket.socket()
sk.bind(("127.0.0.1",))
sk.listen() conn,_ = sk.accept()
msg = b"hello,world,this is a very long message"
len_msg = struct.pack("i",len(msg))
conn.send(len_msg) # 先发送数据长度
conn.send(msg)
conn.send(b"eva") conn.close()
sk.close()
server
#!/usr/bin/env python3
# _*_ coding:utf- _*_
import socket
import struct
import time sk = socket.socket()
sk.connect(("127.0.0.1",)) time.sleep(0.1)
len_msg = sk.recv() # 接受数据的长度
len_msg = len_msg = struct.unpack("i",len_msg)[] # 转回实际的长度
msg = sk.recv(len_msg) # 根据实际长度,接收数据
print(msg) msg2 = sk.recv()
print(msg2)
sk.close()
client
服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始TCP监听
s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来 客户端套接字函数
s.connect() 主动初始化TCP服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 公共用途的套接字函数
s.recv() 接收TCP数据
s.send() 发送TCP数据
s.sendall() 发送TCP数据
s.recvfrom() 接收UDP数据
s.sendto() 发送UDP数据
s.getpeername() 连接到当前套接字的远端的地址
s.getsockname() 当前套接字的地址
s.getsockopt() 返回指定套接字的参数
s.setsockopt() 设置指定套接字的参数
s.close() 关闭套接字 面向锁的套接字方法
s.setblocking() 设置套接字的阻塞与非阻塞模式
s.settimeout() 设置阻塞套接字操作的超时时间
s.gettimeout() 得到阻塞套接字操作的超时时间 面向文件的套接字的函数
s.fileno() 套接字的文件描述符
s.makefile() 创建一个与该套接字相关的文件
socket更多方法介绍
4. 基于TCP实现的简单文件传输
#!/usr/bin/env python3
# _*_ coding:utf- _*_
import socket
import json
import os
import struct sk = socket.socket()
sk.bind(("0.0.0.0",))
sk.listen() conn,_ = sk.accept() # 下载
file_path = r"E:\python-25期周末班学习资料\day08\视频\3.网络基础.mp4"
file_name = os.path.basename(file_path)
print(file_name)
file_size = os.path.getsize(file_path)
print(file_size) file_info = {"file_name":file_name,"file_size":file_size}
info_json = json.dumps(file_info).encode("utf-8") # str.encode('utf-8') bytes
send_len = struct.pack("i",len(info_json)) conn.send(send_len) # 发送数据长度
conn.send(info_json) # 发送文件信息 # 发送文件
with open(file_path,"rb") as f:
while file_size > :
content = f.read() # 一次读取2048个字节
conn.send(content) # 发送数据
file_size -= len(content) conn.close()
sk.close()
server
#!/usr/bin/env python3
# _*_ coding:utf- _*_
import socket
import struct
import json sk = socket.socket()
sk.connect(("10.0.2.174",)) # 接收文件信息
json_len = sk.recv()
json_len = struct.unpack("i",json_len)[]
info_json = sk.recv(json_len).decode("utf-8")
info_dic = json.loads(info_json)
print(info_dic) # 接收文件
with open(info_dic["file_name"],"wb") as f:
while info_dic["file_size"] > :
content = sk.recv() # 接收数据
info_dic["file_size"] -= len(content)
f.write(content) # 写入文件 sk.close()
client
六. socketserver模块
基于原生的socket tcp协议的server不能同时接收多个client端的请求,使用socketserver模块可以同时接受多个client请求
import socketserver
Host = "0.0.0.0"
Port = class FTPServer(socketserver.BaseRequestHandler):
def handle(self):
conn = self.request
addr = self.client_address
print(addr)
while True:
msg = conn.recv().decode("utf-8")
conn.send(b"hhhhh") # 创建一个server对象,将服务地址绑定到 0.0.0.0:
server = socketserver.ThreadingTCPServer((Host,Port),FTPServer) # 让server永远运行下去,除非强制停止程序
server.serve_forever()
server
import time
import socket
sk = socket.socket()
sk.connect(("127.0.0.1",)) while :
sk.send(b"hello")
msg = sk.recv()
print(msg)
time.sleep() sk.close()
client
七. FTP作业
1. 软件的开发规范
bin --- 程序的入口
conf --- 配置信息
core --- 核心代码
db --- 存放程序数据
log --- 程序日志
目录结构
Python开发【第八篇】: 网络编程的更多相关文章
- iOS开发网络篇—网络编程基础
iOS开发网络篇—网络编程基础 一.为什么要学习网络编程 1.简单说明 在移动互联网时代,移动应用的特征有: (1)几乎所有应用都需要用到网络,比如QQ.微博.网易新闻.优酷.百度地图 (2)只有通过 ...
- Python开发【第一篇】:目录
本系列博文包含 Python基础.前端开发.Web框架.缓存以及队列等,希望可以给正在学习编程的童鞋提供一点帮助!!! Python开发[第一篇]:目录 Python开发[第二篇]:初识Python ...
- Python开发【第二篇】:初识Python
Python开发[第二篇]:初识Python Python简介 Python前世今生 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏 ...
- Python全栈【Socket网络编程】
Python全栈[socket网络编程] 本章内容: Socket 基于TCP的套接字 基于UDP的套接字 TCP粘包 SocketServer 模块(ThreadingTCPServer源码剖析) ...
- python基础教程总结13——网络编程,
1.网络设计模块 1.1 socket模块 根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认. 1)服务器监听:是服务器端套接 ...
- Python开发【第一篇】:目录
本系列博文包含Python基础.前端开发.Web框架.缓存以及队列等,希望可以给正在学习Python编程的朋友们提供一点帮助! .Python开发[第一篇]:目录 .Python开发[第二篇]:初始P ...
- 第八篇:python基础_8 面向对象与网络编程
本篇内容 接口与归一化设计 多态与多态性 封装 面向对象高级 异常处理 网络编程 一. 接口与归一化设计 1.定义 (1)归一化让使用者无需关心对象的类是什么,只需要知道这些对象都具备某些功能就可以了 ...
- Python开发【第*篇】【Socket网络编程】
1.Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. so ...
- iOS开发网络篇—网络编程基础(一)
一.为什么要学习网络编程 1.简单说明 在移动互联网时代,移动应用的特征有: (1)几乎所有应用都需要用到网络,比如QQ.微博.网易新闻.优酷.百度地图 (2)只有通过网络跟外界进行数据交互.数据更新 ...
- Python之路(第三十篇) 网络编程:socket、tcp/ip协议
一.客户端/服务器架构 1.硬件C/S架构(打印机) 打印机作为一个服务端,电脑连接打印机进行打印 2.软件C/S架构 互联网中处处是C/S架构 如谷歌网站是服务端,你的浏览器是客户端(B/S架构也是 ...
随机推荐
- OpenGL+VS2012编译环境配置
OpenGL库主体分为三部分,分别是 gl(OpenGL核心库) glu(Utility Library,OpenGL实用库) glut(Utility Toolkit,OpenGL实用工具库) gl ...
- OpenGL(十六) 鼠标、键盘交互响应事件
OpenGL中通过鼠标和键盘跟程序交互的实现需要实现注册鼠标和键盘响应事件,在一定条件下,该事件被触发,事件里的程序被执行,达到交互的目的. 通过glutMouseFunc(&OnMouse) ...
- Qt5.8 下链接 Mysql 错误以及解决方法(无论 Mysql 是什么版本的,64 位 Qt 要用 64 位的 Mysql 驱动,32 位的 Qt 要用 32 位的Mysql 驱动)
Qt 5.8 下链接 Mysql(Windows 平台下),有朋友会出现一个这个无法连接的错误 QSqlDatabase: QMYSQL driver not loaded QSqlDatabase: ...
- easyui Full Layout
@{ Layout = null;}<!DOCTYPE html><html><head> <meta name="viewport&q ...
- jquery对象及页面加载完成写法
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- MVVM 下 ContextMenu的命令绑定
原文:MVVM 下 ContextMenu的命令绑定 由于ContextMenu不继承父级的DataContext,所以如果要绑定父级的DataContext,直接DataContext=" ...
- 梧桐那时雨http://blog.csdn.net/fuchaosz/article/details/51882935?readlog
Ubuntu 16.04 一系列软件安装命令,包括QQ.搜狗.Chrome.vlc.网易云音乐安装方法 原创 2016年07月20日 11:44:01 标签: ubuntu 27024 1 简介 Ub ...
- vs2015 生成 cordova 页面中文乱码
原文:vs2015 生成 cordova 页面中文乱码 1.用VS2015新创建Cordova项目,启动运行index.html 中文显示乱码 解决方案: 1.使用text/html通用解析编码utf ...
- WPF中的多进程(Threading)处理实例(一)
原文:WPF中的多进程(Threading)处理实例(一) 说明:希望通过揣摩这些案例,能进一步了解进程的工作原理. 1.方法一描述的是在同一窗口中,在计算素数的同时而不影响Canvas的工作. 方法 ...
- SqlServer 无法为可更新的订阅设置发布服务器登录名 sp_link_publication
原文:SqlServer 无法为可更新的订阅设置发布服务器登录名 sp_link_publication 没有截图: 创建可更新订阅,正常创建了发布,在订阅端创建订阅,最后一步提示完成,却出现了警告: ...