专栏首页python3[PYTHON] 核心编程笔记(16.P

[PYTHON] 核心编程笔记(16.P

16.1 介绍

16.1.1 什么是客户/服务器架构?

硬件的客户/服务器架构

软件客户/服务器架构

16.1.2 客户/服务器网络编程

16.2 套接字: 通讯端点

16.2.1 什么是套接字?

套接字是一种具有通讯端点感念的计算机网络数据结构

16.2.2 套接字地址:主机与端口

主机和端口类似区号和电话号码的一对组合

合法的端口号范围是0到65535,小于1024的端口号为系统保留端口

16.2.3 面向连接与无连接

面向连接(TCP)

套接字只有两种一种是面向连接套接字,即在通讯之前一定要建立一条连接,这种通讯方式提供了顺序的,可靠的不会重复的数据传输,每一份要发送的信息都会拆分成多份,每份都会不多不少的到达目的地后重新按顺序拼装起来,传给正在等待的应用程序

实现这种连接的主要协议就是传输控制协议(即TCP)

要创建TCP套接字就得在创建的时候指定套接字类型为SOCK_STREAM,表示为流套接字

无连接(UDP)

与虚电路相反的数据报型是无连接套接字,即无需建立连接就可以进行通讯,这意味着数据到达的顺序,可靠性及数据不重复性就无法保证,数据会保留在数据边界,数据不会像TCP协议那样被拆封为小块

使用数据报来传输数据就像邮政服务,邮件包裹不一定会按照他们发送的顺序到达,而且可能还到达不了,而且还可能被重传

由于面向连接套接字提供一些维持虚电路连接的开销,数据报较他来说基本上没有负担,所以它能更好的×××能,适合于某些应用场合

实现这种连接的主要协议就是用户数据报协议(即UDP)

要创建UDP套接字就得在创建的时候指定套接字类型为SOCK_DGRAM,即datagram数据报

由于这些套接字使用Internet协议来查找网络中的主机,这样形成的整个系统一般都会由这两对协议(TCP/IP)和(UDP/IP)来提及

16.3 Python中的网络编程

本节我们主要使用socket模块,模块中的socket()函数被用来创建套接字,其有自己的一套函数来提供基于套接字的网络传输

16.3.1 socket()模块函数:

创建套接字语法:

socket(socket_family,socket_type,protocol=0)

socket_family可以是AF_UNIX或AF_INET,soket_type可以是SOCK_STREAM或SOCK_DGRAM,protocal一般不填,默认为0

创建一个TCP/IP套接字,需要调用socket.socket()

tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

创建一个UDP/IP套接字,需要调用socket.socket()

udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

使用from socket import * 将socket模块里的所有属性带到命名空间里

当我们创建了套接字对象后,所有的交互豆浆通过对该套接字对象的方法进行调用

16.3.2 套接字对象(内建)方法

函数描述

s.bind()绑定地址(主机,端口号对)到套接字

s.listen()开始TCP监听

s.accept()被动接受TCP服务器连接,(阻塞式)等待连接到来

s.connect()主动初始化TCP服务器连接

s.connect_ex()connect()函数的扩展版本,出错时返回出错码,而不是抛异常公共用途的套接字函数

s.recv()接收TCP数据

s.send()发送TCP数据

s.sendall()完整发送TCP数据

s.recvfrom()接收UDP数据

s.sendto()发送UDP数据

s.getpeername()连接到当前套接字的远端地址

s.getsockname()当前套接字的地址

s.getsockopt()返回指定套接字的参数

s.setsockopt()设置指定套接字的参数

s.close()关闭套接字

s.setblocking()设置套接字的阻塞与非阻塞模式

s.settimeout()设置阻塞套接字操作的超时时间

s.gettimeout()得到阻塞套接字操作的超市时间

面向文件的套接字的函数

s.fileno()曹姐字的文件描述符

s.makefile()创建一个与该套接字关联的文件

16.3.3 创建一个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服务器负责监听连接,设置完,服务器就可以进行无限循环了

默认服务器会调用accept()阻塞式函数等待连接,来之前程序一直会处于挂起状态

一旦接收到一个连接,accept()函数就会返回一个单独的客户的套接字用于后续通讯.

例,tsTserv.py文件会创建一个TCP服务程序,这个程序会把客户发过来的字符串加上一个时间戳(格式:'[时间]数据')返回给客户

# vi tsTserv.py
--------------------------------
#!/usr/bin/env python
#coding: UTF-8 
from socket import *
from time import ctime
HOST = ''               #绑定IP
PORT = 21567            #端口号
BUFSIZ = 1024           #缓冲1K
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(BUFSIZ)
        if not data:
            break
        tcpCliSock.send('[%s] %s'%(ctime(),data))
        print [ctime()],':',data
tcpCliSock.close()
tcpSerSock.close()
--------------------------------

16.3.4 创建TCP客户端

cs = socket()#创建客户套接字

cs.connect()#尝试连接服务器

comm_loop#通讯循环

cs.send()/cs.recv()#对话(发送/接收)

cs.close()#关闭客户套接字

所有套接字都由socket.socket()函数创建,在客户有了套接字之后,可以调用connect()函数去连接服务器,连接服务器后,就可以与服务器对话,对话结束可关闭套接字结束连接

例,程序连接到服务器,提示用户输入要传输的数据,然后显示服务器返回的加了时间戳的结果

# vi tsTclnt.py
-------------------------------
#!/usr/bin/env python
from socket import *
HOST = '192.168.8.18'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)
tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)
while True:
    data = raw_input('>')
    if not data:
        break
    tcpCliSock.send(data)
    data = tcpCliSock.recv(BUFSIZ)
    if not data:
        break
    print data
tcpCliSock.close()
-------------------------------

16.3.5 运行我们的客户端与服务器程序

# python tsTserv.py
-----------------------------
waiting for connection...
-----------------------------
# python tsTclnt.py
----------------------------------
>send test data
[Thu Dec 19 12:46:36 2013] send test data
>hi
[Thu Dec 19 12:46:51 2013] hi
>how are you?
[Thu Dec 19 12:47:08 2013] how are you?
-----------------------------------
# python tsTserv.py
--------------------------------------
waiting for connection...
...connected from:  ('192.168.8.19', 56868)
['Thu Dec 19 12:46:36 2013'] : send test data
['Thu Dec 19 12:46:51 2013'] : hi
['Thu Dec 19 12:47:08 2013'] : how are you?
---------------------------------------

核心提示:

"友好地"退出的一个方法就是把服务器无限循环放在一个try-except语句中try子句中,并捕获EOFError和KeyboardInterrupt异常,在异常处理子句中,调用close()函数关闭服务器的套接字

例:

# vi tsTserv.py
--------------------------------
root@ubuntu:~/python# vi tsTserv.py    
#!/usr/bin/env python
#coding: UTF-8 
from socket import *
from time import ctime
HOST = ''               #绑定IP
PORT = 21567            #端口号
BUFSIZ = 1024           #缓冲1K
ADDR = (HOST,PORT)
tcpSerSock = socket(AF_INET, SOCK_STREAM)
tcpSerSock.bind(ADDR)   #套接字
tcpSerSock.listen(5)    #最大连接数
try:
    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))
            print [ctime()],':',data
except EOFError,KeyboardInterrupt:
    tcpCliSock.close()
    tcpSerSock.close()
--------------------------------
# vi tsTclnt.py
-------------------------------
#!/usr/bin/env python
from socket import *
HOST = '192.168.8.18'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)
tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)
try:
    while True:
        data = raw_input('>')
        if not data:
            break
        tcpCliSock.send(data)
        data = tcpCliSock.recv(BUFSIZ)
        if not data:
            break
        print data
except EOFError,KeyboardInterrupt:
    tcpCliSock.close()
-------------------------------

16.3.6 创建一个UDP服务器

ss = socket()#创建一个服务器套接字
ss.bind()#绑定服务器套接字
inf_loop:#服务器无限循环
cs = ss.recvfrom()/ss.sendto()#对话(接收与发送)
ss.close()#关闭服务器套接字
例,创建一个能接收客户的消息,在消息前加一个时间戳后返回的UDP服务器
# vi tsUserv.py
-----------------------------
#!/usr/bin/env python
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和TCP服务器的另一个重要区别是,由于数据报套接字是无连接的,所以无法把客户的链接将诶另外的套接字进行后续通讯,这些服务器只是接受消息,需要的话,给客户返回一个结果就可以了
16.3.7 创建一个UDP客户端
cs = socket()#创建客户套接字
comm_loop:#通讯循环
cs.sendto()/cs.recvfrom()#对话(发送/接收)
cs.close()#关闭客户套接字
在套接字对象创建好之后,我们就进入一个与服务器的对话循环,在通讯结束后,套接字就被关闭了
例,创建一个UDP客户端,程序会提示用户输入要传给服务器的信息,显示服务器返回的加了时间戳的结果
# vi tsUclnt.py
-------------------------------------
#!/usr/bin/env python
from socket import *
from time import ctime
HOST = ''
PORT = 21568
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
    print data
udpSerSock.close()
-------------------------------------
# vi tsUclnt.py     
-----------------------------------------
#!/usr/bin/env python
from socket import *
HOST = '192.168.8.18'
PORT = 21568
BUFSIZ = 1024
ADDR = (HOST, PORT)
udpCliSock = socket(AF_INET,SOCK_DGRAM)
while True:
    data = raw_input('> ')
    if not data:
        break
    udpCliSock.sendto(data, ADDR)
    data, ADDR = udpCliSock.recvfrom(BUFSIZ)
    if not data:
        break
    print data
udpCliSock.close()
-----------------------------------------

16.3.8 执行UDP服务器和客户端

# python tsUserv.py
---------------------------------
waiting for message...
----------------------------------
# python tsUclnt.py
---------------------------------
> hi
[Thu Dec 19 17:25:01 2013] hi
> test1
[Thu Dec 19 17:25:03 2013] test1
> test2
[Thu Dec 19 17:25:05 2013] test2
----------------------------------
# python tsUserv.py
----------------------------------
waiting for message...
...received from and returned to: ('192.168.8.19', 46359)
hi
waiting for message...
...received from and returned to: ('192.168.8.19', 46359)
test1
waiting for message...
...received from and returned to: ('192.168.8.19', 46359)
test2
waiting for message...
-----------------------------------

16.3.9 套接字模块属性

属性名字描述

AF_UNIX,AF_INET,AF_INET6Python支持的套接字家族

SO_STREAM,SO_DGRAM套接字类型(TCP=流,UDP=数据报)

has_ipv6标识是否支持IPV6的标志变量

异常

error套接字相关错误

herror主机和地址相关的错误

gaierror地址相关错误

timeout超时

函数

socket()用指定的地址家族,套接字类型和协议类型(可选)创建一个套接字对象

socketpair()用指定的地址家族,套接字类型和协议类型(可选)创建一个套接字对象

fromfd()用一个已经打开的额文件描述符创建一个套接字对象

数据属性

ssl()在套接字初始化一个安全套接字层(SSL),不做整数验证

getaddrinfo()得到地址信息

getfqdn()返回完整的域的名字

gethostname()得到当前主机名

gethostbyname()由主机名得到对应的ip地址

gethostbyname()_ex()gethostbyname()扩展版本,返回主机名,主机所有的别名和IP地址列表

gethostbyaddr()由IP地址得到DNS信息,返回一个类似gethostbyname()_ex()的三元组

getprotobyname()由协议名(如"tcp")得到对应的号码

getservbyname()/由服务名得到对应的端口号或相反

getservbyport()两个函数中,协议名都是可选的

ntohl()/htohs()把一个整数由网络字节序转为主机字节序

htonl()/htons()把一个整数由主机字节序转为网络字节序

inet_aton()/把IP地址转为32位×××,以及反向函数(仅对IPV4有效)

inet_ptoa()

inet_pton()/把IP地址转为二进制格式以及反响函数(仅对IPV4有效)

inet_ntop()

getdefaulttimeout()/得到/设置默认的套接字超时时间,单位秒(浮点数)

setdefaulttimeout()

16.4 SocketServer模块

SocketServer是标准库中一个高级别的模块,用于简化网络客户与服务器的实现,模块中,已经实现了一些可供使用的类

SocketServer模块的类

类描述

BaseServer包含服务器的核心功能与混合(mix-in)类的钩子功能,这个类用于派生,不要直接生成

这个类的类对象,可以考虑使用TCPServer和IDPServer

TCPServer/基本的网络同步TCP/UDP服务器

UDPServer

UnixStreamServer/基本的基于文件同步TCP/UDP服务器

UnixDatagramServer

ForkingMixIn/实现了核心的进程化或线程化的功能,用于与服务器类进行混合(mix-in),以提供一些异步特性.

ThreadingMixIn不要直接生成这个类的对象

ForkingTCPServer/ForkingMixIn和TCPServer/UDPServer的组合

ForkingUDPServer

ThreadingTCPServer/ThreadingMixIn和TCPServer/UDPServer组合

ThreadingUDPServer

BaseRequestHandler包含处理服务请求的核心功能,只用于派生新的类,不要直接生成这个类的对象

可以考虑使用StreamRequestHandler或DatagramRequestHandler

StreamRequestHandler/TCP/UDP服务器的请求处理类的一个实现

DatagramRequestHandler

16.4.1 创建一个SocketServerTCP服务器

使用SocketServer里的TCPServer和StreamRequestHandler类创建一个时间戳TCP服务器

# vi tsTservSS.py
---------------------------------------------
#!/usr/bin/env python
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()
---------------------------------------------

16.4.2 创建SocketServerTCP客户端

例,这是一个时间戳TCP客户端,它知道如何与SocketServer里StreamRequestHandler对象进行

# vi tsTclntSS.py
------------------------------------
#!/usr/bin/env python
from socket import *
HOST = 'localhost'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)
while True:
    tcpCliSock = socket(AF_INET, SOCK_STREAM)
    tcpCliSock.connect(ADDR)
    data = raw_input('> ')
    if not data:
        break
    tcpCliSock.send('%s\r\n' % data)
    data = tcpCliSock.recv(BUFSIZ)
    if not data:
        break
    print data.strip()
    tcpCliSock.close()
------------------------------------

16.4.2 执行TCP服务器和客户端

# python tsTservSS.py
---------------------------------------
waiting for connection...
...connected from:  ('127.0.0.1', 40712)
...connected from:  ('127.0.0.1', 40713)
...connected from:  ('127.0.0.1', 40714)
...connected from:  ('127.0.0.1', 40715)
...connected from:  ('127.0.0.1', 40716)
...connected from:  ('127.0.0.1', 40717)
----------------------------------------
# python tsTclntSS.py
---------------------------------------
> test1
[Fri Dec 20 04:44:02 2013] test1
> test2
[Fri Dec 20 04:44:16 2013] test2
> test3
[Fri Dec 20 04:44:18 2013] test3
----------------------------------------

注:连接服务器连接了2次

16.5 Twisted框架介绍

Twisted是一个完全事件驱动的网络框架,它允许你使用和开发完全异步的网络应用程序和协议

16.5.1 创建一个Twisted Reactor TCP服务器

例,这是一个使用Twisted Internet类的时间戳TCP服务器

# vi tsTservTW.py
----------------------------------
#!/usr/bin/env python
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):
        self.transport.write('[%s] %s' %(ctime(), data))
factory = protocol.Factory()
factory.protocol = TSServProtocol
print 'waiting for connection...'
reactor.listenTCP(PORT, factory)
reactor.run()
----------------------------------

16.5.2 创建一个Twisted Reactor TCP客户端

# vi tsTclntTW.py
--------------------------------
#!/usr/bin/env python
from twisted.internet import protocol, reactor
HOST = 'localhost'
PORT = 21567
class TSClntProtocol(protocol.Protocol):
    def sendData(self):
        data = raw_input("> ")
        if data:
            print '...sending %s...' % data
            self.transport.write(data)
        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()
--------------------------------

16.5.3 执行TCP服务器和客户端

Twisted客户显示的内容与我们之前的客户类似:

# python tsTservTW.py
---------------------------
waiting for connection...
----------------------------
# python tsTclntTW.py
-----------------------------------
> test1
...sending test1...
[Fri Dec 20 10:36:10 2013] test1
> test2
...sending test2...
[Fri Dec 20 10:36:15 2013] test2
------------------------------------
# python tsTservTW.py
----------------------------------
waiting for connection...
...connected from:  127.0.0.1
-----------------------------------

注: "connection from" 输出没有其他的信息,因为我们只询问服务器的transport对象的getPeer()函数要了主机地址的信息

16.6 相关模块

网络/套接字编程相关模块

模块描述

socket底层网络接口,本章讨论过

anycore/为能异步处理客户请求的网络应用程序提供底层功能

select在单线程网络服务器程序中,管理多个套接字连接

SocketServer包含了些网络应用程序服务器所需要的高级别模块,提供了完整的进程和线程版本

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python 聊天程序

    套接字是为特定网络协议(例如TCP/IP,ICMP/IP,UDP/IP等)套件对上的网络应用程序提供者提供当前可移植标准的对象。它们允许程序接受并进行连接,如发...

    py3study
  • python socket编程

    socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。在...

    py3study
  • Pycharm远程调试服务器代码(使用P

    1.随便准备一个项目工程,在本地用Pipenv创建一个虚拟环境并生成Pipfile和pipfile.lock文件,如下:

    py3study
  • caffe源码分析-SyncedMemory

    本文主要分析caffe中Blob内存管理类SyncedMemory,主要内容包括:

    bear_fish
  • 区块链是什么,三分钟认识区块链

    区块链是一种技术,能够实现去中心化(Decentralized)。那么去中心化是什么呢?要搞懂去中心化,首先让我们了解一下中心化是什么。

    南坡海瑞
  • kubernetes系列教程(十三)一次性任务Job和周期任务

    Windows下可以通过批处理脚本完成批处理任务,脚本运行完毕后任务即可终止,从而实现批处理任务运行工作,类似的任务如何在kubernetes中运行呢?答案是J...

    HappyLau谈云计算
  • 为什么“时间管理四象限”没有用处?

    这个图是把当前工作按照紧急程度和重要程度进行分类,以便更好的安排工作时间做最有意义的事情。

    顾宇
  • re正则表格式

    用户1733462
  • 区块链小白的入场新姿势

    +区块链技术是继互联网、无线通信、云计算、大数据之后计算和网络技术的又一创新。它是当下热门技术之一,也是比较深奥的一门学科。那么什么是区块链? ? 区块链(Bl...

    企鹅号小编
  • jquery 操作ajax 相关方法

    jQuery.get()   使用一个HTTP GET 请求从服务器加载数据。   jQuery.get(url [,data] [,success(dat...

    用户1197315

扫码关注云+社区

领取腾讯云代金券