网络
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()

最新文章

  1. 解决yum update失败
  2. cf #379div2
  3. centos 7 挂载大硬盘
  4. linq多表join与group
  5. osgearth使用prjected投影
  6. SQLSERVER 跨服 跨库
  7. 实现最小宽度的几种方法及CSS Expression[转]
  8. CF371 D Searching Rectangles
  9. IOS开发初体验
  10. 前端笔记之JavaScript(八)关于元素&amp;计算后的样式
  11. 在虚拟机上利用宿主机共享目录编译linux程序
  12. Mac定时关机、重启、休眠命令行
  13. ActiveReports 大数据分析报告:2018中国电影再次迎来黄金时代
  14. 解决Cell重用问题
  15. wpf expender 展开动画
  16. EUI List列表实现人物列表 (List的Item复用,Item获取)
  17. Linux下编译busybox时出现的问题
  18. 深入理解java的形参和实参
  19. MyFirstDay(附6篇python亲历面试题)
  20. python学习笔记七:浅拷贝深拷贝

热门文章

  1. Sublime text3配置C++环境
  2. [考试反思]1018csp-s模拟测试78(lrd day2) :规律
  3. NOIP模拟 37
  4. JS中的两种数据类型以及实现引用类型的深拷贝
  5. [ZJOI2013]K大数查询——整体二分
  6. Apache服务部署静态网站
  7. python内置模块collections介绍
  8. linux下安装opencv3.0
  9. Kubernetes概述
  10. thinkphp分页样式css代码