Python网络编程

简介

使用套接字进行网络编程。python

网络编程

使用socket模块的socket()函数,能够建立套接字。react

socket模块函数

要建立套接字,必须使用socket.socket()函数,语法以下:shell

socket(socket_family, socket_type, protocol=0)编程

其中,socket_familyAF_UNIXAF_INETsocket_typeSOCK_STREAMSOCK_DGRAMprotocol一般省略,默认为0windows

导入模块缓存

  • 建立TCP/IP套接字安全

    tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)服务器

  • 建立UDP/IP套接字网络

    udpSock = socket.socket(socket.AF_INET, socket.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() 控制套接字的模式(仅支持windows)

面向阻塞的套接字方法

名称 描述
s.setblocking() 设置套接字的阻塞或非阻塞模式
s.settimeout() 设置阻塞套接字操做的超时时间
s.gettimeout() 获取阻塞套接字操做的超时时间

面向文件的套接字方法

名称 描述
s.fileno() 套接字的文件描述符
s.makefile() 建立与套接字关联的文件对象

数据属性

名称 描述
s.family 套接字家族
s.type 套接字类型
s.proto 套接字协议

建立TCP服务器

TCP服务器的通常伪代码。

ss = socket()                   # 建立服务器套接字
ss.bind()                       # 套接字与地址绑定
ss.listen()                     # 监听链接
inf_loop:                       # 服务器无限循环
    cs = ss.accept()            # 接收客户端链接
    comm_loop:                  # 通讯循环
        cs.recv() / cs.send()   # 对话(接受/发送)
    cs.close()                  # 关闭客户端套接字
ss.close()                      # 关闭服务器套接字(可选)
  • 全部套接字都是经过使用socket.socket()函数来建立
  • 服务器须要占用一个端口并等待客户端的请求,因此它们必须绑定到一个本地地址
  • 由于TCP是一种面向链接的通讯系统,因此TCP服务器开始操做以前,必须安装一些基础设施
  • TCP服务器必须监听(传入) 的链接,一旦这个安装过程完成后,服务器能够开始它的无限循环
  • 调用accept()函数以后,就开启了一个简单的服务器,它会等待客户端的链接,默认状况下,accept()是阻塞的,这意味着执行将被暂停,直到一个链接到达
  • 一旦服务器接受一个链接,就会返回(经过accept())一个独立的客户端套接字,用来与即将到来的消息进行交换
  • 一旦建立了临时套接字,通讯就能够开始,经过使用这个新的套接字,客户端与服务器就能够开始参与发送和接受的对话中,直到链接终止
  • 当一方关闭链接或者向对方发送一个空字符串时,一般就会关闭链接
  • 在代码中,一个客户端链接关闭以后,服务器就会等待另外一个客户端链接

下面是一个TCP服务器程序,它接受客户端发送的数据字符串,并将其打上时间戳,并返回给客户端。

# coding: utf-8

from socket import *
from time import ctime


HOST = ''       # HOST变量是空白,这是对bind()方法的标识,表示它能够使用任何可用的地址
PORT = 21567    
BUFSIZ = 1024   # 缓冲区大小设置为1KB
ADDR = (HOST, PORT) 

tcpSerSock = socket(AF_INET, SOCK_STREAM)   # 分配TCP服务器套接字
tcpSerSock.bind(ADDR)                       # 将套接字绑定到服务器地址
tcpSerSock.listen(5)    # 开启TCP监听器的调用,参数是在链接被转接或拒绝以前,传入链接请求的最大数

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(bytes('[%s] %s' % (ctime(), data), 'utf-8'))

    tcpCliSock.close()                      # 关闭当前客户端链接

# tcpSerSock.close()

建立TCP客户端

TCP客户端的通常伪代码。

cs = socket()               # 建立客户端套接字
cs.connect()                # 尝试链接服务器
comm_loop:                  # 通讯循环
    cs.send() / cs.recv()   # 对话(发送 / 接受)
cs.close()                  # 关闭客户端套接字

Python代码实现的TCP客户端。

# coding: utf-8

from socket import *

HOST = '127.0.0.1'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)

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

while True:
    data = input('> ')
    if not data:
        break
    tcpCliSock.send(bytes(data, 'utf-8'))
    data = tcpCliSock.recv(BUFSIZ)
    if not data:
        break
    print(data.decode('utf-8'))

tcpCliSock.close()

执行TCP服务器和客户端

服务器端

bovenson@ThinkCentre:~/Git/notes/Python/Code/LearnPythonCode/network_programming$ python3 tsTserv.py 
waiting for connection...
...connected from: ('127.0.0.1', 35550)

客户端

bovenson@ThinkCentre:~/Git/notes/Python/Code/LearnPythonCode/network_programming$ python3 tsTclnt.py 
> Hello
[Mon Jun  4 11:20:46 2018] b'Hello'
> World
[Mon Jun  4 11:20:48 2018] b'World'
>

建立UDP服务器

UDP服务器不须要TCP服务器那么多的设置,由于它们不是面向链接的,除了等待传入的链接以外,几乎不须要作其余工做。

ss = socket()                           # 建立服务器套接字
ss.bind()                               # 绑定服务器套接字
inf_loop():                             # 服务器无限循环
    cs = ss.recvfrom() / ss.sendto()    # 关闭(接受 / 发送)
ss.close()                              # 关闭服务器套接字

UDP服务器和TCP服务器之间的另外一个显著差别是,觉得数据报套接字是无链接的,因此就没有为了成功通讯而使一个客户端链接到一个独立的套接字转换操做。这些服务器仅仅接受消息并有可能回复数据。

# coding: utf-8

from socket import *
from time import ctime

HOST = ''
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)

udpSerSock = socket(AF_INET, SOCK_DGRAM)
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客户端

客户端伪代码以下。

ss = socket()                   # 建立客户端套接字
comm_loop:                      # 通讯循环
    cs.sendto() / cs.recvfrom() # 对话(发送 / 接收)
cs.close()                      # 关闭客户端套接字

Python实现的客户端代码。

# coding: utf-8

from socket import *
from time import ctime

HOST = ''
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)

udpSerSock = socket(AF_INET, SOCK_DGRAM)
udpSerSock.bind(ADDR)

while True:
    print('waiting for message...')
    data, addr = udpSerSock.recvfrom(BUFSIZ)
    udpSerSock.sendto(bytes('[%s] %s' % (ctime(), data), 'utf-8'), addr)
    print('...received from and returned to:', addr)

# udpSerSock.close()

执行UDP服务器和客户端

客户端

/usr/bin/python3.5 /home/bovenson/Git/notes/Python/Code/LearnPythonCode/network_programming/tsUclnt.py
> Hello
b"[Mon Jun  4 12:42:07 2018] b'Hello'"
> World
b"[Mon Jun  4 12:42:08 2018] b'World'"
>

服务端

/usr/bin/python3.5 /home/bovenson/Git/notes/Python/Code/LearnPythonCode/network_programming/tsUserv.py
waiting for message...
...received from and returned to: ('127.0.0.1', 59736)
waiting for message...
...received from and returned to: ('127.0.0.1', 59736)
waiting for message...

和TCP不一样的是,UDP客户端能够先于UDP服务器运行。可是TCP服务器必须先于TCP客户端运行。

socket模块属性

数据属性

属性名称 描述
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() 以给定的地址家族、套接字类型和协议类型(可选)建立一对套接字对象
create_connection() 常规函数,接受一个地址(主机号,端口号)对,返回套接字对象
fromfd() 以一个打开的文件描述符建立一个套接字对象
ssl() 经过套接字启动一个安全套接字层链接;不执行证书验证
getaddrinfo() 获取一个五元组序列形式的地址信息
getnameinfo() 给定一个套接字地址,返回(主机号,端口号)二元组
getfqn() 返回完整的域名
gethostname() 返回当前主机名
gethostbyname() 将一个主机名映射到它的IP地址
gethostbyname_ex() gethostname()的扩展版本,它返回主机名、别名主机集合和IP地址列表
gethostbyaddr() 将一个IP地址映射到DNS信息:返回与gethostbyname_ex()相同的3元组
getprotobyname() 将一个协议名(如tcp)映射到一个数字
getservbyname()/getservbyport() 将一个服务名映射到一个端口号,或者反过来;对于任何一个函数来讲,协议名都是可选的
ntohl()/ntohs() 未来自网络的整数转换为主机字节顺序
htonl()/htons() 未来自主机的整数转换为网络字节顺序
inet_aton()/inet_ntoa() 将IP地址八进制字符串转换为32位的包格式,或者反过来(仅用于IPv4地址)
inet_pton()/inet_ntop() 将IP地址字符串转换成打包的二进制格式,或者反过来(同时适用于IPv4和IPv6地址)
getdefaulttimeout()/setdefaulttimeout() 以秒(浮点数)为单位返回默认套接字超时时间;以秒(浮点数)为单位设置默认套接字超时时间

SocketServer模块

SocketServer是标准库中的一个高级模块,它的目标是简化不少样板代码,它们是建立网络客户端和服务器所需必须的代码。

描述
BaseServer 包含核心服务器功能和mix-in类的钩子;仅用于推导,这样不会建立这个类的实例;能够用TCPServerUDPServer建立类的实例
TCPServer/UDPServer 基础的网络同步TCP/UDP服务器
UnixStreamServer/UnixDatagramServer 基于文件的基础同步TCP/UDP服务器
ForkingMixIn/ThreadingMixIn 核心派出或线程功能;只用做mix-in类与一个服务器类配合实现一些异步性;不能直接实例化这个类
ForkingTCPServer/ForkingUDPServer ForkingMixInTCPServer/UDPServer的组合
ThreadingTCPServer/ThreadingUDPServer ThreadingMixInTCPServer/UDPServer的组合
BaseRequestHandler 包含处理服务请求的核心功能;仅仅用于推导,这样没法建立这个类的实例;能够使用StreamRequestHandlerDatagramRequestHandler建立类的实例
StreamRequestHandler/DatagramRequestHandler 实现TCP/UDP服务器的服务处理器

事件包括消息的发送和接受。

类定义只包括一个用来接收客户端消息的事件处理程序,全部其余的功能都来自使用的SocketServer类。

建立SocketServer TCP服务器

# coding: utf-8

from socketserver import (TCPServer as TCP, StreamRequestHandler as SRH)

from time import ctime

HOST = ''
PORT = 21567
ADDR = (HOST, PORT)


class MyRequestHandler(SRH):
    def handle(self):
        print('...connected from:', self.client_address)
        self.wfile.write('[%s] %s' % (ctime(), self.rfile.readline()))

tcpServ = TCP(ADDR, MyRequestHandler)
print('waiting for connection...')
tcpServ.serve_forever()

MyRequestHandler做为SocketServerStreamRequestHandler的一个子类,并重写了它的handler()方法。当接收到一个来自客户端的消息时,就会调用handler()方法。而StreamRequestHandler类将输入和输出套接字看做相似文件的对象,所以咱们将使用readline()来获取客户端消息,并利用write()将字符串发送回客户端。

建立SocketServer TCP客户端

# coding: utf-8

from socket import *

HOST = 'localhost'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)

while True:
    tcpCliSock = socket(AF_INET, SOCK_STREAM)
    tcpCliSock.connect(ADDR)
    data = input('> ')
    if not data:
        break
    tcpCliSock.send(bytes('%s\r\n' % data, 'utf-8'))
    data = tcpCliSock.recv(BUFSIZ)
    if not data:
        break
    print(data.strip())
    tcpCliSock.close()

执行TCP服务器和客户端

服务端

/usr/bin/python3.5 /home/bovenson/Git/notes/Python/Code/LearnPythonCode/network_programming/tsTservSS.py
waiting for connection...
...connected from: ('127.0.0.1', 36118)
...connected from: ('127.0.0.1', 36120)
...connected from: ('127.0.0.1', 36122)

客户端

/usr/bin/python3.5 /home/bovenson/Git/notes/Python/Code/LearnPythonCode/network_programming/tsTclntSS.py
> a
b"[Mon Jun  4 15:34:12 2018] b'a\\r\\n'"
> c
b"[Mon Jun  4 15:34:13 2018] b'c\\r\\n'"
>

Twisted框架

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

建立Twisted Reactor TCP服务器

# coding: utf-8

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

PORT = 21567

class TSServProtocol(protocol.Protocol):
    def connectionMade(self):   # 客户端链接到服务器时就会执行
        clnt = self.clnt = self.transport.getPeer().host
        print('...connected from:', clnt)

    def dataReceived(self, data):   # 当服务器收到客户端经过网络发送的一些数据时就会调用 dataReceived() 方法
        self.transport.write(bytes('[%s] %s' % (ctime(), data), 'utf-8'))

factory = protocol.Factory()
factory.protocol = TSServProtocol
print('waiting for connection...')
reactor.listenTCP(PORT, factory)
reactor.run()

建立Twisted Reactor TCP客户端

# coding: utf-8

from twisted.internet import protocol, reactor

HOST = 'localhost'
PORT = 21567

class TSClntProtocol(protocol.Protocol):
    def sendData(self):
        data = input('> ')
        if data:
            print('...sending %s...' % data)
            self.transport.write(bytes(data, 'utf-8'))
        else:
            self.transport.loseConnection()

    def connectionMade(self):
        self.sendData()

    def dataReceived(self, data):
        print(data)
        self.sendData()


class TSClntFactory(protocol.ClientFactory):
    protocol = TSClntProtocol
    clientConnectionLost = clientConnectionFailed = lambda self, connector, reason: reactor.stop()

reactor.connectTCP(HOST, PORT, TSClntFactory())
reactor.run()

执行TCP服务器和客户端

服务器

/usr/bin/python3.5 /home/bovenson/Git/notes/Python/Code/LearnPythonCode/network_programming/tsTservTW.py
waiting for connection...
...connected from: 127.0.0.1

客户端

/usr/bin/python3.5 /home/bovenson/Git/notes/Python/Code/LearnPythonCode/network_programming/tsTclntTW.py
> a
...sending a...
b"[Mon Jun  4 16:40:40 2018] b'a'"
> b
...sending b...
b"[Mon Jun  4 16:40:42 2018] b'b'"
>

参考

  • 《Python核心编程》
相关文章
相关标签/搜索