下面是python网络编程中基础入门介绍,希望对各位有所帮助!
- 服务器是一系列硬件或软件为客户端提供服务,等待客户端请求,并响应请求,即提供服务,然后等待更多的请求
- 客户端对服务进行一次性的请求,接受由服务器提供的响应。客户端可在一段时间多次请求,这些请求被当作不同的事物来处理
- 打印机服务器是硬件服务器
- 文件服务器也是硬件服务器
- 软件服务器主要服务包括程序执行,数据传输检索,聚合,更新等数据操作
- Web服务器是一种常见的软件服务器,用户请求网页内容,服务器做出响应,并等待下次请求
- 数据库服务器是另一类软件服务器,接受客户端的存储或检索请求,响应请求,等待更多的事物处理
- 窗体服务器:这些服务器可认为是硬件服务器,运行时在附带显示设备的计算机上
- 在服务器响应客户端之前,有一些预备工作需要处理,之后才可以进行相关请求的处理
- 首先会建立通信端点,使得服务器处于监听状态,等待客户端的响应
- 客户端只需创建它的单一通信端点,然后建立于服务器的连接即可,之后,客户端发出请求,并等待响应,若确认或受到某种信息,断开通信即可
套接字
- 套接字是计算机网络数据结构,体现了通信端点的概念
- 在任何通信开始之前,网络应用程序必须创建套接字,若没有套接字,将不会有任何通信
- 套接字最初是为同一主机的应用程序所创建的,实现主机间的进程通信
- 共有两种套接字:基于文件和面向网络的
- 由于是python网络编程,所以只讲网络型的套接字,家族名称为AF_INET,或者地址家族:因特网
套接字地址:主机-端口对
- 套接字类似于通信的基础设施,主机和端口号就是区号和电话号码的组合
- 有效的端口号范围是0—65535
面向连接的套接字和无连接的套接字
- 面向连接的套接字
- 面向连接意味着进行通信前需要先创建连接
- 面向连接的通信提供序列化的,可靠的和不重复的数据交付,而没有记录边界
- 面向连接的消息可拆分成多段片段,并且每一个小片段都能够到达目的地,之后按照拆分顺序以及到达顺序将信息组合传递给等待的应用程序
- 实现连接类型的主要协议有传输控制协议(TCP),要创建TCP套接字,必须使用SOCK_STREAM作为套接字类型,TCP套接字的名字SOCK_STREAM基于流套接字的其中一种表示
- 无连接的套接字
- 数据包类型的套接字是一种无连接的套接字,在通信开始之前,不需要先建立连接,消息以整体进行发送
- 无连接的类型不能保证数据到达目的地的准确性,并且可能在网络中存在大量重复的消息,因为一旦没有传递成功,就会重传消息,由此产生了重复的消息
- 实现这种连接类型的主要协议是用户数据报协议(UDP)
- 创建UDP协议需要使用SOCK_DGRAM作为套接字类型
- python中的socket模块提供了socket()函数来建立套接字对象
使用socket.socket()函数创建套接字,语法如下:socket(socket_family,socket_type,protocol=0)
其中socket_family是AF_UNIX或AF_INET,socket_type是SOCK_STREAM或SOCK_DGRAM,protocol通常省略,默认为0
使用下面的方式调用socket.socket()模块函数,创建TCP/IP套接字对象
tcpSock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
创建UDP/IP套接字udpSOCK=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
*可以使用from socket import *方式来导入socket模块的全部内容
代码缩短为tcpSock=socket(AF_INET,SOCK_STREAM)
一旦创建了套接字对象就可以使用套接字对象的方法进行进一步交互
名称 | 描述 |
---|---|
服务器套接字 | 服务器端的如下 |
s.bind() | 将地址(主机名,端口号)绑定到套接字上 |
s.listen() | 设置并启动TCP监听 |
s.accept() | 被动接受TCP客户端连接,一直等待直到连接到达(阻塞) |
客户端套接字方法 | 客户端的如下 |
s.connect() | 主动发起TCP服务器连接 |
s.connect_ex() | connect()的扩展版本,会议错误码的形式返回问题,而不是抛出异常 |
普通的套接字方法 | 普通的套接字方法如下: |
s.recv() | 接收TCP消息 |
s.recv_into() | 接收TCP消息到指定的缓冲区 |
s.send() | 发送TCP消息 |
s.sendall() | 完整的发送TCP消息 |
s.recvfrom() | 接收UDP消息 |
s.recvfrom_into() | 接收UDP消息到指定的缓冲区 |
s.sendto() | 发送UDP消息 |
s.getpeername() | 连接到套接字(TCP)的远程地址 |
s.getsockname() | 当前套接字的地址 |
s.getsockopt() | 返回给定套接字选项的值 |
s.setsockopt() | 设置给定套接字选项的值 |
s.shutdown() | 关闭连接 |
s.close() | 关闭套接字 |
s.detach() | 在未关闭文件描述符的情况下关闭套接字,返回文件描述符 |
s.ioctl() | 控制套接字的模式(仅支持 windows) |
面向阻塞的套接字 | 面向阻塞的套接字方法 |
s.setblocking() | 设置套接字的阻塞或非阻塞模式 |
s.settimeout() | 设置阻塞套接字操作的超时时间 |
s.gettimeout() | 获取阻塞套接字操作的超时时间 |
面向文件的套接字方法 | 面向文件 |
s.fileno() | 套接字的文件描述符 |
s.makefile() | 创建与套接字关联的文件对象 |
数据属性 | 数据属性如下 |
s.family | 套接字家族 |
s.type | 套接字类型 |
s.proto | 套接字协议 |
创建服务器的伪代码如下:
ss = socket() # 创建服务器套接字
ss.bind() # 套接字与地址绑定
ss.listen() # 监听连接
inf_loop: # 服务器无限循环
cs = ss.accept() # 接受客户端连接 *comm_loop*: # 通信循环
cs.recv()/cs.send() # 对话(接收/发送)
cs.close() # 关闭客户端套接字
ss.close() # 关闭服务器套接字#(可选)
所有的套接字都是通过socket.socket()函数创建,由于服务器需要占用一个端口并等待客户端的请求,所以要将套接字与本地地址绑定
TCP服务器必须监听传入的连接,设置完成后,进入无限的等待客户端建立连接的循环之中
调用accept()函数之后,就开启一个简单的(单线程)服务器,此服务器会等待客户端的连接
默认情况下,accept为阻塞状态,意味着代码执行暂停,直到有一个连接到达
当服务器接收一个连接时就会返回一个客户端的套接字(使用accept方法),客户端套接字用来与消息进行交换
服务端与客户端的连接,一开始类似于主接线员与客户端先进行交互,然后把客户端这条线交给处理客户端请求的其他接线员,此时主接线员就会空出,等待下一个客户端的接入
创建了临时套接字,通信开始,当一方关闭连接或向对方发送一个空字符串时,通常就会关闭连接
一般来讲,当一个客户端关闭后,服务器进入下次循环,等待另一个客户端的连接
代码如下:(服务端)
from socket import * #导入socket模块的所有属性 from time import ctime #导入时间戳函数 BUFSIZE=1024 HOST='' #HOST设置为空,告诉bind()函数可以接收任何主机名,特殊的标识 PORT=21567 ADDR=(HOST,PORT) #设置主机名和端口号 #建立服务端套接字并进行设置 tcpSerSock=socket(AF_INET,SOCK_STREAM) tcpSerSock.bind(ADDR) tcpSerSock.listen(5) #设置连接被转接或拒绝前的可连接最大数 while True: print("Waiting for connection...") tcpCliSock,addr=tcpSerSock.accept() #建立客户端套接字 print("connected from:" ,addr) while True: #服务器端开始无限循环等待 data=tcpCliSock.recv(BUFSIZE) #接收客户端消息 if not data: #判断若数据为空,则退出循环 break tcpCliSock.send("%s,%s"%(ctime(),data)) #返回带有时间戳的客户端消息,即响应 tcpCliSock.close() #关闭客户端套接字 tcpSerSock.close() #此行永不执行,只是告诉你如何优雅的关闭服务端套接字
from socket import * HOST='127.0.0.1' #or 'localhost' PORT=21567 BUFSIZE=1024 ADDR=(HOST,PORT) tcpCliSOCK=socket(AF_INET,SOCK_STREAM) tcpCliSOCK.connect(ADDR) while True: data=input('> ') if not data: break tcpCliSOCK.send(data) data=tcpCliSOCK.recv(BUFSIZE) if not data: break print(data.decode('utf-8')) tcpCliSOCK.close()
- 客户端当连接上服务器之后需要进行数据输入
- 客户端在两种情况下跳出循环:
- 客户端为输入数据
- 客户端recv失败,服务器端终止会话
- 注意当在两个计算机上运行上述代码时,就会明显的看到客户端连接服务器的全部流程了,这将是非常激动的时刻
UDP服务器与TCP服务器的一个显著差异是:UDP面向无连接类型,所以不会有为了通信成功转换客户端套接字到一个独立的套接字操作
from socket import * #导入socket模块所有属性 from time import ctime HOST='' PORT=21567 ADDR=(HOST,PORT) #设置绑定地址 BUFSIZE=1024 udpSerSock=socket(AF_INET,SOCK_DGRAM) #建立udp服务端套接字 udpSerSock.bind(ADDR) #绑定地址 while True: print("Waiting for message...") data,addr=udpSerSock.recvfrom(BUFSIZE) #建立连接并接受数据消息 udpSerSock.sendto("[%s],%s"%(ctime(),data),addr) #发送消息,添加时间戳,地址 print("received from and returned to:",addr) udpSerSock.close() #此行永不执行,只是告诉你如何优雅的关闭服务器
- 需要注意udp在接收或发送消息时一定要有地址变量
- 接受的地址是客户端地址,发出也是客户端地址
from socket import * HOST='localhost' #设置本地主机 PORT=21567 BUFSIZE=1024 ADDR=(HOST,PORT) udpCliSock=socket(AF_INET,SOCK_DGRAM) #建立客户端udp套接字 while True: data=input('> ') #客户端将要发送的消息 if not data: break udpCliSock.sendto(data,ADDR) #客户端发送消息 data,ADDR=udpCliSock.recvfrom(BUFSIZE) #客户端接收来自服务端的消息 if not data: #判读消息是否为空 break print(data) #打印消息 udpCliSock.close()
属性名称 描述 数据属性 AF_UNIX,AF_INET,AF_INET6,AF_NETLINK,AF_TIPC python中支持的套接字家族地址 SO_STREAM,SO_DGRAM 套接字类型(TCP=流,UDP=数据报) has_ipv6 指示是否支持ipv6的布尔标记 异常 error 套接字相关错误 herror 主机和地址相关错误 gaierror 地址相关错误 timeout 超时时间 函数 socket() 以给定的地址家族,套接字类型和协议类型创建一个套接字对象 socketpair() 以给定的地址家族,套接字类型和协议类型创建一对套接字对象 creat_connection() 常规函数,接收一个地址对,返回套接字对象 fromfd() 以一个打开的文件描述符创建一个套接字 ssl() 通过套接字启动一个安全套接字层连接,不执行证书验证 getaddrinfo() 获取一个五元组序列形式的地址信息 getnameinfo() 给定一个套接字地址,返回主机名,端口号的二元组 getfqdn() 返回完整的域名 gethostname() 返回当前主机名 gethostbyname() 将一个主机名映射到它的地址 gethostbyname_ex() gethostbyname的扩展版,返回主机名,别名主机集合和IP地址列表 gethostbyaddr() 将一个IP地址映射到DNS信息,返回gethostbyname_ex()相同的三元组 getprotobyname() 将一个协议名映射到一个数字 getservbyname()/getservbyport() 将一个服务名映射到端口号或将一个端口号映射为服务名;对于任何一个函数来讲,协议名都是可选的 ntohl()/ntohs() 将来自网络的整数转换为主机字节顺序 htonl()/htons() 将来自主机的整数转换为网络字节顺序 inet_aton()/inet_ntoa() 将IP地址八进制字符串转换成32为的包格式,或者反过来 inet_pton()/inet_ntop() 将IP地址字符串转换成打包的二进制格式,或反过来 getdefaulttimeout()/setdefaulttimeout() 以秒(浮点数)为单位返回默认的套接字超时时间/以秒为单位设置套接字的超时时间
关于更多的socket莫信息,请参阅python官方文档
SocketServer模块目的是简化样板代码,此模块中的代码是创建网络客户端和服务器端所必需的代码
现在采用面向对对象的编程思想,将有助于组织数据,应用程序变为事件驱动,即只有在系统中的时间发生时,才会工作
下面是SocketServer模块类
类 描述 BaseServer 包含核心服务器功能和min-in类的钩子;仅用于推导,不会创建这个类的实例;可使用TCPServer或UDPServer TCPServer/UDPServer 基础的网络同步TCP/UDP服务器 UnixStreamServer/UnixDatagramServer 基于文件的同步TCP/UDP服务器 ForkingMixIN/ThreadingMinIn 核心派出或线程功能;只作用于min-in类与一个服务器类配合实现一些异步性;不能直接实例化这个类 ForkingTCPServer/ForkingUDPServer FOrkingMInIn和TCPServer/UDPServer的组合 ThreadingTCPServer/ThreadingUDPServer ThreadingMinIn和TCPServer/UDPServer的组合 BaseRequestHandler 包含处理服务请求的核心功能;仅仅用于推导,无法创建实例对象,可以使用StreamRequestHandler或DatagramRequestHandler创建类的实例 StreamRequestHandler/DataRequestHandler 实现TCP/UDP服务器的服务处理器
- 事件驱动就是事件的发送与接收服务,用类定义只是包括一个用来接收客户端消息的事件处理程序,其他的功能由SockerServer类实现,在此处的服务器循环中,并非在服务器中创建代码,而是定义一个处理程序,当接受传入请求时服务器调用写的函数
此处还是创建一个时间戳服务器
from Socketserver import (TCPServer as TCP,StreamRequestHandler as SRH) from time import ctime HOST='' PORT=21567 ADDR(HOST,PORT) class MyRequestHandler(SRH): #继承SRH类,并重写handle方法 def handle(self): print("...connected from :",self.client_address) self.wfile.write('[%s] %s'%(ctime(),self.rfile.readline())) tcpServ=TCP(ADDR,MyRequestHandler) #建立Tcp服务器根据主机信息和请求处理类 print("Waiting for connection...") tcpServ.serve_forever() #无限循环
- 当有消息传入时,handle方法被调用,因为处理请求程序像是文件读写,所以使用文件的读写方式接受和发送消息
from socket import * HOST='localhost' PORT=21567 ADDR(HOST,PORT) BUFSIZ=1024 while True: tcpCliSock=socket(AF_INET,SOCK_STREAM) tcpCliSock.connect(ADDR) data=input('> ') if not data: break tcpCliSock.send('%s\r\n'%data) #类似文件接收机制,需使用回车符 data=tcpCliSock.recv(BUFSIZ) if not data: break print(data.strip()) tcpCliSock.close()
- 由于使用请求处理程序,服务器接受连接,获取连接并关闭连接,所以不会一直与客户端连接,需要多次请求时,需多次创建新的套接字对象
关于python网络编程这一块,还有一个叫做Twisted框架,但由于涉及较多知识点,小白我不是特别了解,就不在此记录了
上面是最基本的python服务器与客户端搭建的代码,希望对学习网络编程的小伙伴一个入门介绍吧!