Python网络编程的一些代码片断与分析

来源:互联网 发布:涉密网络 编辑:程序博客网 时间:2024/05/21 08:01

【转】 Python网络编程的一些代码片断与分析  

2011-06-02 16:46:41|  分类: 计算机-Python|字号 订阅

转载自 Arbow
最终编辑 Arbow

平时在工作中都是用Java搞网络编程,而C的网络编程几乎没有写过。但是我们可以在Python中实现类似C那样的网络编程,毕竟在Python中,很多库都是对c库,unix库的简单封装。【转】 Python网络编程的一些代码片断与分析 - JiangZX - 江志祥的博客
p.s 本文随时会补充,使用rss的童子们要注意了
另外,本文的代码基本来自于 http://blog.chinaunix.net/u/19742/article_66836.html ,特此声明。
1. Socket Server
没什么好说的,跟C一个样,特别是Socket的参数。。。
#!/usr/bin/env python
#coding=utf-8
import socket, struct
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_SOCKET, socket.TCP_NODELAY, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', False, 0))
s.bind(("localhost", 8080))
s.listen(10)
print "Hostname is", socket.gethostname()
while(1):
    client_sock, client_addr = s.accept()
    print "Client", client_sock.getpeername(), " connected, address is", client_addr
    client_sock.close()
要注意的是 socket.SO_LINGER 这个参数,需要用 struct.pack 将两个值转化为整数。
这个Server接收1个客户端连接,输出信息后关闭。若在accept后用线程处理,加入读写处理逻辑,这样就是一个最简单的网络服务器了。
不过,考虑到Python中的GIL,那个假的Thread,我可是宁愿 os.fork() 一个子进程来处理读写,也比用啥Thread好。
不过子进程阿,线程之类的还是落伍了,有一些好的IO复用方式可以实现非阻塞的网络编程。
2. Select
Select是比较‘落伍'的模型了,不过对于1024个连接以下的普通服务器,用这个也没有什么问题。对于LuaSocket这种只支持Ansi C的玩意,就只能用select了。
#!/usr/bin/env python
#coding=utf-8
"""
非阻塞socket的使用(此程序在ubuntu linux和windows xp上测试,Windows可以支持select.select)
监控socket的三个list:in/out/err
程序以5000ms的时间长度为间隔,如果有客户端的请求,接收连接并进行显示;如果没有的话,
每隔5000ms显示一次"no data coming"
"""
import socket,select
host = ""
port = 5000
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((host,port))
s.listen(5)
while 1:
    infds,outfds,errfds = select.select([s,],[],[],5)
    # 如果infds状态改变,进行处理,否则不予理会
    if len(infds) != 0:
        clientsock,clientaddr = s.accept()
        buf = clientsock.recv(8196)
        if len(buf) != 0:
            print "Socket from ", clientaddr, " send ", buf
        clientsock.close()
    print "no data coming"
这段代码省略了一些socket参数设置,主要关注点在于while 1后面的逻辑上。将句柄放到select函数的列表参数中,就可以一个线程内阻塞获取所有句柄的事件了。我觉得这样挺好的,比起啥线程处理靠谱。
3. Poll
Select默认只能支持1024个句柄,要更多的并发连接,就要靠poll了。poll相比select复杂了些,有个监听事件的注册。
#!/usr/bin/env python
#coding=utf-8
"""
非阻塞socket的使用(此程序在ubuntu linux上测试,Windows没有select.poll)
监控socket的三个状态:IN/ERR/HUP
程序以5000ms的时间长度为间隔,如果有客户端的请求,接收连接并进行显示;如果没有的话,
每隔5000ms显示一次"no data coming"
"""
import socket,select, sys
host = ""
port = 5000
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((host,port))
s.listen(5)
# 创建poll对象,仅用于unix-like系统
p = select.poll()
p.register(s.fileno(),select.POLLIN|select.POLLERR|select.POLLHUP)
while 1:
    results = p.poll(5000)
    # 如果有结果返回,进行处理,否则不予理会
    if len(results) != 0:
        if results[0][1] == select.POLLIN:
            clientsock,clientaddr = s.accept()
            buf = clientsock.recv(8196)
            if len(buf) != 0:
                print "Socket from ", clientaddr, " send ", buf
            clientsock.close()
    print "no data coming"
不过在poll了一堆结果之后,处理跟select还是差不多的,属于啥类型的事件就执行啥逻辑。这里特别要注意select类型的选择,Eurasia3里面就做得很好,参考 http://eurasia.googlecode.com/svn/trunk/src/eurasia/web.py :
读事件:POLLIN|POLLPRI
写事件:POLLOUT
错误事件:POLLERR|POLLHUP|POLLNVAL
4. EPoll
Poll在C10K这样的场景下很吃力,这时候就需要epoll了。在Java下面,只能选择SUN JDK5 update10以上的版本,要是还在用JDK1.4,只能干瞪眼。幸好,Python中,我们只需要很少的步骤就可以让服务器使用牛X的epoll模型:
先到 http://sourceforge.net/projects/pyepoll/ 下载pyepoll库,安装
在上面的例子代码中加入
import epoll
for attr in ['poll', 'error', 'POLLIN', 'POLLPRI', 'POLLOUT', 'POLLERR', 'POLLHUP', 'POLLNVAL']:
    setattr(select, attr, getattr(epoll, attr))
我靠,就是这样简单! 此外,还可以使用 libevent 或者 libev 的python库,它们也使用epoll!
5. 半关闭
不懂半关闭的自己搜索去。常见场景是使用 echo -ne "Some text" | nc host port 的时候,echo的文本发送给服务器后,会关闭客户端socket的写通道,只保留读通道。这个时候,如果是貌似牛X的JavaNIO,就会报一个网络连 接已关闭的SB错误。Python和Erlang都不存在这种问题:
这是服务器端的例子:
"""
半开连接服务器:创建的每个socket只允许进行一次读取操作
"""
import socket
host = ""
port = 5000
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((host,port))
s.listen(5)
while 1:
    clientsock,clientaddr = s.accept()
    buf = clientsock.recv(8196)
    # 关闭读(接收)操作
    clientsock.shutdown(socket.SHUT_RD)
    if len(buf) != 0:
        print str(clientaddr),buf
    clientsock.send("OK\r\n");
    clientsock.close()
    
6. 管道
Python里面,不仅仅是网络连接句柄,子进程句柄,文件句柄都可以用 select,poll的。
#!/usr/bin/env python
#coding=utf-8
import select, sys, subprocess
vmstat_pipe = subprocess.Popen('vmstat 1', shell=True, bufsize=1024, 
stdout=subprocess.PIPE).stdout
vmstat_pipe = subprocess.Popen('iostat 2', shell=True, bufsize=1024, 
stdout=subprocess.PIPE).stdout
while 1:
    infds,outfds,errfds = select.select([vmstat_pipe,vmstat_pipe],[],[],1)
    if len(infds) != 0:
        for p in infds:
            print "Get ", p.readline(), "from pipe", p
因此在Python中,主线程内完成客户端Socket的逻辑和后段子进程管道的读取,是很easy的事情。
7. 广播
#!/usr/bin/env python
#coding=utf-8
"""
广播服务器,接收来自广播的消息,并进行应答
设置socket选项:SO_BROADCAST
"""
import socket
host = ""
port = 50000
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1)
s.bind((host,port))
while 1:
    message,address = s.recvfrom(8192)
    print "Got data from",address
    s.sendto("I am here",address)
s.close()
这种UDP包的广播没啥特别的,就是SO_BROADCAST参数设置而已。
简单介绍了下常见的一些网络编程代码,更多可以到文中提到的作者blog上面看。这些代码只能算是Demo级别,有空我会写一下靠谱的网络服务器代码分析,比如 eurasia3 的 web.py。
原创粉丝点击