设为首页 收藏本站
查看: 931|回复: 0

[经验分享] Foundations of Python Network Programming

[复制链接]

尚未签到

发表于 2015-4-24 05:20:48 | 显示全部楼层 |阅读模式
以前,人们热衷于如何将两台机器互相连接,许多连接的方法在今天已经过时,还有很多方法沿用至今。TCP/IP就是之一,可以说,TCP/IP协议是当今使用范围最广的协议,这本书所有的内容都是基于TCP/IP的。TCP/IP的数据传输层是TCP和UDP,我们通过TCP和UDP连接远程机器时,只需要远程机器的IP和端口号,然后建立连接传输数据。其中TCP和UDP又有着许多不同之处。
何时使用TCP?
    1. 你需要确保传输的数据准确的到达并且保持完整。
    2. 你需要发送大量的数据,而不是简单的请求和返回。
    3. 你能忍受建立连接时消耗的时间。(效率低)
何时使用UDP?
    1. 你不关心你发送的包是否准确的到达,或者你能自己处理这些问题。(不稳定)
    2. 你只是希望得到一个简单的请求和返回。
    3. 你需要快速的建立连接。(效率高)
    4. 你发送的数据量不是很大。UDP限制每个包不能超过64KB,通常人们使用UDP时只使用了低于1KB。
在Python中建立一个TCP或UDP连接是一件非常简单的事情,需要使用Socket模块,这是Python的标准模块。
客户端(Network Clients)
1. 创建一个socket对象

DSC0000.gif s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)第一个参数socket.AF_INET说明我们使用的是IPv4,第二个参数socket.SOCK_STREAM指的是我们使用TCP进行数据传输,如果要使用UDP,则使用socket.SOCK_DGRAM,如:

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)2. connect连接远程服务器

s.connect(("www.example.com", 80))连接远程服务器需要远程服务器的IP和端口,注意到上面我们使用了服务器的域名也是可以的,因为Python为我们做了DNS的解析。同时,注意到connect的参数是一个tuple。
我们上面连接的是一个http站点,默认端口是80,我们可以通过下面的方法获取到默认的端口号:

port = socket.getservbyname('http', 'tcp')相应的,你可以查询诸如:smtp,ftp等等端口号。
3. 连接后,从一个socket对象获取信息
比如,获取本机的IP地址和端口号,获取远程机器的IP地址和端口号,如:

#!/usr/bin/env python
# Information Example - Chapter 2

import socket

print "Creating socket DSC0001.gif ",
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "done."

print "Looking up port number",
port = socket.getservbyname('http', 'tcp')
print "done."

print "Connecting to remote host on port %d" % port,
s.connect(("www.google.com", port))
print "done."

print "Connected from", s.getsockname()
print "Connected to", s.getpeername()输出结果会显示:
Creating socket... done.
Looking up port number... done.
Connecting to remote host on port 80... done.
Connected from ('192.168.XX.XX', 2548)
Connected to ('64.233.189.104', 80)
可以看到,我的本机使用的是一个随机的端口号(2548),每次执行端口号都会不同。
4. File-like 对象
我们可以通过Socket对象来执行一些比如发送(send(), sendto()),接收数据的操作(recv(), recvfrom()),同时,我们还可以把Socket对象转换为一个类似文件的对象(File-like Object),然后使用其中的write()来发送数据,read(), readline()来接收数据。
File-like对象更适合TCP连接,因为TCP连接必须保证数据流能够完整正确的到达,数据流表现的更像是一个文件。而UDP却不是,它是一个基于包的连接,它只管把这些包发送出去,如果使用File-like对象来处理,将很难追踪定位出现的错误。生成一个File-like对象通过下面的语句:

fd = s.makefile('rw', 0) #s 是前面的创建的socket对象,rw表示可读和可写权限然后,就可以调用fd的write(), readines()等方法了。例子如下,同时注意细节的错误处理,这里不详细介绍:

#!/usr/bin/env python
# Error Handling Example With Shutdown and File-Like Objects - Chapter 2

import socket, sys, time

host = sys.argv[1]
textport = sys.argv[2]
filename = sys.argv[3]

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error, e:
    print "Strange error creating socket: %s" % e
    sys.exit(1)

# Try parsing it as a numeric port number.

try:
    port = int(textport)
except ValueError:
    # That didn't work.  Look it up instread.
    try:
        port = socket.getservbyname(textport, 'tcp')
    except socket.error, e:
        print "Couldn't find your port: %s" % e
        sys.exit(1)

try:
    s.connect((host, port))
except socket.gaierror, e:
    print "Address-related error connecting to server: %s" % e
    sys.exit(1)
except socket.error, e:
    print "Connection error: %s" % e
    sys.exit(1)

fd = s.makefile('rw', 0)

print "sleeping"
time.sleep(10)
print "Continuing."

try:
    fd.write("GET %s HTTP/1.0\r\n\r\n" % filename)
except socket.error, e:
    print "Error sending data: %s" % e
    sys.exit(1)

try:
    fd.flush()
except socket.error, e:
    print "Error sending data (detected by flush): %s" % e
    sys.exit(1)

try:
    s.shutdown(1)
except socket.error, e:
    print "Error sending data (detected by shutdown): %s" % e
    sys.exit(1)

while 1:
    try:
        buf = fd.read(2048)
    except socket.error, e:
        print "Error receiving data: %s" % e
        sys.exit(1)
    if not len(buf):
        break
    sys.stdout.write(buf)
注意上面在我们发送了数据之后,使用了shutdown方法,是为了保证发送的数据成功到达目标机器。因为shutdown()会等待,直到接收到一个准确的退出代码。
服务器端(Network Server)
通过TCP创建一个服务端可以总结为如下四个步骤:
1. 创建一个socket对象。(create socket object)
2. 设置socket对象的属性。(set options)
3. 绑定一个端口。(bind to a port)
4. 监听来自客户端的连接。(listen for connection)
针对上面的四个步骤,下面是一个最简单的实现:

host = ''    #接受来自任何端口的连接
port = 51423

#第一步,创建一个socket对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#第二步,设置socket属性
s.setsockopt(socket.SOL_SOCKET, socket.SO_RESUSEADDR, 1)

#第三步,绑定一个端口
s.bind((host, port))

#第四步,监听来自客户端的连接
s.listen(5)     #参数5表示同时监听5个连接
通过UDP创建一个服务端步骤也差不多,创建一个socket,设置option,bind端口,然而,UDP不需要listen()和accept(),而是使用recvfrom()就足够了。recvfrom()函数返回两个信息:接受的数据(data)和客户端的地址(address)和端口(port)。下面是UDP服务端的例子:

#!/usr/bin/env python
# UDP Echo Server - Chapter 3 - udpechoserver.py
import socket, traceback

host = ''                               # Bind to all interfaces
port = 51423

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))

while 1:
    try:
        message, address = s.recvfrom(8192)
        print "Got data from", address
        # Echo it back
        s.sendto(message, address)
    except (KeyboardInterrupt, SystemExit):
        raise
    except:
        traceback.print_exc()
Domain Name System(DNS)
我们能很轻松的记住博客园的域名,却基本上很难说出它的IP地址来,因为DNS为我们解析了域名。
socket.getaddrinfo()根据主机名或域名等来获取相应的信息。

socket.getaddrinfo(host, port[, family[, socktype[, proto[, flags]]]]))
返回值是一个tuple的列表,每个tuple返回如下信息:

(family, socktype, proto, canonname, sockaddr)
同时,gethostbyaddr()根据IP地址获取相应的信息,同时使用getaddrinfo()和gethostbyaddr()可以实现对域名的双重验证。如下面的例子:

import sys, socket

def getipaddrs(hostname):
    """Get a list of IP addresses from a given hostname.  This is a standard
    (forward) lookup."""
    result = socket.getaddrinfo(hostname, None, 0, socket.SOCK_STREAM)
    return [x[4][0] for x in result]

def gethostname(ipaddr):
    """Get the hostname from a given IP address.  This is a reverse
    lookup."""
    return socket.gethostbyaddr(ipaddr)[0]

try:
    # First, do the reverse lookup and get the hostname.
    hostname = gethostname(sys.argv[1]) # could raise socket.herror

    # Now, do a forward lookup on the result from the earlier reverse
    # lookup.
    ipaddrs = getipaddrs(hostname)      # could raise socket.gaierror
except socket.herror, e:
    print "No host names available for %s; this may be normal." % sys.argv[1]
    sys.exit(0)
except socket.gaierror, e:
    print "Got hostname %s, but it could not be forward-resolved: %s" % \
          (hostname, str(e))
    sys.exit(1)

# If the forward lookup did not yield the original IP address anywhere,
# someone is playing tricks.  Explain the situation and exit.
if not sys.argv[1] in ipaddrs:
    print "Got hostname %s, but on forward lookup," % hostname
    print "original IP %s did not appear in IP address list." % sys.argv[1]
    sys.exit(1)

# Otherwise, show the validated hostname.
print "Validated hostname:", hostname
OK,第一部分就到这里。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-60006-1-1.html 上篇帖子: 译:selenium webdriver (python) 下篇帖子: Python天天美味(11)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表