python ==》 网络编程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python ==》 网络编程相关的知识,希望对你有一定的参考价值。
一、服务端和客户端
BS架构 (腾讯通软件:server+client)
CS架构 (web网站)
C/S架构与socket的关系:
我们学习socket就是为了完成C/S架构的开发
二、OSI七层模型
互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层
学习socket一定要先学习互联网协议:
1.首先:本节课程的目标就是教会你如何基于socket编程,来开发一款自己的C/S架构软件
2.其次:C/S架构的软件(软件属于应用层)是基于网络进行通信的
3.然后:网络的核心即一堆协议,协议即标准,你想开发一款基于网络通信的软件,就必须遵循这些标准。
4.最后:就让我们从这些标准开始研究,开启我们的socket编程之旅
socket:就是位于 应用层和传输层 之间。socket帮我们封装了一系列协议,统一标准。
三、socket是什么?
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
四、套接字发展史及分类
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。
1、基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
2、基于网络类型的套接字家族
套接字家族的名字:AF_INET
(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
五、套接字工作流程
生活中的场景,你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。
生活中的场景就解释了这工作原理,也许TCP/IP协议族就是诞生于生活中,这也不一定。
socket例子:
1.服务端与客户端的正常通信。
import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.bind((‘127.0.0.1‘,8080)) phone.listen(5) print(‘really ==== go!!‘) conn,client_addr=phone.accept() print(conn,client_addr) data=conn.recv(1024) conn.send(data.upper()) print(‘client data:<%s>‘%data) conn.close() phone.close()
import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect ((‘127.0.0.1‘,8080)) phone.send(‘hello‘.encode(‘utf-8‘)) data1 = phone.recv(1024) print(‘server back res:<%s>‘%data1) phone.close()
首先: 服务端 先开始运行,等待接收, 之后,客户端运行,向服务端发送信息。 结果如下: 服务端: really ==== go!! <socket.socket fd=452, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘127.0.0.1‘, 8080), raddr=(‘127.0.0.1‘, 53789)> (‘127.0.0.1‘, 53789) client data:<b‘hello‘> 客户端: server back res:<b‘HELLO‘> 客户端发了 hello 给服务端, 服务端收到信息,做了 大写化处理,返回给客户端。
2.服务端与客户端的正常通信。socket通信循环
import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.bind((‘127.0.0.1‘,8080)) phone.listen(5) print(‘really ==== go!!‘) conn,client_addr=phone.accept() print(conn,client_addr) while True: #通信循环 data=conn.recv(1024) # print(‘server has recv‘) conn.send(data.upper()) print(‘client data:<%s>‘%data) conn.close() phone.close()
import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect ((‘127.0.0.1‘,8080)) while True: cmd = input(‘>>:‘).strip() if not cmd:continue #如果cmd为空,继续发 phone.send(cmd.encode(‘utf-8‘)) print(‘====> has send‘) data = phone.recv(1024) print(‘server back res:<%s>‘%data) phone.close()
首先: 这里比上一个例子,优化了,这里设置了input,可以自己输入。 添加了个循环,当客户端输入为空,不在报错,而是需要继续输入。 结果如下: 服务端: really ==== go!! <socket.socket fd=452, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘127.0.0.1‘, 8080), raddr=(‘127.0.0.1‘, 53865)> (‘127.0.0.1‘, 53865) client data:<b‘1‘> client data:<b‘2‘> client data:<b‘3‘> client data:<b‘a‘> client data:<b‘b‘> client data:<b‘c‘> 客户端: >>:1 ====> has send server back res:<b‘1‘> >>:2 ====> has send server back res:<b‘2‘> >>:3 ====> has send server back res:<b‘3‘> >>:a ====> has send server back res:<b‘A‘> >>:b ====> has send server back res:<b‘B‘> >>:c ====> has send server back res:<b‘C‘> 注意: 这里服务端在接到客户端的额信息是,只做了加大化吃力,所以把abc处理后为ABC返回给客户端。
3.服务端与客户端的正常通信。socket链接循环。
import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.bind((‘127.0.0.1‘,8080)) phone.listen(5) print(‘really ==== go!!‘) while True: #链接循环 conn,client_addr=phone.accept() print(conn,client_addr) while True: #通信循环 try: data=conn.recv(1024) # print(‘server has recv‘) conn.send(data.upper()) print(‘client data:<%s>‘%data) except Exception: break conn.close() phone.close()
import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect ((‘127.0.0.1‘,8080)) while True: cmd = input(‘>>:‘).strip() if not cmd:continue #如果cmd为空,继续发 phone.send(cmd.encode(‘utf-8‘)) data = phone.recv(1024) print(‘server back res:<%s>‘%data) phone.close()
import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect ((‘127.0.0.1‘,8080)) while True: cmd = input(‘>>:‘).strip() if not cmd:continue #如果cmd为空,继续发 phone.send(cmd.encode(‘utf-8‘)) print(‘====> has send‘) data = phone.recv(1024) print(‘server back res:<%s>‘%data) phone.close()
首先,这里加的链接循环是为了防止,当我们有多个客户端时, 要关闭其中一个,而不导致整个程序出错。 在没做链接循环前,当我们关闭了其中一个客户端,服务端那里是不能在运行的。 输出结果: 服务端: really ==== go!! <socket.socket fd=384, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘127.0.0.1‘, 8080), raddr=(‘127.0.0.1‘, 53898)> (‘127.0.0.1‘, 53898) client data:<b‘1‘> client data:<b‘2‘> client data:<b‘a‘> client data:<b‘b‘> client data:<b‘c‘> <socket.socket fd=384, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘127.0.0.1‘, 8080), raddr=(‘127.0.0.1‘, 53911)> (‘127.0.0.1‘, 53911) <socket.socket fd=384, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘127.0.0.1‘, 8080), raddr=(‘127.0.0.1‘, 53911)> (‘127.0.0.1‘, 53911) client data:<b‘a‘> client data:<b‘s‘> client data:<b‘d‘> 客户端1: >>:1 server back res:<b‘1‘> >>:2 server back res:<b‘2‘> >>:3 server back res:<b‘3‘> >>:a server back res:<b‘A‘> >>:b server back res:<b‘B‘> >>:c server back res:<b‘C‘> 客户端2: >>:a ====> has send server back res:<b‘A‘> >>:s ====> has send server back res:<b‘S‘> >>:d ====> has send server back res:<b‘D‘>
4.socket模拟ssh远程执行。
import subprocess import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.bind((‘127.0.0.1‘,8081)) phone.listen(5) print(‘really ==== go!!‘) while True: #链接循环 conn,client_addr=phone.accept() print(conn,client_addr) while True: #通信循环 try: cmd=conn.recv(1080) if not cmd: break #针对linux #执行cmd命令,拿到cmd的结果,结果应该是bytes类型 #。。。 #发送命令结果 res = subprocess.Popen(cmd.decode(‘utf-8‘), shell=True, stdout=subprocess.PIPE, # 正确 stderr=subprocess.PIPE # 错误 ) stdout = res.stdout.read() stderr = res.stderr.read() conn.send(stdout+stderr) except Exception: break conn.close() phone.close()
import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect ((‘127.0.0.1‘,8081)) while True: cmd = input(‘>>:‘).strip() if not cmd:continue #如果cmd为空,继续发 phone.send(cmd.encode(‘utf-8‘)) cmd_res = phone.recv(1080) print(cmd_res.decode(‘gbk‘)) phone.close()
说明: 1.客户端远程执行服务端。 2.登录的是windows系统,用的是‘gbk’ 编码。 服务端: really ==== go!! <socket.socket fd=400, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘127.0.0.1‘, 8081), raddr=(‘127.0.0.1‘, 53955)> (‘127.0.0.1‘, 53955) 客户端: >>:dir 驱动器 E 中的卷没有标签。 卷的序列号是 0001-0682 E:\\zbk\\work_\\work_8.21 socket模拟ssh远程执行 的目录 2017/08/21 19:25 <DIR> . 2017/08/21 19:25 <DIR> .. 2017/08/21 19:25 333 客户端1.py 2017/08/21 16:29 367 客户端2.py 2017/08/21 19:25 966 服务端2.py 2017/08/21 19:05 413 模块subprocess.py 4 个文件 2,079 字节 2 个目录 266,249,355,264 可用字节 >>:ipconfig /all Windows IP 配置 主机名 . . . . . . . . . . . . . : DESKTOP-0QR7V9H 主 DNS 后缀 . . . . . . . . . . . : 节点类型 . . . . . . . . . . . . : 混合 IP 路由已启用 . . . . . . . . . . : 否 WINS 代理已启用 . . . . . . . . . : 否 以太网适配器 以太网: 媒体状态 . . . . . . . . . . . . : 媒体已断开连接 连接特定的 DNS 后缀 . . . . . . . : 描述. . . . . . . . . . . . . . . : Realtek PCIe GBE Family Controller 物理地址. . . . . . . . . . . . . : 80-FA-5B-3C-8F-54 DHCP 已启用 . . . . . . . . . . . : 是 自动配置已启用. . . . . . . . . . : 是 无线局域网适配器 本地连接* 1: 媒体状态 . . . . . . . . . . . . : 媒体已断开连接 连接特定的 DNS 后缀 . . . . . . . : 描述. . . . . . . . . . . . . . . : Microsoft Wi-Fi Direct Virtual Adapter 物理地址. . . . . . . . . . . . . : 70-1C-E7-32-BC-D5 DHCP 已启用 . . . . . . . . . . . : 是 自动配置已启用. . . . . . . . . . : 是 无线局域网适配器 WLAN: 连接特定的 DNS 后缀 . . . . . . . : 描述. . . . . . . . . . . . . . . : Intel(R) Dual Band Wireless- >>:
5.socket 解决粘包问题。
import struct import subprocess from socket import * phone = socket(AF_INET,SOCK_STREAM) phone.bind((‘127.0.0.1‘,8080)) phone.listen(5) print(‘ready go !!‘) while True: conn,client.addr=phone.accept() print(conn,client_addr) while True: try: cmd = conn.recv(1024) if not cmd : break res = subprocess.Popen(cmd.decode(‘utf-8‘),shell = True, stdout = stdout.subprocess.PIPE, stderr = stderr.subprocess.PIPE,) stdout = res.stdout.read() stderr = res.stderr.read() header = struct.pack(‘i‘,len(stdout)+len(stderr)) conn.send(header) conn.send(stdout) conn.send(stderr) except Exception: break conn.close() phone.close()
import struct from socket import * phone = socket(AF_INET,SOCK_STREAM) phone.connect((‘127.0.0.1‘,8080)) while True: cmd = input(‘>>:‘).strip() if not cmd : continue phone.sent(cmd.encode(‘utf-8‘)) header_struct = phone.recv(4) unpack_res = struct.unpack(‘i‘,header_struct) total_size = unpack_res[0] total_data = b‘‘ recv_size = 0 while recv_size < total_size: recv_data = phone.recv(1024) recv_size += len(recv_data) total_data += recv_data print(total_data.decode(‘gbk‘)) phone.close()
以上是关于python ==》 网络编程的主要内容,如果未能解决你的问题,请参考以下文章