1、socket简介

在开始python网络编程之前,首先要了解一下套接字,套接字是一种计算机网络数据结构。套接字有两种,分别是基于文件型的和基于网络型的,前者如AF_UNIX,后者如AF_INET、AF_NETLINK。套接字端口就像电话号码一样,是通讯身份的唯一标识,合法的端口号范围为0到65535,其中,小于1024的端口号为系统保留端口,如果是Unix操作系统,保留的端口号使用可以通过/etc/services文件获得。

套接字的类型有两种,面向连接的套接字与无连接的套接字。前者提供了顺序的、可靠的、不会重复的数据传输,而且也不会加上数据边界,每一个要发送的信息,可能会被拆成多份,实现这种连接的主要协议就是传输控制协议TCP,套接字类型为SOCK_STREAM,使用IP来查找网络中的主机,这样就形成了整个系统。无连接的套接字,数据到达的顺序、可靠性及数据不重复性无法保证,数据报会保留数据边界,发送消息时不会拆成小块,虽有这么多缺点,但正是无连接的,所以不需要承担维持虚电路连接的负担,性能更好,实现这种连接的主要协议是用户数据报协议UDP,套接字类型为SOCK_DGRAM,也是通过IP来查找网络中的主机。

2、python socket

python网络编程使用socket模块,创建套接字用其socket()函数,语法如下:

socket(socket_family, socket_type, protocol = 0)

socket_family可以是AF_UNIX或AF_INET,socket_type可以是SOCK_STREAM或SOCK_DGRAM,protocol一般不写,默认为0。

当我们创建了套接字以后,所有的交互都将通过对该套接字对象的方法调用进行,下面列举一些套接字对象的常用函数。

服务器端——

bind():绑定地址到套接字。
listen():开始TCP监听。
accept():被动接受TCP客户的连接,阻塞式等待连接的到来。

客户端——

connect():主动初始化TCP服务器连接。
connect_ex():上一个函数的扩展版本,出错时返回出错码,而不是抛出异常。

公共用途——

recv():接收TCP数据。
send():发送TCP数据。
sendall():完整发送TCP数据。
recvfrom():接收UDP数据。
sendto():发送UDP数据。
getpeername():连接到当前套接字的远端的地址。
getsockname():当前套接字的地址。
getsockopt():返回指定套接字的参数。
setsockopt():设置指定套接字的参数。
close():关闭套接字。

阻塞相关——

setblocking():设置套接字的阻塞与非阻塞模式。
settimeout():设置阻塞套接字操作的超时时间。
gettimetout():得到阻塞套接字操作的超时时间。

面向文件——

fileno():套接字的文件描述符。
makefile():创建一个与该套接字关联的文件。

3、TCP

下面是TCP服务器的伪代码——

ss = socket()#创建服务器套接字
ss.bind()#把地址绑定到套接字上
ss.listen()#监听连接
inf_loop:#服务器无限循环
    cs = ss.accept()#接收客户的连接
    comm_loop:#通讯循环
        cs.recv()/cs.send()#对话,接收与发送
    cs.close()#关闭客户套接字
ss.close()#关闭服务器套接字,可选

下面是TCP客户端的伪代码——

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

下面以一个简单的例子作为介绍,服务器、客户端在同一台机子上,服务器充当时间戳的角色,客户端发送数据后返回一个带时间的相同数据。

TimeServer.py——

#!/usr/bin/env python 

from socket import * 
from time import ctime 

HOST = '' # any available 
PORT = 21567 # unused 
BUFSIZE = 1024 
ADDR = (HOST, PORT) 

tcpSerSock = socket(AF_INET, SOCK_STREAM) 
tcpSerSock.bind(ADDR) 
tcpSerSock.listen(5) # max concurrence access 

while True: 
    print 'waiting for connection...' 
    tcpCliSock, addr = tcpSerSock.accept() # blocking and passive 
    print '...connected from: ', addr 

    while True: 
        data = tcpCliSock.recv(BUFSIZE) 
        if not data: 
            break 
        tcpCliSock.send('[%s] %s' %(ctime(), data))

    tcpCliSock.close() 

tcpSerSock.close() # optional and never exec

TimeClient.py——

#!/usr/bin/env python 

from socket import * 

HOST = 'localhost' # local host 
PORT = 21567 # the same port as the server 
BUFSIZE = 1024 
ADDR = (HOST, PORT) 

pCliSock = socket(AF_INET, SOCK_STREAM) 
tcpCliSock.connect(ADDR) 

while True: 
    data = raw_input('> ') 
    if not data: 
        break 
    tcpCliSock.send(data) 
    data = tcpCliSock.recv(BUFSIZE) 
    if not data: 
        break 
    print data 

tcpCliSock.close()

4、UDP

UDP相对TCP来说比较简单,因为是无连接的,所以服务端不需要listen和accept,客户端也不需要connect。

下面是UDP服务器的伪代码——

ss = socket()
ss.bind()#把地址绑定到套接字上
inf_loop:#服务器无限循环
    cs.recvfrom()/cs.sendto()#对话,接收与发送
ss.close()#关闭服务器套接字,可选

下面是UDP客户端的伪代码——

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

SocketServer模块是一个基于socket模块的高级别的套接字通讯模块,它支持在新的线程或进程中处理客户的请求。这个模块是标准库中一个高级别的模块,用于简化网络客户与服务器的实现,模块中已经实现了一些可供使用的类。下面用SocketServer实现上面的例子,即时间戳服务器。

timeSSSer.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 handler(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()

timeSSCli.py——

#!/usr/bin/env python 

from socket import * 

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

while True: 
    tcpCliSock = socket(AF_INET, SOCK_STREAM) # socket every time 
    tcpCliSock.connect(ADDR) 
    data = raw_input('> ') 
    if not data: 
        break 
    tcpCliSock.send('%s\r\n' %data) 
    data = tcpCliSock.recv(BUFSIZE) 
    if not data: 
        break 
    print data.strip() 
    tcpCliSock.close()

5、其它模块

twisted模块是一个完全事件驱动的网络框架,可以用来开发完全异布的网络应用程序和协议,创建一个完整的系统,系统中可以有网络协议、线程、安全认证、即时通讯、数据库管理、网页、电子邮件、命令行参数、图形界面集成等。下面使用twisted模块实现上面例子中同样的功能。

timeTSSer.py——

#!/usr/bin/env python 

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

PORT = 21567 

class TSServProtocol(protocol.Protocol): 
    def connectionMade(self): 
        self.clnt = self.transport.getPeer().host 
        print '...connected from:', self.clnt 
    def dataReceived(self, data): 
        print '...received from [%s]\n\t%s' %(self.clnt, data) 
        self.transport.write('[%s] %s' %(ctime(), data)) 

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

timeTSCli.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() 

factory = protocol.ClientFactory() 
factory.protocol = TSClntProtocol 
reactor.connectTCP(HOST, PORT, factory) 
reactor.run()

asyncore/asynchat模块,为能异步处理客户请求的网络应用程序提供底层功能。

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