python核心编程第3版第2章 网络编程【读书笔记】

客户端/服务器架构

服务器是一系列硬件或软件,为一个或多个客户端(服务的用户)提供所需的“服务”。它存在的唯一目的就是等待客户端的请求,并响应它们(提供服务),然后等待更多请求。
客户端因特定的请求而联系服务器,并发送必要的数据,然后等待服务器的回应,最后完成请求或给出故障的原因。
客户端/服务器架构既可以应用于计算机硬件,也可以应用于软件。

硬件客户端/服务器架构

打印服务器、文件服务器

软件客户端/服务器架构

web服务器
数据库服务器
窗体服务器

客户端/服务器网络编程

在服务器响应客户端请求之前,首先会创建一个通信端点,它能够使服务器监听请求。一旦一个通信端点已经建立,监听服务器就可以进入无限循环中,等待客户端的连接并响应它们的请求。
客户端所需要做的只是创建它的单一通信端点,然后建立一个到服务器的连接。然后,客户端就可以发出请求,该请求包括任何必要的数据交换,一旦请求被服务器处理,且客户端收到结果或某种确认信息,此次通信就会被终止。

套接字:通信端点

套接字

在任何类型的通信开始之前,网络应用程序必须创建套接字,没有它将无法进行通信。
有两种类型的套接字:基于文件的和面向网络的。

基于文件的:
AF_UNIX,它代表地址家族:UNIX。
基于网络的:
AF_INET,它代表地址家族:因特网,使用最广泛。
AF_INET6,用于第6版因特网协议(IPv6)寻址。
AF_NETLINK,无连接的套接字。
AF_TIPC,支持TIPC,TIPC(进程间通信协议)允许计算机及群之中的及其相互通信。

python只支持AF_UNIX、AF_NETLINK、AF_TIPC、AF_INET家族。

套接字地址:主机-端口对

一个网络地址由主机名和端口号对组成,有效的端口号范围 0-65535。

面向连接的套接字 TCP

在进行通信之前必须先建立一个连接,这种类型的通信也成为虚拟电路或者流套接字。
实现这种连接类型的主要协议是传输控制协议(TCP)
为了创建TCP套接字,必须使用SOCK_STREAM作为套接字类型。

无连接的套接字 UDP

数据报类型的套接字,它是一种无连接的套接字,在通信开始之前并不需要建立连接。
实现这种连接类型的主要协议是用户数据报协议(UDP)
为了创建UDP套接字,必须使用SOCK_DGRAM作为套接字类型。

python中的网络编程 socket模块

socket()函数,用于创建套接字对象。

socket()模块函数

创建套接字:

socket(socket_family, socket_type, protocol=0) 

socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。 protocol 通常省略,默认值为 0。

创建 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 *:

tcpSock = socket(AF_INET, SOCK_STREAM) 
udpSock = socket(AF_INET, SOCK_DGRAM)

套接字对象内置方法

名称描述
服务器端套接字函数
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()控制套接字的模式
面向阻塞的套接字方法
s.setblocking()设置套接字的阻塞与非阻塞模式
s.settimeout()设置阻塞套接字操作的超时时间
s.gettimeout()得到阻塞套接字操作的超时时间
面向文件的套接字的函数
s.fileno()套接字的文件描述符
s.makefile()创建一个与该套接字关联的文件对象
数据属性
s.family()套接字家族
s.type套接字类型
s.proto套接字协议

创建TCP服务器

伪代码:
ss = socket()                 # 创建服务器套接字
ss.bind()                     # 套接字与地址绑定
ss.listen()                   # 监听连接
inf_loop:                     # 服务器无限循环
    cs = ss.accept()          # 接受客户端连接
    comm_loop:                # 通信循环
        cs.recv()/cs.send()   # 对话(接收/发送)
    cs.close()                # 关闭客户端套接字
ss.close()                    # 关闭服务器套接字(可选)

TCP时间戳服务器:这个脚本创建一个TCP服务器,它接收来自客户端的消息,然后将消息加上时间戳前缀并发送回客户端。

from socket import *
from time import ctime
HOST = ''  # 变量是空白的,表示它可以使用任何可用的地址
PORT = 21567  # 选择一个随机的端口号
BUFSIZ = 1024  # 对于该应用程序,将缓冲区大小设置为1KB
ADDR = (HOST, PORT)   # 套接字地址,主机-端口对

tcpSerSock = socket(AF_INET, SOCK_STREAM)  # 分配了TCP服务器套接字
tcpSerSock.bind(ADDR)  # 将套接字绑定到服务器地址,下一行开启TCP监听器的调用
tcpSerSock.listen(5)  # listen()方法的参数实在连接被转接或拒绝之前,接入连接请求的最大数

while True:  # 进入服务器的无限循环之中,被动的等待客户端的连接
    print 'waiting for connection...'
    tcpCliSock, addr = tcpSerSock.accept()  # 当一个连接请求出现时,我们进入对话循环中
    print '...connected from:', addr

    while True:  # 在该循环中我们等待客户端发送的消息
        data = tcpCliSock.recv(BUFSIZ)
        if not data:  # 如果消息是空白的,这意味着客户端已经退出,此时将跳出对话循环,然后等待另一个客户端连接
            break
        tcpCliSock.send('[%s] %s' % (ctime(), data))  # 如果得到客户端发送的消息,就将其格式化并返回相同的数据,但是会在这些数据中加上当前时间戳的前缀。

    tcpCliSock.close()
tcpSerSock.close()  # 永远不会执行

创建TCP客户端

伪代码:
cs = socket()                 # 创建客户端套接字
cs.connect()                  # 尝试连接服务器
ss.listen()                   # 通信循环
comm_loop:                    # 服务器无限循环
    cs.recv()/cs.send()       # 对话(接收/发送)
cs.close()                    # 关闭客户端套接字

TCP时间戳客户端:这个脚本创建一个TCP客户端,它提示用户输入发送到服务器端的消息,并接受从服务器端返回的添加了时间戳前缀的相同消息,然后将结果展示给用户。

from socket import *

HOST = 'localhost'  # 指定服务器的主机名
PORT = 21567   # 指定服务器的端口号,端口号PORT应该与你为服务器设置的完全相同
BUFSIZ = 1024  # 对于该应用程序,将缓冲区大小设置为1KB
ADDR = (HOST, PORT)  # 套接字地址,主机-端口对

tcpCliSock = socket(AF_INET, SOCK_STREAM)  # 分配了TCP客户端套接字
tcpCliSock.connect(ADDR)  # 主动调用并连接到服务器

while True:  # 并不会永远进行下去
    data = raw_input('> ')  # 第一种条件:用户没有输入
    if not data:
        break
    tcpCliSock.send(data)
    data = tcpCliSock.recv(BUFSIZ)  # 服务器终止且对recv()方法的调用失败
    if not data:
        break
    print data
tcpCliSock.close()

创建UDP服务器

伪代码:
ss = socket()                            # 创建服务器套接字
ss.bind()                                # 绑定服务器套接字
inf_loop:                                # 服务器无限循环
    cs = ss.recvfrom()/ss.sendto()       # 关闭(接收/发送)
ss.close()                               # 关闭服务器套接字(可选)

UDP时间戳服务器:这个脚本创建一个UDP服务器,它接受客户端发来的消息,并将加了时间戳前缀的该消息返回给客户端。

from socket import *
from time import ctime

HOST = ''  # 变量是空白的,表示它可以使用任何可用的地址
PORT = 21567  # 选择一个随机的端口号
BUFSIZ = 1024  # 对于该应用程序,将缓冲区大小设置为1KB
ADDR = (HOST, PORT)  # 套接字地址,主机-端口对

udpSerSock = socket(AF_INET, SOCK_DGRAM)  # 分配了UDP服务器套接字
udpSerSock.bind(ADDR)  # 将套接字绑定到服务器地址

while True:  # 进入服务器的无限循环之中,被动的等待客户端的连接
    print 'waiting for message...'
    data, addr = udpSerSock.recvfrom(BUFSIZ)  # 当一条消息到达时,我们就处理它,并将其发送回客户端,然后等待另一条消息
    udpSerSock.sendto('[%s] %s' % (ctime(), data), addr)  
    print '...received from and returned to:', addr
udpSerSock.close()  # 永远不会执行

创建UDP客户端

伪代码:
cs = socket()                       # 创建客户端套接字
comm_loop:                          # 通信循环
    cs.sendto()/cs.recvfrom()       # 对话(接收/发送)
cs.close()                          # 关闭客户端套接字

UDP时间戳客户端:这个脚本创建一个UDP客户端,它提示用户输入发送给服务器的消息,并接收服务器加了时间戳前缀的消息,然后将它们显示给用户。

from socket import *

HOST = 'localhost'  # 指定服务器的主机名
PORT = 21567   # 指定服务器的端口号,端口号PORT应该与你为服务器设置的完全相同
BUFSIZ = 1024  # 对于该应用程序,将缓冲区大小设置为1KB
ADDR = (HOST, PORT)  # 套接字地址,主机-端口对

udpCliSock = socket(AF_INET, SOCK_DGRAM)  # 分配了UDP客户端套接字

while True:  # 并不会永远进行下去
    data = raw_input('> ')  # 第一种条件:用户没有输入
    if not data:
        break
    udpCliSock.sendto(data, ADDR)  # 简单的发送一条消息并等待副武器的回复
    data, ADDR = udpCliSock.recvfrom(BUFSIZ)  # 服务器终止且对recvfrom()方法的调用失败
    if not data:
        break
    print data
udpCliSock.close()

socket模块属性

属性描述
数据属性
AF_UNIX, AF_INET, AF_INET6、AF_NETLINK、AF_TIPCPython 支持的套接字地址家族
SO_STREAM, SO_DGRAM套接字类型 (TCP = 流, UDP = 数据报)
has_ipv6表示是否支持 IPv6 的布尔标记
异常
error套接字相关错误
herror主机和地址相关的错误
gaierror地址相关的错误
timeout超时
函数
socket()用指定的地址家族,套接字类型和协议类型(可选)创建一个套接字对象
socketpair()用指定的地址家族,套接字类型和协议类型(可选)创建一对套接字对象
create_connection()常规函数,它接收一个地址(主机名,端口号)对,返回套接字对象
fromfd()用一个已经打开的文件描述符创建一个套接字对象
ssl()通过套接字启动一个安全套接字层(SSL)连接;不做证书验证。
getaddrinfo()获取一个五元组序列形式的地址消息
getnameinfo()给定一个套接字地址,返回(主机名,端口号)二元组
getfqdn()返回完整的域名
gethostname()返回当前主机名
gethostbyname()将一个主机名映射到它的IP地址
gethostbyname_ex()gethostbyname()的扩展版本,返回主机名,主机所有的别名和 IP 地址列表。
gethostbyaddr()由 IP 地址得到 DNS 信息,返回一个类似 gethostbyname_ex() 的 3 元组。
getprotobyname()将一个协议名(如‘tcp’)映射到一个数字
getservbyname()/getservbyport()将一个服务名映射到一个端口号,或者反过来;对于任何一个函数来说,协议名都是可选的
ntohl()/ntohs()将来自网络的整数转换为主机字节顺序
htonl()/htons()将来自主机的整数转换为网络字节顺序
inet_aton()/inet_ntoa()将IP地址八进制字符串转换为32位的包格式,或者反过来(仅用于IPv4地址)
inet_pton()/net_ntop()将IP地址字符串转换成打包的二进制格式,或者反过来(同时适用于IPv4和IPv6地址)
getdefaulttimeout()/setdefaulttimeout()以秒(浮点数)为单位返回默认套接字超时时间;以秒(浮点数)为单位设置默认套接字超时时间

SocketServer模块

SocketServer模块

描述
BaseServer包含服务器的核心功能与混合(mix-in)类的钩子功能;仅用于推到,这样不会创建这个类的实例;可以用TCPServer或UDPServer创建类的实例
TCPServer/ UDPServer基础的网络同步 TCP/UDP 服务器
UnixStreamServer/ UnixDatagramServer基于文件的基础同步 TCP/UDP 服务器
ForkingMixIn/ThreadingMixIn核心派出或线程功能;只用作mix-in类与一个服务器类配合实现一些异步性;不能直接实例化这个类
ForkingTCPServer/ ForkingUDPServerForkingMixIn 和 TCPServer/UDPServer 的组合
ThreadingTCPServer/ ThreadingUDPServerThreadingMixIn 和 TCPServer/UDPServer 的组合
BaseRequestHandler包含处理服务请求的核心功能;仅仅用于推导,这样无法创建这个类的实例;可以使用 StreamRequestHandler 或 DatagramRequestHandler创建类的实例
StreamRequestHandler/ DatagramRequestHandler实现TCP/UDP 服务器的服务处理器

创建SocketServerTCP 服务器

SocketServer 时间戳服务器:通过使用 SocketServer 类、TCPServer 和 StreamRequestHandler ,创建一个时间戳 TCP 服务器。

from SocketServer import (TCPServer as TCP, StreamRequestHandler as SRH)
from time import ctime

HOST = ''  # 变量是空白的,表示它可以使用任何可用的地址
PORT = 21567  # 选择一个随机的端口号
ADDR = (HOST, PORT)  # 套接字地址,主机-端口对


class MyRequestHandler(SRH):  # 得到了请求处理程序MyRequestHandler,作为SocketServer中StreamRequestHandler的一个子类

    def handle(self):   # 重写handle方法,当街收到一个来自客户端的消息时,就调用它
        print '...connected from:', self.client_address
        self.wfile.write('[%s] %s' % (ctime(), self.rfile.readline()))  # 使用readline()来获取客户端消息,并利用write()将字符串发送回客户端

tcpServ = TCP(ADDR, MyRequestHandler)  # 用给定的主机信息和请求处理类创建了TCP服务器
print 'waiting for connection...'
tcpServ.serve_forever()   # 无限循环的等待并服务于客户端请求

创建SocketServerTCP 客户端

SocketServer 时间戳客户端:它知道如何与类似文件的SocketServer类StreamRequest Handler 对象通信。

from socket import *

HOST = 'localhost'  # 指定服务器的主机名
PORT = 21567   # 指定服务器的端口号,端口号PORT应该与你为服务器设置的完全相同
BUFSIZ = 1024  # 对于该应用程序,将缓冲区大小设置为1KB
ADDR = (HOST, PORT)  # 套接字地址,主机-端口对

while True:  # 并不会永远进行下去
    tcpCliSock = socket(AF_INET, SOCK_STREAM)  # 分配了TCP客户端套接字
    tcpCliSock.connect(ADDR)  # 主动调用并连接到服务器
    data = raw_input('> ')  # 第一种条件:用户没有输入
    if not data:
        break
    tcpCliSock.send('%s\r\n' % data)  # 因为这里使用的处理程序类对待套接字通信就像文件一样,所以必须发送行终止符
    data = tcpCliSock.recv(BUFSIZ)  # 服务器终止且对recv()方法的调用失败
    if not data:
        break
    print data.strip()   # 当得到从服务器返回的消息时,用strip()函数对其进行处理并使用print声明自动提供的换行符
    tcpCliSock.close()  # SocketServer请求处理程序的默认行为是接受连接、获取请求,然后关闭连接

Twisted 框架介绍

Twisted 是一个完整的事件驱动的网络框架。利用它既能使用也能开发完整的异步网络应用程序和协议。

创建 Twisted Reactor TCP 服务器

Twisted Reactor 时间戳 TCP 服务器:这是一个使用 Twisted Internet 类的时间戳 TCP 服务器。

from twisted.internet import protocol, reactor
from time import ctime

PORT = 21567  # 设置常用端口号


class TSServProtocol(protocol.Protocol):  # 获得protocol类并为时间戳服务器调用TSServProtocol

    def connectionMade(self):  # 重写 connectionMade,当一个客户端连接到服务器时就会执行
        clnt = self.clnt = self.transport.getPeer().host  # 获取主机信息
        print '...connected from:', clnt

    def dataReceived(self, data):  # 重写 dataReceived,当服务器接收到客户端通过网络发送的一些数据时就会调用
        self.transport.write('[%s] %s' % (ctime(), data))

factory = protocol.Factory()  # 创建一个协议工厂,因为每次得到一个接入连接时,都能制造协议的一个实例
factory.protocol = TSServProtocol
print 'waiting for connection...'  # reactor中安装一个TCP监听器,以此检查服务请求,当它接收到一个请求时,就会创建一个TSServProtocol实例来处理那个客户端的事务
reactor.listenTCP(PORT, factory)  # reactor作为该方法的一个参数在数据中传输,能在无须自己提取它的情况下访问它
reactor.run()

创建 Twisted Reactor TCP 客户端

Twisted Reactor Timestamp TCP 客户端:用 Twisted 重写我们已经熟悉的时间戳 TCP 客户端。

from twisted.internet import protocol, reactor
from time import ctime

PORT = 21567  # 设置常用端口号


class TSServProtocol(protocol.Protocol):  # 获得protocol类并为时间戳服务器调用TSServProtocol

    def connectionMade(self):  # 重写 connectionMade,当一个客户端连接到服务器时就会执行
        clnt = self.clnt = self.transport.getPeer().host  # 获取主机信息
        print '...connected from:', clnt

    def dataReceived(self, data):  # 重写 dataReceived,当服务器接收到客户端通过网络发送的一些数据时就会调用
        self.transport.write('[%s] %s' % (ctime(), data))

factory = protocol.Factory()  # 创建一个协议工厂,因为每次得到一个接入连接时,都能制造协议的一个实例
factory.protocol = TSServProtocol
print 'waiting for connection...'  # reactor中安装一个TCP监听器,以此检查服务请求,当它接收到一个请求时,就会创建一个TSServProtocol实例来处理那个客户端的事务
reactor.listenTCP(PORT, factory)  # reactor作为该方法的一个参数在数据中传输,能在无须自己提取它的情况下访问它
reactor.run()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值