您的位置:首页 > 理论基础 > 计算机网络

Dave Python 练习十八 -- 网络编程

2011-09-19 11:10 316 查看
#encoding=utf-8

###***************  网络编程  ***************

#**********  Part 1: 套接字:通讯端点  *******************

### 1.1 套接字
#套接字起源于20 世纪70 年代加利福尼亚大学伯克利分校版本的Unix,即人们所说的BSD Unix。
#因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同
#一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或IPC。套接字有两种,分别是基于文
#件型的和基于网络型的。

#Unix 套接字是我们要介绍的第一个套接字家族。其“家族名”为AF_UNIX(在POSIX1.g 标准中
#也叫AF_LOCAL),表示“地址家族:UNIX”。包括Python 在内的大多数流行平台上都使用术语“地址
#家族”及其缩写“AF”。而老一点的系统中,地址家族被称为“域”或“协议家族”,并使用缩写“PF”
#而不是“AF”。同样的,AF_LOCAL(在2000-2001 年被列为标准)将会代替AF_UNIX。不过,为了向后
#兼容,很多系统上,两者是等价的。Python 自己则仍然使用AF_UNIX。

#另一种套接字是基于网络的,它有自己的家族名字:AF_INET,或叫“地址家族:Internet”。
#还有一种地址家族AF_INET6 被用于网际协议第6 版(IPv6)寻址上。还有一些其它的地址家族,所有地址家族中,AF_INET 是使用最广泛的一个。
#
#Python 2.5 中加入了一种Linux 套接字的支持:AF_NETLINK套接字家族让用户代码与内核代码之间的IPC 可以使用标准BSD 套
#接字接口。而且,相对之前那些往操作系统中加入新的系统调用,proc 文件系统支持或是“IOCTL”等笨重的方案来说,这种方法显得更为优美,更为安全。
#
#Python 只支持AF_UNIX,AF_NETLINK,和AF_INET 家族。

## 1.2 套接字地址:主机与端口
#如果把套接字比做电话的插口——即通讯的最底层结构,那主机与端口就像区号与电话号码的
#一对组合。有了能打电话的硬件还不够,你还要知道你要打给谁,往哪打。一个Internet 地址由网
#络通讯所必需的主机与端口组成。而且不用说,另一端一定要有人在听才可以。否则,你就会听到
#熟悉的声音“对不起,您拨的是空号,请查对后再播”。你在上网的时候,可能也见过类似的情况,
#如“不能连接该服务器。服务器无响应或不可达”。

#合法的端口号范围为0 到65535。其中,小于1024 的端口号为系统保留端口。如果你所使用的
#是Unix 操作系统,保留的端口号(及其对应的服务/协议和套接字类型)可以通过/etc/services
#文件获得。

## 1.3 面向连接与无连接
##1.3.1 面向连接
#无论你使用哪一种地址家族。套接字的类型只有两种。一种是面向连接的套接字,即在通讯之
#前一定要建立一条连接,就像跟朋友打电话时那样。这种通讯方式也被称为“虚电路”或“流套接
#字”。面向连接的通讯方式提供了顺序的,可靠的,不会重复的数据传输,而且也不会被加上数据边
#界。这也意味着,每一个要发送的信息,可能会被拆分成多份,每一份都会不多不少地正确到达目
#的地。然后被重新按顺序拼装起来,传给正在等待的应用程序。
#实现这种连接的主要协议就是传输控制协议(即TCP)。要创建TCP 套接字就得在创建的时候,
#指定套接字类型为SOCK_STREAM。TCP 套接字采用SOCK_STREAM 这个名字,表达了它做为流套接字的
#特点。由于这些套接字使用Internet 协议(IP)来查找网络中的主机,这样形成的整个系统,一般
#会由这两个协议(TCP 和IP)来提及,即TCP/IP。

## 1.3.2 无连接
#与虚电路完全相反的是数据报型的无连接套接字。这意味着,无需建立连接就可以进行通讯。
#但这时,数据到达的顺序,可靠性及数据不重复性就无法保证了。数据报会保留数据边界,这就表
#示,数据不会像面向连接的协议那样被拆分成小块。
#使用数据报来传输数据就像邮政服务一样。邮件和包裹不一定会按它们发送的顺序到达。事实
#上,它们还有可能根本到不了!而且,由于网络的复杂性,数据还可能被重复传送。
#既然数据报有这么多缺点,为什么还要使用它呢?由于
#面向连接套接字要提供一些保证,以及要维持虚电路连接,这都是很重的额外负担。数据报没有这
#些负担,所以它更“便宜”。通常能提供更好的性能,更适合某些应用场合。

#实现这种连接的主要协议就是用户数据报协议(即UDP)。要创建UDP 套接字就得在创建的时候,
#指定套接字类型为SOCK_DGRAM。SOCK_DGRAM 这个名字,也许你已经猜到了,来自于单词“datagram”
#(“数据报”)。由于这些套接字使用Internet 协议来查找网络中的主机,这样形成的整个系统,一
#般会由这两个协议(UDP 和IP)来提及,即UDP/IP。

#**********  Part 2: Python 中的网络编程  *******************
## 2.1 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 的套接字,你要这样调用socket.socket():
#tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#
#同样地,创建一个UDP/IP 的套接字,你要这样:
#udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#
#由于socket 模块中有太多的属性。我们在这里破例使用了'from module import *'语句。使用
#'from socket import *',我们就把socket 模块里的所有属性都带到我们的命名空间里了,这样能
#大幅减短我们的代码。
#tcpSock = socket(AF_INET, SOCK_STREAM)
#当我们创建了套接字对象后,所有的交互都将通过对该套接字对象的方法调用进行。

##2.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()               关闭套接字

####Blocking-Oriented Socket Methods
#s.setblocking()         设置套接字的阻塞与非阻塞模式
#s.settimeout()          设置阻塞套接字操作的超时时间
#s.gettimeout()          得到阻塞套接字操作的超时时间
#
####面向文件的套接字的函数
#s.fileno()              套接字的文件描述符
#s.makefile()            创建一个与该套接字关连的文件

#2.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 是一个面向连接的通讯系统,在TCP 服务器
#可以开始工作之前,要先完成一些设置。TCP 服务器必需要“监听”(进来的)连接,设置完成之后,
#服务器就可以进入无限循环了。
#
#一个简单的(单线程的)服务器会调用accept()函数等待连接的到来。默认情况下,accept()
#函数是阻塞式的,即程序在连接到来之前会处于挂起状态。套接字也支持非阻塞模式。
#
#一旦接收到一个连接,accept()函数就会返回一个单独的客户的套接字用于后续的通讯。

#在临时套接字创建好之后,通讯就可以开始了。客户与服务器都使用这个新创建的套接字进行
#数据的发送与接收,直到通讯的某一方关闭了连接或发送了一个空字符串之后,通讯就结束了。

#在代码中,当客户连接关闭后,服务器继续等待下一个客户的连接。代码的最后一行,会把服
#务器的套接字关闭。由于服务器处在无限循环中,不可能会走到这一步,所以,这一步是可选的。
#我们写这一句话的主要目的是要提醒读者,在设计一个更智能的退出方案的时候,比方说,服务器
#被通知要关闭的时,要确保close()函数会被调用。

#示例:tsTserv.py 创建一个TCP 服务器程序,当客户端连接上来之后返回'hello dave'

#encoding=utf-8

#from socket import *
#from time import ctime
#
#HOST = ''   #HOST 变量为空,表示bind()函数可以绑定在所有有效的地址上。
#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(b'Hello Dave') #注意在Python 3.x 版本这里只能send bytes类型
#
#tcpCliSock.close()
#tcpSerSock.close()

## 2.4 创建TCP 客户端

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

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

#tcTlnt.py 的代码。程序连接到服务器,向服务器发送‘hello world’,服务器返回‘hello dave’
#from socket import *
#
#HOST = 'localhost'  #服务器的主机名与端口号
#PORT = 21567    #端口号要与服务器上的设置完全相同
#BUFSIZ = 1024
#ADDR = (HOST, PORT)
#
#tcpCliSock = socket(AF_INET, SOCK_STREAM)
#tcpCliSock.connect(ADDR)
#
#while True:
#    tcpCliSock.send(b'Hello world')
#    data = tcpCliSock.recv(BUFSIZ)
#
#    if not data:
#        break
#    print(data)
#
#tcpCliSock.close()

## 2.6 创建一个UDP 服务器
#由于UDP 服务器不是面向连接的,所以不用像TCP 服务器那样做那么多设置工作。事实上,并不用设置什么东西,直接等待进来的连接就好了。
#ss = socket() # 创建一个服务器套接字
#ss.bind() # 绑定服务器套接字
#inf_loop: # 服务器无限循环
#cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)
#ss.close() # 关闭服务器套接字
#
#从伪代码中可以看出,使用的还是那套先创建套接字然后绑定到本地地址(主机/端口对)的方法。
#无限循环中包含了从客户那接收消息,返回加了时间戳的结果和回去等下一个消息这三步。
#同样的,由于代码不会跳出无限循环,所以,close()函数调用是可选的。我们写这一句话的原因是
#要提醒读者,在设计一个更智能的退出方案的时候,要确保close()函数会被调用。

##UDP 时间戳服务器 (tsUserv.py)

#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(ctime().encode('utf-8'), addr)  #这里注意在Python 3里sendto 需要用bytes 进行传送,所以需要使用encode进行转换
#    print('...received from and returned to:', addr)
#
#udpSerSock.close()

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

## UDP 客户端,程序会提示用户输入要传给服务器的信息,显示服务器返回的加了时间戳的结果。

#from socket import *
#
#HOST = 'localhost'
#PORT = 21567
#BUFSIZ = 1024
#ADDR = (HOST, PORT)
#
#udpCliSock = socket(AF_INET, SOCK_DGRAM)
#
#while True:
#    data = input('> ')
#    if not data:
#        break
#    udpCliSock.sendto(data.encode('utf-8'), ADDR)  #这里注意在Python 3里sendto 需要用bytes 进行传送,所以需要使用encode进行转换
#    data, ADDR = udpCliSock.recvfrom(BUFSIZ)
#    if not data:
#        break
#    print(data)
#
#udpCliSock.close()

#先运行服务器,在运行客户端,提示输入data,然后服务器返回时间。

## 2.8 套接字模块属性

#socket 模块属性
#
#属性名字                            描述
#
####数据属性
#AF_UNIX, AF_INET, AF_INET6          Python 支持的套接字家族
#SO_STREAM, SO_DGRAM                 套接字类型 (TCP = 流, UDP = 数据报)
#has_ipv6                            表示是否支持IPv6 的标志变量
#
####异常
#error                   套接字相关错误
#herrora                 主机和地址相关的错误
#gaierror                地址相关的错误
#timeout                 超时
#
####函数
#socket()                用指定的地址家族,套接字类型和协议类型(可选)创建一个套接字对象
#socketpair()            用指定的地址家族,套接字类型和协议类型(可选)创建一对套接字对象
#fromfd()                用一个已经打开的文件描述符创建一个套接字对象
#
####数据属性
#ssl()                    在套接字初始化一个安全套接字层(SSL)。不做证书验证。
#getaddrinfo()            得到地址信息
#getfqdn()                返回完整的域的名字
#gethostname()           得到当前主机名
#gethostbyname()         由主机名得到对应的ip 地址
#gethostbyname_ex()      gethostbyname()的扩展版本,返回主机名,主机所有的别名和IP 地址列表。
#gethostbyaddr()         由IP 地址得到DNS 信息,返回一个类似gethostbyname_ex()的3 元组。
#getprotobyname()        由协议名(如'tcp')得到对应的号码。
#getservbyname()/        由服务名得到对应的端口号或相反
#getservbyport()         两个函数中,协议名都是可选的。
#ntohl()/ntohs()         把一个整数由网络字节序转为主机字节序
#htonl()/htons()         把一个整数由主机字节序转为网络字节序
#inet_aton()/            把IP 地址转为32 位整型,以及反向函数。(仅对IPv4 地址有效)
#inet_ntoa()
#inet_pton()/            把IP 地址转为二进制格式以及反向函数。(仅对IPv4 地址有效)
#inet_ntop()
#getdefaulttimeout()/    得到/设置默认的套接字超时时间,单位秒(浮点数)
#setdefaulttimeout()


-------------------------------------------------------------------------------------------------------
Blog: http://blog.csdn.net/tianlesoftware Weibo: http://weibo.com/tianlesoftware Email: dvd.dba@gmail.com
DBA1 群:62697716(满); DBA2 群:62697977(满) DBA3 群:62697850(满)
DBA 超级群:63306533(满); DBA4 群: 83829929(满) DBA5群: 142216823(满)
DBA6 群:158654907(满) DBA7 群:69087192(满) DBA8 群:172855474
DBA 超级群2:151508914 DBA9群:102954821 聊天 群:40132017(满)
--加群需要在备注说明Oracle表空间和数据文件的关系,否则拒绝申请
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: