网络体系结构就是使用这些用不一样媒介链接起来的不一样设备和网络系统在不一样的应用环境下实现互操做性,并知足各类业务需求的一种粘合剂。网络体系结构解决互质性问题彩是分层方法。python
1.网络(OSI)的7层模型:shell
应用层--->为应用程序提供网络通讯服务编程
表示层--->数据表示windows
会话层--->主机间通讯(两个应用进程间)服务器
传输层--->端到端的链接,隔离网络的上下层协议,使得网络应用与下层协议无关网络
网络层--->寻找最优路径,转发数据包数据结构
数据链路层--->无差错的链路链接并发
物理层--->二进制传输异步
2.端口socket
是一种抽象的软件结构,包括一些数据结构和I/O缓冲区。与协议有关。
3.套接字存在于通讯区域中。
通讯区域也叫地址族,它是一个抽象的概念,主要用于将经过套接字通讯的进程的共有特性综合在一块儿。
为保证数据的正确性,在网络协议中须要制定网络字节顺序,采用统一的网络字节顺序。
网络通讯三要素:
IP地址:用于表示主机(IP地址 = 网络ID+主机ID)
端口号:用于标识进程的逻辑端口
传输协议:TCP UDP
网络通讯过程就是一个不断封装和解析的过程
Socket是链接应用程序与网络驱动程序的桥梁,Socket在应用程序中建立,经过绑定操做与驱动程序创建关系。
套接字是为特定网络协议(例如TCP/IP,ICMP/IP,UDP/IP等)套件对上的网络应用程序提供者提供当前可移植标准的对象。它们容许程序接受并进行链接,如发送和接受数据。为了创建通讯通道,网络通讯的每一个端点拥有一个套接字对象极为重要。
套接字为BSD UNIX系统核心的一部分,并且他们也被许多其余相似UNIX的操做系统包括Linux所采纳。许多非BSD UNIX系统(如ms-dos,windows,os/2,mac os及大部分主机环境)都以库形式提供对套接字的支持。
三种最流行的套接字类型是:stream,datagram和raw。stream和datagram套接字能够直接与TCP协议进行接口,而raw套接字则接口到IP协议。但套接字并不限于TCP/IP。
套接字模块是一个很是简单的基于对象的接口,它提供对低层BSD套接字样式网络的访问。使用该模块能够实现客户机和服务器套接字。要在python 中创建具备TCP和流套接字的简单服务器,须要使用socket模块。利用该模块包含的函数和类定义,可生成经过网络通讯的程序。
SOCKET内建方法
函数 | 描述 |
服务器端套接字函数 | |
s.bind() | 绑定地址(主机,端口号对)到套接字 s.bind(address) 将套接字绑定到地址。 address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。 |
s.listen() | 开始TCP 监听 开始监听传入链接。添加参数是指定在拒绝链接以前,能够挂起的最大链接数量。 若是参数等于5,表示内核已经接到了链接请求,但服务器尚未调用accept进行处理的链接个数最大为5 |
s.accept() | 被动接受TCP 客户的链接,(阻塞式)等待链接的到来 接受链接并返回(conn,address),其中conn是新的套接字对象,能够用来接收和发送数据。address是链接客户端的地址。 接收TCP 客户的链接(阻塞式)等待链接的到来 |
客户端套接字函数 | |
s.connect() | 主动初始化TCP 服务器链接,参数为地址(address) 链接到address处的套接字。通常,address的格式为元组(hostname,port),若是链接出错,返回socket.error错误。 |
s.connect_ex() | connect()函数的扩展版本,出错时返回出错码,而不是抛异常 同上,只不过会有返回值,链接成功时返回 0 ,链接失败时候返回编码,例如:10061 |
公共用途的套接字函数 | |
s.recv() | 接收TCP 数据 sk.recv(bufsize[,flag]) 接受套接字的数据。数据以字符串形式返回,bufsize指定最多能够接收的数量。flag提供有关消息的其余信息,一般能够忽略。 |
s.send() | 发送TCP 数据 将string中的数据发送到链接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容所有发送。 |
s.sendall() | 完整发送TCP 数据 将string中的数据发送到链接的套接字,但在返回以前会尝试发送全部数据。成功返回None,失败则抛出异常。 内部经过递归调用send,将全部内容发送出去。 |
s.recvfrom() | 接收UDP 数据 与recv()相似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 |
s.sendto() | 发送UDP 数据 将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。 |
s.getpeername() | 链接到当前套接字的远端的地址 返回链接套接字的远程地址。返回值一般是元组(ipaddr,port)。 |
s.getsockname() | 当前套接字的地址 返回套接字本身的地址。一般是一个元组(ipaddr,port) |
s.getsockopt() | 返回指定套接字的参数 |
s.setsockopt() | 设置指定套接字的参数 |
s.close() | 关闭套接字 |
面向模块的套接字函数 | |
s.setblocking() | 设置套接字的阻塞与非阻塞模式 是否阻塞(默认True),若是设置False,那么accept和recv时一旦无数据,则报错。 |
s.settimeout()a | 设置阻塞套接字操做的超时时间 设置套接字操做的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。通常,超时期应该在刚建立套接字时设置,由于它们可能用于链接的操做(如 client 链接最多等待5s ) |
s.gettimeout()a | 获得阻塞套接字操做的超时时间 |
面向文件的套接字的函数 | |
s.fileno() | 套接字的文件描述符 |
s.makefile() | 建立一个与该套接字关连的文件 |
创建服务器链接须要六个步骤:
1.建立socket对象。调用socket构造函数。
socket=socket.socket(familly,type)
family的值能够是AF_UNIX(Unix域,用于同一台机器上的进程间通信),也能够是AF_INET(对于IPV4协议的TCP和 UDP),至于type参数,SOCK_STREAM(流套接字)或者 SOCK_DGRAM(数据报文套接字),SOCK_RAW(raw套接字)。
2.则是将socket绑定(指派)到指定地址上,socket.bind(address)
address必须是一个双元素元组,((host,port)),主机名或者ip地址+端口号。若是端口号正在被使用或者保留,或者主机名或ip地址错误,则引起socke.error异常。
3.绑定后,必须准备好套接字,以便接受链接请求。
socket.listen(backlog)
backlog指定了最多链接数,至少为1,接到链接请求后,这些请求必须排队,若是队列已满,则拒绝请求。
4.服务器套接字经过socket的accept方法等待客户请求一个链接:
connection,address=socket.accept()
调用accept方法时,socket会进入'waiting'(或阻塞)状态。客户请求链接时,方法创建链接并返回服务器。accept方法返回 一个含有俩个元素的元组,形如(connection,address)。第一个元素(connection)是新的socket对象,服务器经过它与客 户通讯;第二个元素(address)是客户的internet地址。
5. 处理阶段,服务器和客户经过send和recv方法通讯(传输数据)。服务器调用send,并采用字符串形式向客户发送信息。send方法 返回已发送的字符个数。服务器使用recv方法从客户接受信息。调用recv时,必须指定一个整数来控制本次调用所接受的最大数据量。recv方法在接受 数据时会进入'blocket'状态,最后返回一个字符串,用它来表示收到的数据。若是发送的量超过recv所容许,数据会被截断。多余的数据将缓冲于接 受端。之后调用recv时,多余的数据会从缓冲区删除。
6. 传输结束,服务器调用socket的close方法以关闭链接。
创建一个简单客户链接则须要4个步骤。
第1步,建立一个socket以链接服务器 socket=socket.socket(family,type)
第2步,使用socket的connect方法链接服务器 socket.connect((host,port))
第3步,客户和服务器经过send和recv方法通讯。
第4步,结束后,客户经过调用socket的close方法来关闭链接。
如下案例都是以TCP方式链接
import socket sk = socket.socket() address = ('127.0.0.1',8000) sk.bind(address) sk.listen(2) print("......") while True: conn, addr = sk.accept() print(addr) while True: try: date = conn.recv(1024) except Exception: break if not date:break print(str(date, "utf8")) inp = input(">>>>:") conn.send(bytes(inp,"utf8")) # conn, addr = sk.accept() # while True: # date = conn.recv(1024) # if not date: # conn, addr = sk.accept() # continue # print(str(date, "utf8")) # inp = input(">>>>:") # conn.send(bytes(inp,"utf8")) # conn.close() conn.close()
import socket sk = socket.socket() address = ("127.0.0.1",8000) sk.connect(address) while True: inp = input(">>>>:") if inp == "q": break sk.send(bytes(inp, "utf8")) date = sk.recv(1024) print(str(date,"utf8")) sk.close()
简单的模拟qq对话(socket模块)
import socket,subprocess sk = socket.socket() address = ('0.0.0.0',8000) sk.bind(address) sk.listen(2) print("......") while True: conn, addr = sk.accept() print(addr) while True: try: date = conn.recv(1024) except Exception: break if not date:break print(str(date, "utf8")) obj = subprocess.Popen(str(date, "utf8"),shell=True,stdout=subprocess.PIPE) cmd_result = obj.stdout.read() result_len = str(len(cmd_result)) print(result_len) conn.send(bytes(result_len,"utf8")) conn.send(cmd_result) conn.close()
import socket sk = socket.socket() address = ("127.0.0.1",8000) sk.connect(address) while True: inp = input(">>>>:") if inp == "q": break sk.send(bytes(inp, "utf8")) result_len = int(str(sk.recv(1024),"utf8")) print(result_len) date = bytes() while len(date) != result_len: da = sk.recv(1024) date+=da print(str(date,"gbk")) sk.close()
简单的FTP上传图片
import subprocess import socket import os sk=socket.socket() print(sk) address=('127.0.0.1',8000) sk.bind(address) sk.listen(3) print('waiting......') BASE_DIR=os.path.dirname(os.path.abspath(__file__)) while 1: conn, addr = sk.accept() while 1: data=conn.recv(1024) cmd,filename,filesize=str(data,'utf8').split('|') path=os.path.join(BASE_DIR,'tupian',filename) filesize=int(filesize) f=open(path,'ab') has_receive=0 while has_receive!=filesize: data=conn.recv(1024) f.write(data) has_receive+=len(data) f.close()
import socket import os sk=socket.socket() address=('127.0.0.1',8000) sk.connect(address) BASE_DIR=os.path.dirname(os.path.abspath(__file__)) while True: inp=input('>>>').strip()# post|11.png cmd,path=inp.split('|') path=os.path.join(BASE_DIR,path) filename=os.path.basename(path) file_size=os.stat(path).st_size file_info='post|%s|%s'%(filename,file_size) sk.sendall(bytes(file_info,'utf8')) f=open(path,'rb') has_sent=0 while has_sent!=file_size: data=f.read(1024) sk.sendall(data) has_sent+=len(data) f.close() print('上传成功')
socketserver是标准库中一个高级别的模块,用于简化网络客户与服务器的实现。模块中,已经实现了一些可供使用的类。
SocketServer模块中的类主要有如下几个:
一、BaseServer 包含服务器的核心功能与混合类(mix-in)的钩子功能。这个类主要用于派生,不要直接生成这个类的类对象,能够考虑使用TCPServer和UDPServer类。
二、TCPServer 基本的网络同步TCP服务器
三、UDPServer 基本的网络同步UDP服务器
四、ForkingMixIn 实现了核心的进程化功能,用于与服务器类进行混合(mix-in),以提供一些异步特性。不要直接生成这个类的对象。
五、ThreadingMixIn 实现了核心的线程化功能,用于与服务器类进行混合(mix-in),以提供一些异步特性。不要直接生成这个类的对象。
六、ForkingTCPServer ForkingMixIn与TCPServer的组合
七、ForkingUDPServer ForkingMixIn与UDPServer的组合
八、BaseRequestHandler
九、StreamRequestHandler TCP请求处理类的一个实现
十、DataStreamRequestHandler UDP请求处理类的一个实现
1、处理程序
使用本模块时,必须定义一个继承于基类(父类)BaseRequestHandler的处理程序类。BaseRequestHandler类的实例h能够实现如下方法:
一、h.handle() 调用该方法执行实际的请求操做。调用该函数能够不带任何参数,可是几个实例变量包含有用的值。h.request包含请求,h.client_address包含客户端地址,h.server包含调用处理程序的实例。对于TCP之类的数据流服务,h.request属性是套接字对象。对于数据报服务,它是包含收到数据的字节字符串。全部的逻辑函数都写在这个方法里。
二、h.setup() 该方法在handle()以前调用。默认状况下,它不执行任何操做。若是但愿服务器实现更多链接设置(如创建SSL链接),能够在这里实现。
三、h.finish() 调用本方法能够在执行完handle()以后执行清除操做。默认状况下,它不执行任何操做。若是setup()和handle()方法都不生成异常,则无需调用该方法。
若是知道应用程序只能操纵面向数据流的链接(如TCP),那么应从StreamRequestHandler继承,而不是BaseRequestHandler。StreamRequestHandler类设置了两个属性,h.wfile是将数据写入客户端的类文件对象,h.rfile是从客户端读取数据的类文件对象。
若是要编写针对数据包操做的处理程序并将响应持续返回发送方,那么它应当从DatagramRequestHandler继承。它提供的类接口与StramRequestHandler相同。
2、服务器
socketserver 类提供的server类之间的关系
+------------+
| BaseServer | -----原生类(基础类)
+------------+
|
v
提供tcp链接的一些服务 与UDPserver只差一个参数(address_family = socket.AF_UNIX)
+-----------+ +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+ +------------------+
|
v
提供tcp链接的一些服务 与TCPserver只差一个参数(address_family = socket.AF_UNIX)
+-----------+ +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+ +--------------------+
要使用处理程序,必须将其插入到服务器对象。定义了四个基本的服务器类。
(1)TCPServer(address,handler) 支持使用IPv4的TCP协议的服务器,address是一个(host,port)元组。Handler是BaseRequestHandler或StreamRequestHandler类的子类的实例。
(2)UDPServer(address,handler) 支持使用IPv4的UDP协议的服务器,address和handler与TCPServer中相似。
(3)UnixStreamServer(address,handler) 使用UNIX域套接字实现面向数据流协议的服务器,继承自TCPServer。
(4)UnixDatagramServer(address,handler) 使用UNIX域套接字实现数据报协议的服务器,继承自UDPServer。
全部四个服务器类的实例都有如下方法和变量:
一、s.socket 用于传入请求的套接字对象。
二、s.sever_address 监听服务器的地址。如元组("127.0.0.1",80)
三、s.RequestHandlerClass 传递给服务器构造函数并由用户提供的请求处理程序类。
四、s.serve_forever() 处理无限的请求
五、s.shutdown() 中止serve_forever()循环
六、s.fileno() 返回服务器套接字的整数文件描述符。该方法能够有效地经过轮询操做(如select()函数)使用服务器实例。
3、定义自定义服务器
服务器每每须要特殊的配置来处理不一样的网络地址族、超时期、并发和其余功能,能够经过继承上面四个基本服务器类来自行定义。
能够经过混合类得到更多服务器功能,这也是经过进程或线程分支添加并发行的方法。为了实现并发性,定义了如下类:
(1)ForkingMixIn 将UNIX进程分支添加到服务器的混合方法,使用该方法可让服务器服务多个客户。
(2)ThreadingMixIn 修改服务器的混合类,可使用线程服务多个客户端。
要向服务器添加这些功能,可使用多重继承,其中首先列出混了类。
因为并发服务器很经常使用,为了定义它,SocketServer预约义了如下服务器类:
(1)ForkingUDPServer(address,handler)
(2)ForkingTCPServer(address,handler)
(3)ThreadingUDPServer(address,handler)
(4)ThreadingTCPServer(address,handler)
建立socketserver服务端与客户端
import socketserver print("......") class Myserver(socketserver.BaseRequestHandler): def handle(self): #源码以定义好的方法,只需写具体的逻辑方法 print("服务器启动:") while True: conn = self.request print(self.client_address) while True: try: date = conn.recv(1024) except Exception: break if not date:break print(str(date, "utf8")) inp = input(">>>>:") conn.send(bytes(inp,"utf8")) conn.close() if __name__ == "__main__": server = socketserver.ThreadingTCPServer(("127.0.0.1",8010),Myserver) #元组建立对象,绑定address(),实现监听(源码默认为5) server.serve_forever() #程序启动
import socket sk = socket.socket() address = ("127.0.0.1",8010) sk.connect(address) while True: inp = input(">>>>:") if inp == "q": break sk.send(bytes(inp, "utf8")) date = sk.recv(1024) print(str(date,"utf8")) sk.close()