python_网络编程
网络
ISO(国际标准化组织)--->网络体系结构标准(OSI模型)
OSI:
网络信息传输比较复杂需要很多功能协同-->将功能分开,降低耦合度,让每个模块完成一定的功能-->将这些模块按照一定的顺 序进行组合,完成功能,条理清晰。
按照规定功能,顺序排序的体系结构就叫做OSI模型
OSI七层模型:
1、应用层:
1、提供用户服务,例如处理应用程序,文件传输,数据管理
2、表示层
1、做数据的转换和压缩,解压,加密等
3、会话层
1、决定了进程间的连接建立,选择使用什么样的传输协议
4、传输层
1、建立网络连接,提供合适的连接传输服务,提供流量控制
5、网络层
1、控制分组传输,进行路由选择,网络互联
6、链路层
1、提供链路交换,具体的数据收发
7、物理层
1、物理硬件,具体的传输条件和传输接口
四层模型:
1、应用层(包含应用层、表示层、会话层)
2、传输层
3、网络层
4、物理链路层(包含链路层、物理层)
五层模型(TCP/IP模型):
1、应用层(包含应用层、表示层、会话层)
2、传输层
3、网络层
4、链路层
5、物理层
协议:网络协议即在网络传输过程中,为保证通信正常而制定的都遵循的约定
1、应用层协议:TFTP、DNS、FTP、SMTP、HTTP
2、传输层协议:TCP 、UDP
3、网络层协议:IP、ARP、ICMP
4、物理链路层协议:IEEE
网络知识:
1、主机:
1、主机名称(计算机名,域名):socket.gethostname()查看主机名
2、本地主机表示方法:IP : ‘localhost’、127.0.0.1 表示本机通信地址
3、0.0.0.0:表示在局域网内的可用主机IP
4、192.168.43.105:表示本机在网络上的标识(linux终端上ifconfig查看IP地址)
5、>>> socket.gethostbyname('xdl-gj')
'127.0.1.1'
>>> socket.gethostbyname('localhost')
'127.0.0.1'
2、IP地址:
1、IPv4:点分十进制(127.0.0.1)或二进制(8*4 32为2进制表示)
2、IPv6:更多可用的IP
gethostbyaddr():查看主机的信息,返回一个元组里面有三个类列表分别代表主机名、主机别名、主机地址
>>> socket.gethostbyaddr('127.0.0.1')
('localhost', [], ['127.0.0.1'])
IP地址转换为二进制
1、inet_aton:将地址十进制转换为二进制
>>> socket.inet_aton('127.0.0.1')
b'\x7f\x00\x00\x01'
>>> socket.inet_ntoa(b'\x7f\x00\x00\x01')
'127.0.0.1'
2、inet_ntoa:将地址二进制转为十进制
3、inet_pton(socket.AF_INET.'127.0.0.1'):可以转换IPv4(AF_INET)和IPv6(AF_INET6)
4、inet_ntop()
>>> socket.inet_pton(socket.AF_INET,'127.0.0.1')
b'\x7f\x00\x00\x01'
>>> socket.inet_ntop(socket.AF_INET,b'\x7f\x00\x00\x01')
'127.0.0.1'
3、域名:互联网服务器IP的名字,方便使用
4、端口号:是地址的组成部分,用于在一个系统中区分应用层程序
1、取值范围:1~65535
2、1~255:是众所周知的端口
3、256~1023:系统进程占用
4、1024~49151:登记端口(用户可以使用)
5、19152~65535:私有端口或者动态端口
6、getservnyname():获取系统中某个网络程序的端口号
>>> socket.getservbyname('mysql')
3306
>>> socket.getservbyname('ssh')
22
>>> socket.getservbyname('http')
80
>>> socket.getservbyname('https')
443
5、子网掩码:与IP配合使用,用来确定当前的网段
6、字节序:
1、小端序:低序字节存在低地址位
2、大端序:高序字节存在低地址位
3、网络统一:网络字节序(等同于大端序)保证不同主机按照相同方式发送接收数据
传输层提供的通信类型:
1、面向连接的可靠服务(TCP)
1、TCP协议中规定,传输服务必须建立连接,传输结束必须断开连接,传输数据必须保证可靠
2、建立连接(三次握手)
1、客户端向服务端发送连接请求(发送一个试探的标志字符给服务器,)
2、服务端接受到请求后告知客户端可以连接
3、客户端再次告知服务端已经收到回复,下面开始发送具体消息
3、数据的可靠性:无重复、无丢失、无失序、无错误
4、断开连接(四次挥手)
1、主动方发送标志告知被动方要断开连接
2、被动方返回相应的标志信息告知主动方我已经接收到你的请求(之后会处理没有处理完的事情)
3、被动方再次发送标识信息表示已经准备就绪可以断开
4、主动方断开连接并告诉被动方
5、使用情况:
1、对传输质量要求较高,需求可靠的传输
2、传输的数据量较大(比如传文件),不需要频繁的连接断开
3、比如:QQ消息,邮件发送,文件上传,账户登录
2、面向无连接的不可靠服务(UDP)
1、不保证数据的可靠性
2、数据的发送都是由发起端决定的,不考虑接收端的情况
3、没有三次握手和四次挥手的过程
4、传输效率高
5、可靠性由应用层来实现
6、使用情况:
1、对实时性要求较高
2、网络情况不佳的时候
3、对数据的准确性没有严格要求
4、建立必要的非连接的情况(比如广播、组播)
5、比如:视频、直播
套接字:(网络间进行通信的方式的名称)
在linux中演化为一种文件类型socket
套接字的分类:
1、流式套接字:表示使传输层使用tcp协议提供面向连接的传输服务
2、数据报套接字:表示传输层使用udp协议提供面向无连接的传输服务
3、原始套接字:一般用作底层协议测试(用不到)
基于TCP协议的socket编程
服务端:
1、创建一个tcp流式套接字(socket)
socket(family=AF_INET,type=SOCK_STREAM,proto=0)
1、功能:创建一个套接字对象
2、参数:
1、family:协议族类型(AF_INET UNIX)
2、type:套接字类型
1、SOCK_STREAM:tcp流式套接字
2、SOCK_DGRAM:udp数据报套接字
3、SOCK_RAM:原始套接字
3、proto:子协议选项,一般为0表示没有子协议
3、返回值:返回套接字对象
2、绑定本机的IP和端口号(bind)
bind(address)
1、功能:绑定本机的IP和端口号
2、参数:是一个包含两个元素的元组,元组的第一个元素是主机名(字符串),第二个是使用的端口号(数字)
比如:('',8888)('localhost',8888)('127.0.0.1',8888)只能本机测试使用
('0.0.0.0',8888)(192.168.0.105)可以让其他主机访问
3、将套接字变为可监听套接字(listen)
linten(n)
1、功能:将套接字设置为监听套接字,并且设置一个连接等待队列
2、参数:是一个正整数(>=1),在linux系统下无效
4、套接字等待客户端请求(accept)
accept()
1、功能:阻塞等待客户端的连接
2、参数:无
3、返回值:
1、第一个返回值为 和客户端交互的新的套接字
2、第二个返回值为 连接进来的客户端的address
5、消息的收发(send/recv)
1、recv(buffer)
1、功能:接收网络消息
2、参数:正整数,表示一次接收从缓冲区中拿到的消息的字节数
3、返回值:返回接收到的消息(字节串)
4、当接收的网络缓冲区中没有内容时,则会被阻塞
5、当连接断开后,recv会结束阻塞返回一个空字符串
2、send(data)
1、功能:发送网络消息
2、参数:要发送的内容
3、返回值:实际发送的字节数
4、python3中要求send的内容必须为bytes格式
3、sendall(data)
1、功能:发送网络信息
2、参数:要发送的内容,要求为bytes格式
3、返回值:如果成功发送返回None,发送失败报异常
6、关闭套接字(close)
close()
1、功能:关闭一个套接字
#python网络套接字模块
from socket import * HOST = '127.0.0.1'
PORT = 8888
ADDR = (HOST,PORT)
BUFFERSIZE = 1024 #1、创建流式套接字
socketfd = socket(AF_INET,SOCK_STREAM,proto=0)
#2、绑定本机IP和端口号
socketfd.bind(ADDR)
#3、将套接字变为可监听套接字
socketfd.listen(5) print('wait for connect.....')
#4、套接字等待客户端请求
conn,addr = socketfd.accept() print('connect from ',addr) #5、消息的收发
while True:
data = conn.recv(BUFFERSIZE)
if not data:
break
print('接收到:',data.decode())
n = conn.send(b'Recv your message')
print('发送了%d字节的数据'%n)
#6、关闭套接字
conn.close()#表示和客户端断开连接
socketfd.close()#清除套接字,不能再使用
客户端
connect(address)
1、功能:向服务器发起连接请求
2、参数:address 是一个元组,即为要连接的服务器的地址
注意点:
1、客户端要和服务器端的套接字类型相同
2、客户端就是用创建的套接字和服务器交互
3、recv和send要与服务器配合,避免recv死阻塞
TCP循环服务不能满足多个客户端同时发送请求的情况,它不允许某个客户端单独长期占有服务器资源
from socket import * HOST = '127.0.0.1'
PORT = 8888
ADDR = (HOST,PORT)
BUFFERSIZE = 1024 connfd = socket(AF_INET,SOCK_STREAM)
#连接服务器
connfd.connect(ADDR) #和服务器进行通信
while True:
data = input('发送>>>')
if not data:
break
connfd.send(data.encode())
data = connfd.recv(1024)
print('客户端收到:',data.decode()) #关闭套接字
connfd.close()
tcp数据传输
1、recv会不断取出缓冲区中的内容,如果一次没有拿完,那么下次会继续收取没有拿完的消息
TCP粘包:
1、指的是发送方发送若干数据的时候,因为是数据流的传输方式,导致数据粘连在一起,接收方一次将多次发送过来的数据一起接收,产生接收数据的粘连
2、粘包是TCP传输特有的现象,因为TCP传输没有消息边界。如果是发送连续的内容比如文件等则粘包没有影响,如果是每次发送为单独需要处理的内容则需处理粘包
如何处理粘包
1、将消息格式化(每次固定发送一定长度的数据)
2、发送消息的同时发送一个消息长度标识
3、让消息的发送延迟,使接收端每次都能够有时间接收一个消息
UDP数据报套接字:面向无连接的不可靠的传输服务
服务器:
1、创建数据报套接字
2、绑定本地IP和端口
3、收发消息
recvfrom(BUFFERSIZE)
1、功能:在udp中接收消息
2、参数:buffersize表示一次最多可以接收多少字节的消息
3、返回值:
1、data:接收到的消息
2、addr:表示从哪个客户端接收的消息
4、每次只能接收一个数据包,如果数据包的大小超过recvfrom的设置大小则会出现数据丢失
sendto(data,addr)
1、功能:向一个网络终端发送消息
2、参数:
1、data 要发送的消息(bytes)
2、addr 要发送对象的地址
4、关闭套接字
from socket import *
import sys
from time import ctime #从命令行传入客户端IP和端口号
HOST = sys.argv[1]
PORT = int(sys.argv[2])
ADDR = (HOST,PORT)
BUFFERSIZE = 1024 #1、创建数据报套接字
sockdf = socket(AF_INET,SOCK_DGRAM) #2、绑定本地IP和端口
sockdf.bind(ADDR) #3、收发消息
while True:
data,addr = sockdf.recvfrom(BUFFERSIZE)
print('recv from',addr,':',data.decode())
sockdf.sendto(('在%s接收到消息'%ctime()).encode(),addr)
#4、关闭套接字
sockdf.close()
客户端:
1、创建套接字
2、消息收发
3、关闭套接字
import sys
sys.argv:
1、将命令行内容收集为一个列表,每个元素是命令行中的一项
2、命令行传入的内容均为str格式
3、命令行内容以空格分割,引号可以合成一个整体
from socket import *
import sys #从命令行传入服务器IP和端口号
HOST = sys.argv[1]
PORT = int(sys.argv[2])
ADDR = (HOST,PORT)
BUFFERSIZE = 1024 #1、创建数据报套接字
socketfd = socket(AF_INET,SOCK_DGRAM)
#2、消息收发
while True:
data = input('消息>>')
if not data:#输入为空时客户端退出
break
socketfd.sendto(data.encode(),ADDR)
data,addr = socketfd.recvfrom(BUFFERSIZE)
print('从服务器接收:',data.decode()) #3、关闭套接字
socketfd.close()
总结:tcp 和 udp的区别
1、tcp是有连接的,udp是无连接的
2、tcp有三次握手四次挥手,udp没有
3、tcp是以数据流传输数据,会粘包,udp是数据报的形式传输,没有粘包现象
4、tcp的连接需要消耗一定的资源,相比之下udp资源消耗少
5、tcp保证数据的可靠性,udp不保证
6、tcp需要listen、accept、connect,udp不需要
socket模块 和 套接字属性
套接字属性:
1、getpeername()
1、功能:用作服务器连接套接字,查看连接的客户端的地址
2、getsockname()
1、功能:获取套接字对应的绑定的IP和端口
3、s.type
1、功能:获取套接字的类型(SOCK_STREAM/SOCK_DGRAM/SOCK_RAM)
4、fileno()
1、功能:获取套接字的文件描述符编码
2、文件描述符:系统会给进程中的每一个IO操作对象匹配一个>=0的正整数作为标号,我们称之为该IO操作的文件描述符。一个进程中所有的IO的文件描述符不会重复
5、setsockopt(level,optname,value)
1、功能:设置套接字选项,可以增加或改变套接字的功能
2、参数:
1、level:要定义的选项类型,比如:SOL_SOCKET,IPPROTO_IP,IPPROTO_TCP
2、optname:每种类型都有具体的选项(level的子选项),根据具体的需求选项进行设置
比如:SOL_SOCKET--->SO_REUSEADDR
3、value:将选择的选项设置为 什么值
6、getsockopt(level,otpname)
1、功能:获取相应选项的值,即setsockopt()中的第三个参数value
2、参数:
1、level:要定义的选项类型,比如:SOL_SOCKET,IPPROTO_IP,IPPROTO_TCP
2、optname:每种类型都有具体的选项(level的子选项),根据具体的需求选项进行设置
3、返回值:获取到值
from socket import * HOST = '127.0.0.1'
PORT = 8888
ADDR = (HOST,PORT)
BUFFERSIZE = 1024 socketfd = socket(AF_INET,SOCK_STREAM,proto=0)
print(socketfd.fileno())#3,前面三个分别为os.stdin,os.stdout,os.stdnumber
print(socketfd.type)#SocketKind.SOCK_STREAM
print(socketfd.getsockname())#('0.0.0.0', 0)获取套接字对应的绑定的IP和端口,此时还没有绑定所以为0 #将端口号设置为立即重用
socketfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
print(socketfd.getsockopt(SOL_SOCKET,SO_REUSEADDR))#1,获取上面函数的第三个参数,如果没有设置则返回0 socketfd.bind(ADDR)
print(socketfd.getsockname())#('127.0.0.1', 9999) socketfd.listen(5) print('wait for connect.....')
conn,addr = socketfd.accept()
print('connect from ',conn.getpeername())#connect from ('127.0.0.1', 43236)
print(conn.fileno()) while True:
data = conn.recv(BUFFERSIZE)
if not data:
break
print('接收到:',data.decode())
n = conn.send(b'Recv your message')
print('发送了%d字节的数据'%n) conn.close()#表示和客户端断开连接
socketfd.close()#清除套接字,不能再使用
服务器:
1、硬件服务器:计算机主机 (IBM,HP,)
2、软件服务器:网络服务器,提供后端逻辑服务和请求处理的程序集合及架构,例如:web服务器等
1、服务器架构:c/s b/s 服务器的组织形式
2、服务器追求:更快速,更安全,并发量更大
socket服务器模型
1、循环服务器模型:循环处理客户端的请求,处理完一个继续处理下一个
1、缺点:不能同时处理多个请求,不允许某个客户端长期占用服务器资源
2、因为udp不需要进行连接,所以循环服务器模型更加适合udp通信
2、并发服务器模型:每有一个客户端就创建一个进程或线程处理客户端的具体请求事情,而主线程或主进程继续接收其他客户端的连接
1、多进程实现(fork)
1、创建套接字,绑定,监听
2、接收客户端连接请求,创建新的进程
3、主进程继续接收下一个客户端连接请求,子进程处理客户端事件
4、有客户端断开则关闭相应的子进程
from socket import *
import os
import signal #信号用来处理僵尸进程 def handler(c):
'''子进程处理客户端请求事件的函数'''
while True:
data = conn.recv(BUFFERSIZE)
if not data:
break
print('recv %s from %s'%(data.decode(),addr))
num = conn.send(b'hello')
print('send %s size to %s:'%(num,addr))
conn.close()
os._exit(0)#子进程结束 HOST = '127.0.0.1'
PORT = 8888
ADDR = (HOST,PORT)
BUFFERSIZE = 1024 # 1、创建套接字,绑定,监听
socketfd = socket(AF_INET,SOCK_STREAM)
socketfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#将端口号设置为立即重用
socketfd.bind(ADDR)
socketfd.listen(5)
#处理僵尸进程
signal.signal(signal.SIGCHLD,signal.SIG_IGN) # 2、接收客户端连接请求,创建新的进程
while True:
try:
conn,addr = socketfd.accept()
except KeyboardInterrupt:
print('服务器结束')
socketfd.close()#如果按ctrl+c则关闭套接字
os._exit(0)
except Exception:
continue#如果是其他异常则继续监听
print('接收到客户端连接>',conn.getpeername()) #创建子进程,子进程处理客户端事件
pid = os.fork()
if pid < 0:
print('create process failed')
continue
elif pid ==0:
socketfd.close()#把子进程中的流式套接字关闭
print('接收客户端请求事件')
handler(conn)
else:
conn.close()#把主进程中的连接客户端的套接字关闭
continue# 主进程继续接收下一个客户端连接请求,
2、多线程实现:(threading)
1、创建套接字,绑定,监听
2、接收客户端连接请求,创建新的线程
3、主进程继续接收下一个客户端连接请求,子线程处理客户端事件
4、有客户端断开则关闭相应的子线程
from socket import *
import threading
import os def handler(c):
'''子线程处理客户端请求事件的函数'''
while True:
data = conn.recv(BUFFERSIZE)
if not data:
break
print('recv %s from %s'%(data.decode(),addr))
num = conn.send(b'hello')
print('send %s size to %s:'%(num,addr))
conn.close() HOST = '127.0.0.1'
PORT = 8888
ADDR = (HOST,PORT)
BUFFERSIZE = 1024 # 1、创建套接字,绑定,监听
socketfd = socket(AF_INET,SOCK_STREAM)
socketfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#将端口号设置为立即重用
socketfd.bind(ADDR)
socketfd.listen(5) # 2、接收客户端连接请求,创建新的线程
while True:
try:
conn,addr = socketfd.accept()
except KeyboardInterrupt:
print('服务器结束')
socketfd.close()#如果按ctrl+c则关闭套接字
os._exit(0)
except Exception:
continue#如果是其他异常则继续监听
print('接收到客户端连接>',conn.getpeername()) #创建子进程,子进程处理客户端事件
t = threading.Thread(target = handler,args=(conn,))
t.setDaemon(True)#主线程结束,所有的子线程都结束
t.start()
#conn.close()#把主线程中的连接客户端的套接字关闭
socketserver模块(Python2中:SocketServer)
三部分:
多进程/多线程 TCP/UDP streamhandler/datagramhandler
ForkingMixIn TCPServer StreamRequestHandler
ThreadingMixIn UDPServer DatagramRequestHandler
ThreadingTCPServer = ThreadingMixIn + TCPServer
ThreadingUDPServer = ThreadingMixIn + UDPServer
ForkingTCPServer = ForkingMixIn + TCPServer
ForkingUDPServer = ForkingMixIn + UDPServer
创建步骤:
1、创建服务器类
2、创建处理类
3、使用创建的服务器类来创建服务器
4、运行服务器
#fork +tcp 并发
from socketserver import * #创建服务器类
class Server(ForkingMixIn,TCPServer):#等价于Server(ForkingTCPServer)
pass
#创建处理类
class Handler(StreamRequestHandler):
def handle(self):
'''当客户端连接进来时候会自动调用该函数处理客户端请求时间'''
addr = self.client_address
print('connect from ',addr)
while True:
#self.requset为tcp中为我们自动生成的和客户端交互的套接字 data = self.request.recv(1024)
if not data:
break
print('recv %s from %s'%(data.decode(),addr))
num = self.request.send(b'hello')
print('send %s size to %s:'%(num,addr)) #使用创建的服务器类来生产服务器
server = Server(('127.0.0.1',8888),Handler)
#运行服务器
server.serve_forever()
#fork + udp
from socketserver import * class Server(ForkingUDPServer):
pass class Handler(DatagramRequestHandler):
#udp无连接所以request的含义不同
def handle(self):
data = self.rfile.readline()
print('接收到了:',data.decode())
self.wfile.write(b'recevie message') server = Server(('127.0.0.1',8888),Handler)
server.serve_forever()
from socket import *
import os
import sys
import signal
import time FILE_PATH = '/home/xdl/python/ftp/file_path/'
class FtpServer(object):
def __init__(self,conn):
self.conn = conn def do_list(self):
'''服务器端确认请求是否可以执行'''
filelist = os.listdir(FILE_PATH)
if not filelist or filelist == None:
self.conn.send(b'FAIL')
else:
self.conn.send(b'OK')
time.sleep(0.1)
for file in filelist:
#print(file)
if file[0] != '.' and os.path.isfile(FILE_PATH+file):
self.conn.send(file.encode())
time.sleep(0.1)
self.conn.send(b'##')#告诉客户端我已经发送完毕
print('文件列表发送完毕')
return
def do_get(self,filename):
try:
with open(FILE_PATH+filename,'rb') as fd:
self.conn.send(b'OK')
time.sleep(0.1)
for line in fd:
self.conn.send(line)
except:
print('文件打开失败')
self.conn.send(b'FAIL')
time.sleep(0.1)
self.conn.send(b'##')
print('文件发送成功')
return
def do_put(self,filename):
try:
with open(FILE_PATH+filename,'w') as fd:
print(filename)
self.conn.send(b'ok')
time.sleep(0.1)
while True:
data = self.conn.recv(1024).decode()
if data == '##':
break
fd.write(data)
print('文件接收成功')
return
except OSError:
print('文件打开失败')#可能权限不够导致文件打开失败
self.conn.send(b'FAIL') def main():
if len(sys.argv) != 3:#保证输入的命令是三个部分
print('argv is error')
sys.exit(1)
HOST = sys.argv[1]
PORT = int(sys.argv[2])
ADDR = (HOST,PORT)
BUFFERSIZE = 1024 socketfd = socket(AF_INET,SOCK_STREAM)#创建流式套接字
socketfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#将端口设置为立即可用
socketfd.bind(ADDR)
socketfd.listen(5)
signal.signal(signal.SIGCHLD,signal.SIG_IGN)#处理僵尸进程 while True:
try:
conn,addr = socketfd.accept()
except KeyboardInterrupt:
print('服务器结束')#如果按ctrl+c则关闭套接字
socketfd.close()
os._exit(0)
except Exception:
continue #如果是其他异常则继续监听
print('connect from :',addr) pid = os.fork()#创建子进程
if pid < 0:
print('create process failed')
continue
elif pid ==0:
socketfd.close()#把子进程中的流式套接字关闭
ftp = FtpServer(conn) while True:#接收客户请求
data = conn.recv(1024).decode()
if data[0] =='L':
ftp.do_list()
elif data[0] =='G':
filename = data.split()[-1]
#print(filename)
ftp.do_get(filename)
elif data[0] =='P':
filename = data.split()[-1]
print(filename)
ftp.do_put(filename)
elif data[0] == 'Q':
print('客户端退出')
sys.exit(0)
else:
conn.close()#把主进程中的连接客户端的套接字关闭
continue# 主进程继续接收下一个客户端连接请求 if __name__ == '__main__':
main()
ftp_server
from socket import *
import sys
import time FILE_PATH = '/home/xdl/python/ftp/down_file_path/'
class FtpClient(object):
def __init__(self,connfd):
self.connfd = connfd def do_list(self):
self.connfd.send(b'L')#发送请求类型
#接收服务器端的确认消息
data = self.connfd.recv(1024).decode()
if data == 'OK':
while True:
data = self.connfd.recv(1024).decode()
if data == '##':
break
print(data)
print('文件列表展示完毕')
return
else:
print('文件列表请求失败')
return def do_get(self,filename):
self.connfd.send(('G '+filename).encode())
data = self.connfd.recv(1024).decode()
if data == 'OK':
with open(FILE_PATH+filename,'w') as fd:
while True:
data = self.connfd.recv(1024).decode()
if data == '##':
break
fd.write(data)
print('%s下载完成'%filename)
return
else:
print('下载文件失败')
return def do_put(self,filename):
try:#如果文件打开失败,就不要向服务器发送数据了
with open(FILE_PATH+filename,'rb') as fd:
self.connfd.send(('P '+filename).encode())
data = self.connfd.recv(1024).decode() if data == 'ok':
for line in fd:
self.connfd.send(line)
time.sleep(0.1)
self.connfd.send(b'##')
print("%s文件上传成功"%filename)
else:
print('文件上传失败')
return
except OSError:
print('文件打开失败')
return def do_quit(self):
self.connfd.send(b'Q') def main():
if len(sys.argv) != 3:#保证输入的命令是三个部分
print('argv is error')
sys.exit(1)
HOST = sys.argv[1]
PORT = int(sys.argv[2])
ADDR = (HOST,PORT)
BUFFERSIZE = 1024 connfd = socket()
connfd.connect(ADDR) ftp = FtpClient(connfd)
while True:
print('***********命令选项***********')
print('***********L(展示列表)***********')
print('***********G(下载)***********')
print('***********P(上传)***********')
print('***********Q(退出)***********')
data = input('输入命令>>')
if data == 'L':
ftp.do_list()
elif data[0] == 'G':
filename = data.split()[-1]
ftp.do_get(filename)
elif data[0] == 'P':
filename = data.split()[-1]
ftp.do_put(filename)
elif data == 'Q':
ftp.do_quit()
connfd.close()
sys.exit(0)
else:
print('请输入正确的命令!!!')
continue #关闭套接字
connfd.close()
if __name__ == '__main__':
main()
ftp_client
IO 服务器模型
IO的分类:
1、阻塞IO(效率最低)
2、非阻塞IO(效率相对较高)
1、在遇到原本阻塞的条件时不再阻塞,去执行其他内容,但是往往需要不断轮询阻塞条件是否可以执行
3、IO多路复用
1、同时监控多个IO事件,当IO哪个事件就绪就执行哪个IO事件,形成一种并发的效果
2、select 模块
1、select(win,linux,unix)多路复用
1、select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist)
2、功能:通过select方法监控IO事件
3、参数:
1、rlist:列表,存放要监控的IO事件,将要处理的IO事件(rlist -- wait until ready for reading)
2、wlist:列表,存放要监控的IO事件,要主动处理的IO事件(wlist -- wait until ready for writing)
3、xlist:列表,存放要监控的IO事件,希望发生异常通知你处理的IO事件(xlist -- wait for an ``exceptional condition'')
4、返回值:当select监控的IO事件中有一个或者多个可以处理的时候结束阻塞,进行返回
1、r:列表,参数rlist中如果有可以处理的IO放在这个列表里面
2、w:列表,参数wlist中如果有可以处理的IO放在这个列表里面
3、x:列表,参数xlist中如果有可以处理的IO放在这个列表里面
#基于select的IO多路复用监听服务器
from socket import *
import select #s套接字作为一个IO事件
s = socket()
s.bind(('127.0.0.1',8889))
s.listen(5) rlist = [s]
wlist = []
xlist = [s] while True:
#监听三个关注的列表中的IO事件
rs,ws,es = select.select(rlist,wlist,xlist)
print('有IO事件发生了')
#通过for循环遍历每个返回列表,去处理IO
for r in rs:
if r is s:
c,addr = s.accept()
print("connect from ",addr)
#将新的IO事件加入到监控列表
rlist.append(c)
else:
data = r.recv(1024).decode()
if not data:
print('客户端退出')
rlist.remove(r)
r.close()
print('收到客户端信息',data)
r.send('收到了你的消息'.encode())
for w in ws:
pass
for e in es:
pass
s.close()
#基于select的IO多路复用监听服务器
from socket import *
import select
import sys #s套接字作为一个IO事件
s = socket()
s.bind(('127.0.0.1',8889))
s.listen(5) rlist = [s]
wlist = []
xlist = [s] while True:
#监听三个关注的列表中的IO事件
rs,ws,es = select.select(rlist,wlist,xlist)
print('有IO事件发生了')
#通过for循环遍历每个返回列表,去处理IO
for r in rs:
if r is s:
c,addr = s.accept()
print("connect from ",addr)
#将新的IO事件加入到监控列表
rlist.append(c)
xlist.append(c)#将与客户端交互的套接字加入到异常列表中
else:
data = r.recv(1024).decode()
if not data:
print('客户端退出')
rlist.remove(r)
xlist.remove(r)
r.close()
else:
wlist.append(r)
print('收到客户端信息',data) for w in ws:
r.send('收到了你的消息'.encode())
wlist.remove(w)
for e in es:
if e is s:
s.close()
sys.exit(0)#如果发生异常则退出
else:
e.close()
rlist.remove(e)
xlist.remove(e)
2、poll(linux,unix)多路复用
1、p = select.poll():通过创建poll对象
2、p.register(s):加入你关注的IO事件 相当于select中的三个参数
3、p.poll(): 相当于select函数功能
1、功能:监控哪个IO事件
2、参数:无
3、返回值:[(1,event),(2,event),(3,event)....]
1、每一个就绪的IO事件都会在返回值中给出一个对应的元组
2、元组中,第一个元素为就绪的IO的fileno,第二个元素为具体的就绪事件
4、p.unregister(s):将IO事件移除监控范围 相当于select中rlist.rmove()
5、往往需要写一个字典,让IO对象和fileno对应起来
6、在poll 和 epoll中的事件分类:
1、POLLIN (1):rlist
2、POLLOUT(4):wlist
3、POLLERR(8):xlist
4、POLLHUP(16):断开连接
5、POLLPRI(2):紧急处理
6、POLLNVAL(32):无效数据
#基于poll的IO多路复用监听服务器
from socket import *
import select #s套接字作为一个IO事件
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('127.0.0.1',8888))
s.listen(5)
#以每个IO对象的fileno为键,IO对象为值
fdmap = {s.fileno():s} #创建poll对象
p = select.poll()
#添加关注的IO
p.register(s)#此处添加IO对象的fileno也可以:等价于p.register(s.fileno) while True:
#监控关注的IO
events = p.poll()
#print(events)
for fd,event in events:
if fd == s.fileno():
c,addr = fdmap[fd].accept()
print('Connect from ',addr)
#将客户端套接字添加关注并添加到字典中
p.register(c)
fdmap[c.fileno()] = c
elif event & select.POLLIN :#得到True表示是当前事件类型
data = fdmap[fd].recv(1024).decode()
#如果客户端退出则不再关注,并从字典中移除
if not data:
p.unregister(fd)
del fdmap[fd]
else:
print(data)
fdmap[fd].send('收到你的消息'.encode())
3、epoll(linux,unix)多路复用
3、多路复用的特点:
1、可以同时监听多种IO事件
2、当任意IO事件发生时会处理
3、在处理每个事件时不能死循环(长期占有服务器)
4、IO多路复用,是基于IO的处理,不是多线程或者多进程
4、事件驱动IO
5、异步IO
协程服务器模型
1、协程(微线程,纤程),本质是单线程
2、定义:是一种用户态的轻量级线程
3、特点:
1、轻量级,创建消耗资源非常少
2、不涉及内核
4、优点:
1、无需上下文切换的开销
2、无需同步互斥操作
3、有较高的并发性(IO并发)
4、创建消耗资源少
5、缺点:
1、无法利用计算机的多核资源
2、遇到死循环等待阻塞状态会影响整个程序的运行
6、greenlet模块
1、安装:sudo pip3 install greenlet
from greenlet import greenlet #协程函数
def test1():
print(111111)
gr2.switch()
print(222222)
gr2.switch() def test2():
print(333333)
gr1.switch()
print(444444) #将函数变为协程 gr1 = greenlet(test1)
gr2 = greenlet(test2) gr1.switch()
7、gevent模块
2、安装:sudo pip3 install gevent
import gevent #协程函数
def foo(a,b):
print("Running in foo ",a,b)
gevent.sleep(3)#模拟遇到IO阻塞的情况
print('switch to foo again') def bar():
print('Running in bar')
gevent.sleep(2)#模拟遇到IO阻塞的情况
print('switch to bar again') #注册为协程事件
f = gevent.spawn(foo,1,2)
b = gevent.spawn(bar) gevent.joinall([f,b])
import gevent
from gevent import monkey
#monkey脚本插件使用要在导入socket之前
monkey.patch_all()#修改socket模块的阻塞部分,
from socket import * #协程事件
def handler(c,addr):
print('Connect from ',addr)
while True:
data = c.recv(1024).decode()
if not data:
break
print('Recv messge:',data)
c.send(b'Recv your msg')
c.close() HOST = '127.0.0.1'
PORT = 8888
ADDR = (HOST,PORT) s = socket()
s.bind(ADDR)
s.listen(5)
while True:
c,addr = s.accept()
gevent.spawn(handler,c,addr)
gevent_server
进程 + 协程 方案完成高并发
1、什么是协程(单线程,优点,缺点,高并发量的IO操作)
2、协程如何工作的(在线程栈中进行跳转,是应用层的技术,遇到IO阻塞进行协程选择)
3、写过什么协程代码:协程流式套接字服务器
非阻塞IO 和 超时检测
1、定义:非阻塞IO就是在遇到原本阻塞的IO情形时,不进行阻塞等待,如果满足执行条件即执行,不满足条件就不执行
2、socketfd.setblocking(False)
1、功能:设置一个套接字的阻塞状态
2、参数:
1、默认为True表示套接字为阻塞套接字
2、如果设置为False则表示非阻塞,此时套接字使用阻塞函数时,如果无法正常执行则抛出blocking异常
3、监听套接字设置为非阻塞 accept不再阻塞
4、连接套接字设置为非阻塞 recv不再阻塞
3、定义:在阻塞状态下,设置程序超时时间,当到达时间后进程不再阻塞等待
1、比如:mulitprocessing/threading----->join
2、比如:Queue --->get put
3、比如:select --->select
4、比如:Event ---->wait
4、socketfd.settimeout(5)
1、功能:设置套接字的超时检测时间
2、参数:超时时间
import time
from socket import * HOST = '127.0.0.1'
PORT = 8888
ADDR = (HOST,PORT)
BUFFERSIZE = 1024 socketfd = socket(AF_INET,SOCK_STREAM,proto=0)
socketfd.bind(ADDR)
socketfd.listen(5)
#将socket 变为非阻塞
#socketfd.setblocking(False)
#设置等待超时
socketfd.settimeout(5)
while True: #time.sleep(8)
#套接字等待客户端请求
print('wait for connect.....')
try:
conn,addr = socketfd.accept()
print('connect from ',addr)
conn.setblocking(False)
while True:
data = conn.recv(BUFFERSIZE)
if not data:
break
print('接收到:',data.decode())
n = conn.send(b'Recv your message')
print('发送了%d字节的数据'%n)
conn.close()#表示和客户端断开连接
except Exception:
pass
socketfd.close()#清除套接字,不能再使用
网络广播:(是一端发送,多端接收的模式)
1、使用udp数据报套接字
2、广播地址:192.168.43.255(IP地址为最后改为255)
3、设置套接字为可以发送接收广播的套接字:s.setsockopt(SOL_SOCKET,SO_BROADCAST,1)
4、在网络中如果存在大量的广播会产生广播风暴,占用大量带宽
from socket import * HOST = ''
PORT = 8888
#使用数据报套接字
s = socket(AF_INET,SOCK_DGRAM) #设置套接字为允许广播
s.setsockopt(SOL_SOCKET,SO_BROADCAST,1) s.bind((HOST,PORT)) while True:
try:
msg,addr = s.recvfrom(1024)
print('get msg from',addr)
print('广播通知:',msg.decode())
s.sendto('收到广播'.encode(),addr)
except EXception as e:
print(e)
s.close()
from socket import *
import time #设置发送的广播地址,<broadcast>
#dest = ('127.0.0.255',8888)
dest = ('<broadcast>',8888)#等价于dest = ('127.0.0.255',8888) s = socket(AF_INET,SOCK_DGRAM) s.setsockopt(SOL_SOCKET,SO_BROADCAST,1) while True:
time.sleep(2)
s.sendto('喜迎两会'.encode(),dest)
data, addr = s.recvfrom(1024)
print(data.decode())
s.close()
本地套接字:
1、作用:用作本地两个进程间的通信
2、传输方式:字节流的方式进行数据传输
3、socket(AF_UNIX,SOCK_STREAM)
1、功能:创建本地套接字
4、通信介质:通过套接字文件实现通信
from socket import *
import os #设置通信的套接字文件
address = './sockfile' try:
os.unlink(address)#删除这个目录下的这个文件
except OSError:
if os.path.exists(address):#判断文件是否删除成功
raise s = socket(AF_UNIX,SOCK_STREAM) s.bind(address)#绑定本地套接字文件
s.listen(5) while True:
conn,addr = s.accept()
print('connect from',addr)#addr是一个空的字符串 while True:
data = conn.recv(1024).decode()
if not data:
break
print('recv:',data)
conn.send(b'recv your msg')
conn.close()
s.close()
from socket import * address = './sockfile' s = socket(AF_UNIX,SOCK_STREAM) try:
s.connect(address)
except error as e:
print(e) try:
msg = b'This is a unix msg'
s.send(msg)
data = s.recv(1024).decode()
print('recv',data)
except error as e:
print(e)
finally:
s.close()
http(超文本传输协议)
1、应用层协议----->传输层使用tcp协议
2、用途:
1、网站中网页的传输、数据的传输
2、也可以用作通过编程传输数据
3、特点:
1、支持典型的客户端服务器模式
2、灵活简单
3、几乎支持所有的数据格式(视频,音频,等)
4、是无状态的
5、http1.0 无连接 http1.1 持续连接
请求:
1、格式:
1、请求行:发送什么类型的请求
1、请求方法(
1、get:获取网址(URL)标识的网络资源。
URL:统一资源定位符,指的是网络资源在网络中的位置,网址即为一类的url
2、post:提交一定的附加数据,用以获取相应的返回
3、head:获取URL所标识的响应消息报头
4、put:获取服务器的资源
5、delete:删除一个服务器资源
6、trace:用于测试和诊断
7、connect:保留的请求方法
8、options:请求获取服务器性能,查询相关资源信息
2、请求格式(html,jpg)
3、协议版本(http1.0,http1.1)
2、请求头:对发送请求信息的描述
3、空行:
4、请求体:具体的请求参数或请求内容
1、get请求即为get请求的参数
2、post请求即为post请求提交的内容
网络响应:
1、响应行:反馈响应情况
1、协议版本
2、响应码
1、1XX:提示信息,表示请求已经接收,正在处理
2、2XX:访问成功
1、200:访问成功
3、3XX:重定向,完成任务需要其他操作
1、301:永久重定向
2、302:临时重定向
4、4XX:客户端错误
1、400:客户端请求有语法错误
2、401:访问没有授权
3、403:服务器已收到请求,但是拒绝执行
4、404:请求的服务器资源不存在
5、5XX:服务器端错误
1、500:服务器发生未知错误
2、503:服务器暂时不能执行,请稍后访问再试
3、响应码对应信息
2、响应头:对响应内容的描述
3、空行
4、响应体:根据请求返回给客户端的内容
总结:
1、什么是HTTP协议
2、HTTP协议请求和响应的格式
3、请求方法都有哪些
4、常见的响应吗代表什么
5、get和post的区别
6、http协议的特点
python http server
在python3中为:http.server模块
在python2中为:BaseHTTPServer模块
HTTPServer服务器用来接收客户端的HTTP请求
#导入HTTPServer类,兼容python2和python3
try:
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer#python2
except ImportError:
from http.server import BaseHTTPRequestHandler,HTTPServer#python3 class RequestHandler(BaseHTTPRequestHandler):
'''具体的请求处理类'''
def do_GET(self):
print('do method get')
fd = open('index.html','rb')
content = fd.read()
fd.close()
#设置响应吗
self.send_response(200)
#设置响应头
self.send_header('Content-type','text/html')
#响应设置完毕
self.end_headers()
#发送响应体
self.wfile.write(content)
return
def do_POST(self):
pass #搭建启动服务器
address = ('127.0.0.1',8888)
server = HTTPServer(address,RequestHandler)
server.serve_forever()
python_网络编程的更多相关文章
- Python_网络编程_socket()
什么是 Socket? Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯. 详细资 ...
- Python_网络编程udp-飞秋自动攻击
# 模拟一个接收数据import socketimport time def auto_hack(udp_socket, recv_msg, revc_ip, revc_port=2425): # 发 ...
- python_网络编程_基础
基本的架构有C/S架构 和B/S架构 B/S架构优于C/S架构? 因为统一入口 , 都是从浏览器开始访问 两台电脑实现通信, 需要网卡, 网卡上有全球唯一的mac地址 ARP协议 #通过ip地址就能找 ...
- python_网络编程socketserver模块实现多用户通信
服务端: import socketserver class MyServer(socketserver.BaseRequestHandler): def handle(self): #在这个函数里面 ...
- python_网络编程hmac模块验证客户端的合法性
hmac模块: 比较两个函数的密钥是否一致: import hmac import os msg = os.urandom(32) #生成32位随机字节码 def wdc(): key = b'wdc ...
- python_网络编程struct模块解决黏包问题
为什么会出现黏包现象: 首先只有在TCP协议中才会出现黏包现象,是因为TCP协议是面向流的协议,在发送的数据传输的过程中还有缓存机制来避免数据丢失,因此,在连续发送小数据的时候,以及接收大小不符的时候 ...
- python_网络编程socket(UDP)
服务端: import socket sk = socket.socket(type=socket.SOCK_DGRAM) #创建基于UDP协议的socket对象 sk.bind(('127.0.0. ...
- python_网络编程socket(TCP)
服务端: import socket sk = socket.socket() #创建对象 sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) ...
- 猫哥网络编程系列:HTTP PEM 万能调试法
注:本文内容较长且细节较多,建议先收藏再阅读,原文将在 Github 上维护与更新. 在 HTTP 接口开发与调试过程中,我们经常遇到以下类似的问题: 为什么本地环境接口可以调用成功,但放到手机上就跑 ...
随机推荐
- Redis bin目录和info命令
1.Redis bin目录和info命令 概述: bin目录是说我们的redis的安装目录中的bin目录,里面存放着一些可执行文件 info命令会列出当前连接的Redis实例的所有指标信息 下面我就对 ...
- 学习笔记47_关于Session局限性问题,Memcache
三大问题: 1.Session性能问题 2.不能稳定输出.考虑使用进程外Session 3.组成集群,登录数据进行共享 (比如说像百度,百度网盘,百度文库等是使用不同的服务机器的,怎样避免使用的时候不 ...
- 原生JS实现call,apply,bind函数
1. 前言 使用原生JS实现call和apply函数,充分了解其内部原理.call和apply都是为了解决改变this的指向.作用都相同,只是传参的方式不同.除了第一个参数外,call可以接受一个参数 ...
- 问题 L: 「Usaco2005 Feb」竞选划区O(∩_∩)O 纯属的暴力
题目描述 农场被划分为5x5的格子,每个格子中都有一头奶牛,并且只有荷斯坦(标记为H)和杰尔西(标记为J)两个品种. 如果一头奶牛在另一头上下左右四个格子中的任一格里,我们说它们相连. 奶牛要大选了. ...
- Hibernate一对多、多对一的关系表达
一.关系表达: 1.一对多.多对一表的关系: 学生表: 班级表: 在学生表中,学生的学号是主键.在班级表中,班级号是主键,因此,学生表的外键是classno.因此,班级对应学生是一对多,学生对应班级是 ...
- 在linux上使用ssh登录服务器,Linux权限
本文是作者原创,版权归作者所有.若要转载,请注明出处 ssh为Secure Shell(安全外壳协议)的缩写. 很多ftp.pop和telnet在本质上都是不安全的. 我们使用的Xshell6就是基于 ...
- [转载]2.6 UiPath循环嵌套的介绍和使用
一.循环嵌套的介绍 一个循环体内又包含另一个完整的循环结构,就称之为循环嵌套.内嵌的循环中还可以嵌套循环,这就是多层循环,也叫做多重循环. 二.在UiPath中结合使用循环嵌套生成99乘法表 1.打开 ...
- python脚本 环境准备
现在的公司用 Python 做 Web 开发,入职到现在为止(三个月),算是入门了 Python Web 开发 但是 Python 本身是脚本语言,我还从来没有体会到脚本语言能给日常工作带来的便利 就 ...
- shell编程-基础
1.linux 下 Bash 程序开 1.1 怎样写 shell 脚本 1.使用编辑工具编辑shell 脚本,例如 vim,脚本名字一般用.sh 为后缀,不用.sh 为后缀 时编辑的内容为全黑,不会有 ...
- Vue躬行记(9)——Vuex
Vuex是一个专为Vue.js设计的状态管理库,适用于多组件共享状态的场景.Vuex能集中式的存储和维护所有组件的状态,并提供相关规则保证状态的独立性.正确性和可预测性,这不仅让调试变得可追踪,还让代 ...