当前位置: 首页 > 编程日记 > 正文

python 之socket 网络编程

socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

#!/usr/bin/env python
# -*- coding:utf-8 -*-import socketip_port = ('127.0.0.1',9999)sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)while True:print 'server waiting...'conn,addr = sk.accept()client_data = conn.recv(1024)print client_dataconn.sendall('不要回答,不要回答,不要回答')conn.close()socket server

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
ip_port = ('127.0.0.1',9999)sk = socket.socket()
sk.connect(ip_port)sk.sendall('请求占领地球')server_reply = sk.recv(1024)
print server_replysk.close()socket client

web服务

#!/usr/bin/env python
#coding:utf-8
import socketdef handle_request(client):buf = client.recv(1024)client.send("HTTP/1.1 200 OK\r\n\r\n")client.send("Hello, World")def main():sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.bind(('localhost',8080))sock.listen(5)while True:connection, address = sock.accept()handle_request(connection)connection.close()if __name__ == '__main__':main()

sk.bind(address)

s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。

sk.listen(backlog)

开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。

backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
      这个值不能无限大,因为要在内核中维护连接队列

sk.setblocking(bool)

是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。

sk.accept()

接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。

接收TCP 客户的连接(阻塞式)等待连接的到来

sk.connect(address)

连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

sk.connect_ex(address)

同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061

sk.close()

关闭套接字

sk.recv(bufsize[,flag])

接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。

sk.recvfrom(bufsize[.flag])

与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

sk.send(string[,flag])

将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。

sk.sendall(string[,flag])

将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。

内部通过递归调用send,将所有内容发送出去。

sk.sendto(string[,flag],address)

将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。

sk.settimeout(timeout)

设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )

sk.getpeername()

返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。

sk.getsockname()

返回套接字自己的地址。通常是一个元组(ipaddr,port)

sk.fileno()

套接字的文件描述符

import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
sk.bind(ip_port)while True:data = sk.recv(1024)print dataimport socket
ip_port = ('127.0.0.1',9999)sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
while True:inp = raw_input('数据:').strip()if inp == 'exit':breaksk.sendto(inp,ip_port)sk.close()UDP Demo

实例:智能机器人

#!/usr/bin/env python
# -*- coding:utf-8 -*-import socketip_port = ('127.0.0.1',8888)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)while True:conn,address =  sk.accept()conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')Flag = Truewhile Flag:data = conn.recv(1024)if data == 'exit':Flag = Falseelif data == '0':conn.sendall('通过可能会被录音.balabala一大推')else:conn.sendall('请重新输入.')conn.close()服务端

#!/usr/bin/env python
# -*- coding:utf-8 -*-import socketip_port = ('127.0.0.1',8005)
sk = socket.socket()
sk.connect(ip_port)
sk.settimeout(5)while True:data = sk.recv(1024)print 'receive:',datainp = raw_input('please input:')sk.sendall(inp)if inp == 'exit':breaksk.close()客户端

IO多路复用

I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

Linux

Linux中的 select,poll,epoll 都是IO多路复用的机制。

Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用。

Windows Python:提供: select
Mac Python:提供: select
Linux Python:提供: select、poll、epoll

注意:网络操作、文件操作、终端操作等均属于IO操作,对于windows只支持Socket操作,其他系统支持其他IO操作,但是无法检测 普通文件操作 自动上次读取是否已经变化。

对于select方法:

句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间)参数: 可接受四个参数(前三个必须)
返回值:三个列表select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。
1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中
2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中
3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中
4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。

#!/usr/bin/env python
# -*- coding:utf-8 -*-import socket
import selectsk1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sk1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sk1.bind(('127.0.0.1',8002))
sk1.listen(5)
sk1.setblocking(0)inputs = [sk1,]while True:readable_list, writeable_list, error_list = select.select(inputs, [], inputs, 1)for r in readable_list:# 当客户端第一次连接服务端时if sk1 == r:print 'accept'request, address = r.accept()request.setblocking(0)inputs.append(request)# 当客户端连接上服务端之后,再次发送数据时else:received = r.recv(1024)# 当正常接收客户端发送的数据时if received:print 'received data:', received# 当客户端关闭程序时else:inputs.remove(r)sk1.close()利用select实现伪同时处理多个Socket客户端请求:服务端
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socketip_port = ('127.0.0.1',8002)
sk = socket.socket()
sk.connect(ip_port)while True:inp = raw_input('please input:')sk.sendall(inp)
sk.close()利用select实现伪同时处理多个Socket客户端请求:客户端

此处的Socket服务端相比与原生的Socket,他支持当某一个请求不再发送数据时,服务器端不会等待而是可以去处理其他请求的数据。

但是,如果每个请求的耗时比较长时,select版本的服务器端也无法完成同时操作。

#!/usr/bin/env python
#coding:utf8'''服务器的实现 采用select的方式
'''import select
import socket
import sys
import Queue#创建套接字并设置该套接字为非阻塞模式

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setblocking(0)#绑定套接字
server_address = ('localhost',10000)
print >>sys.stderr,'starting up on %s port %s'% server_address
server.bind(server_address)#将该socket变成服务模式
#backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
#这个值不能无限大,因为要在内核中维护连接队列

server.listen(5)#初始化读取数据的监听列表,最开始时希望从server这个套接字上读取数据
inputs = [server]#初始化写入数据的监听列表,最开始并没有客户端连接进来,所以列表为空

outputs = []#要发往客户端的数据
message_queues = {}
while inputs:print >>sys.stderr,'waiting for the next event'#调用select监听所有监听列表中的套接字,并将准备好的套接字加入到对应的列表中readable,writable,exceptional = select.select(inputs,outputs,inputs)#列表中的socket 套接字  如果是文件呢? #监控文件句柄有某一处发生了变化 可写 可读  异常属于Linux中的网络编程 #属于同步I/O操作,属于I/O复用模型的一种#rlist--等待到准备好读#wlist--等待到准备好写#xlist--等待到一种异常#处理可读取的套接字'''如果server这个套接字可读,则说明有新链接到来此时在server套接字上调用accept,生成一个与客户端通讯的套接字并将与客户端通讯的套接字加入inputs列表,下一次可以通过select检查连接是否可读然后在发往客户端的缓冲中加入一项,键名为:与客户端通讯的套接字,键值为空队列select系统调用是用来让我们的程序监视多个文件句柄(file descrīptor)的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有某一个或多个发生了状态改变''''''若可读的套接字不是server套接字,有两种情况:一种是有数据到来,另一种是链接断开如果有数据到来,先接收数据,然后将收到的数据填入往客户端的缓存区中的对应位置,最后将于客户端通讯的套接字加入到写数据的监听列表:如果套接字可读.但没有接收到数据,则说明客户端已经断开。这时需要关闭与客户端连接的套接字进行资源清理'''for s in readable: if s is server:connection,client_address = s.accept()print >>sys.stderr,'connection from',client_addressconnection.setblocking(0)#设置非阻塞
            inputs.append(connection)message_queues[connection] = Queue.Queue()else:data = s.recv(1024)if data:print >>sys.stderr,'received "%s" from %s'% \(data,s.getpeername())message_queues[s].put(data)if s not in outputs:outputs.append(s)else:print >>sys.stderr,'closing',client_addressif s in outputs:outputs.remove(s)inputs.remove(s)s.close()del message_queues[s]#处理可写的套接字'''在发送缓冲区中取出响应的数据,发往客户端。如果没有数据需要写,则将套接字从发送队列中移除,select中不再监视'''for s in writable:try:next_msg = message_queues[s].get_nowait()except Queue.Empty:print >>sys.stderr,'  ',s,getpeername(),'queue empty'outputs.remove(s)else:print >>sys.stderr,'sending "%s" to %s'% \(next_msg,s.getpeername())s.send(next_msg)#处理异常情况for s in exceptional:for s in exceptional:print >>sys.stderr,'exception condition on',s.getpeername()inputs.remove(s)if s in outputs:outputs.remove(s)s.close()del message_queues[s]基于select实现socket服务端

SocketServer模块

  SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求

连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。

ThreadingTCPServer

ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。

1、ThreadingTCPServer基础

使用ThreadingTCPServer:

  • 创建一个继承自 SocketServer.BaseRequestHandler 的类
  • 类中必须定义一个名称为 handle 的方法
  • 启动ThreadingTCPServer

2、ThreadingTCPServer源码剖析

ThreadingTCPServer的类图关系如下:

内部调用流程为:

  • 启动服务端程序
  • 执行 TCPServer.__init__ 方法,创建服务端Socket对象并绑定 IP 和 端口
  • 执行 BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给 self.RequestHandlerClass
  • 执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...
  • 当客户端连接到达服务器
  • 执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求
  • 执行 ThreadingMixIn.process_request_thread 方法
  • 执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass()  即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用 MyRequestHandler的handle方法)

ThreadingTCPServer相关源码:

class BaseServer:"""Base class for server classes.Methods for the caller:- __init__(server_address, RequestHandlerClass)- serve_forever(poll_interval=0.5)- shutdown()- handle_request()  # if you do not use serve_forever()- fileno() -> int   # for select()Methods that may be overridden:- server_bind()- server_activate()- get_request() -> request, client_address- handle_timeout()- verify_request(request, client_address)- server_close()- process_request(request, client_address)- shutdown_request(request)- close_request(request)- handle_error()Methods for derived classes:- finish_request(request, client_address)Class variables that may be overridden by derived classes orinstances:- timeout- address_family- socket_type- allow_reuse_addressInstance variables:- RequestHandlerClass- socket"""timeout = Nonedef __init__(self, server_address, RequestHandlerClass):"""Constructor.  May be extended, do not override."""self.server_address = server_addressself.RequestHandlerClass = RequestHandlerClassself.__is_shut_down = threading.Event()self.__shutdown_request = Falsedef server_activate(self):"""Called by constructor to activate the server.May be overridden."""passdef serve_forever(self, poll_interval=0.5):"""Handle one request at a time until shutdown.Polls for shutdown every poll_interval seconds. Ignoresself.timeout. If you need to do periodic tasks, do them inanother thread."""self.__is_shut_down.clear()try:while not self.__shutdown_request:# XXX: Consider using another file descriptor or# connecting to the socket to wake this up instead of# polling. Polling reduces our responsiveness to a# shutdown request and wastes cpu at all other times.r, w, e = _eintr_retry(select.select, [self], [], [],poll_interval)if self in r:self._handle_request_noblock()finally:self.__shutdown_request = Falseself.__is_shut_down.set()def shutdown(self):"""Stops the serve_forever loop.Blocks until the loop has finished. This must be called whileserve_forever() is running in another thread, or it willdeadlock."""self.__shutdown_request = Trueself.__is_shut_down.wait()# The distinction between handling, getting, processing and# finishing a request is fairly arbitrary.  Remember:#
    # - handle_request() is the top-level call.  It calls#   select, get_request(), verify_request() and process_request()# - get_request() is different for stream or datagram sockets# - process_request() is the place that may fork a new process#   or create a new thread to finish the request# - finish_request() instantiates the request handler class;#   this constructor will handle the request all by itselfdef handle_request(self):"""Handle one request, possibly blocking.Respects self.timeout."""# Support people who used socket.settimeout() to escape# handle_request before self.timeout was available.timeout = self.socket.gettimeout()if timeout is None:timeout = self.timeoutelif self.timeout is not None:timeout = min(timeout, self.timeout)fd_sets = _eintr_retry(select.select, [self], [], [], timeout)if not fd_sets[0]:self.handle_timeout()returnself._handle_request_noblock()def _handle_request_noblock(self):"""Handle one request, without blocking.I assume that select.select has returned that the socket isreadable before this function was called, so there should beno risk of blocking in get_request()."""try:request, client_address = self.get_request()except socket.error:returnif self.verify_request(request, client_address):try:self.process_request(request, client_address)except:self.handle_error(request, client_address)self.shutdown_request(request)def handle_timeout(self):"""Called if no new request arrives within self.timeout.Overridden by ForkingMixIn."""passdef verify_request(self, request, client_address):"""Verify the request.  May be overridden.Return True if we should proceed with this request."""return Truedef process_request(self, request, client_address):"""Call finish_request.Overridden by ForkingMixIn and ThreadingMixIn."""self.finish_request(request, client_address)self.shutdown_request(request)def server_close(self):"""Called to clean-up the server.May be overridden."""passdef finish_request(self, request, client_address):"""Finish one request by instantiating RequestHandlerClass."""self.RequestHandlerClass(request, client_address, self)def shutdown_request(self, request):"""Called to shutdown and close an individual request."""self.close_request(request)def close_request(self, request):"""Called to clean up an individual request."""passdef handle_error(self, request, client_address):"""Handle an error gracefully.  May be overridden.The default is to print a traceback and continue."""print '-'*40print 'Exception happened during processing of request from',print client_addressimport tracebacktraceback.print_exc() # XXX But this goes to stderr!print '-'*40BaseServer
BaseServer
class TCPServer(BaseServer):"""Base class for various socket-based server classes.Defaults to synchronous IP stream (i.e., TCP).Methods for the caller:- __init__(server_address, RequestHandlerClass, bind_and_activate=True)- serve_forever(poll_interval=0.5)- shutdown()- handle_request()  # if you don't use serve_forever()- fileno() -> int   # for select()Methods that may be overridden:- server_bind()- server_activate()- get_request() -> request, client_address- handle_timeout()- verify_request(request, client_address)- process_request(request, client_address)- shutdown_request(request)- close_request(request)- handle_error()Methods for derived classes:- finish_request(request, client_address)Class variables that may be overridden by derived classes orinstances:- timeout- address_family- socket_type- request_queue_size (only for stream sockets)- allow_reuse_addressInstance variables:- server_address- RequestHandlerClass- socket"""address_family = socket.AF_INETsocket_type = socket.SOCK_STREAMrequest_queue_size = 5allow_reuse_address = Falsedef __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):"""Constructor.  May be extended, do not override."""BaseServer.__init__(self, server_address, RequestHandlerClass)self.socket = socket.socket(self.address_family,self.socket_type)if bind_and_activate:try:self.server_bind()self.server_activate()except:self.server_close()raisedef server_bind(self):"""Called by constructor to bind the socket.May be overridden."""if self.allow_reuse_address:self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)self.socket.bind(self.server_address)self.server_address = self.socket.getsockname()def server_activate(self):"""Called by constructor to activate the server.May be overridden."""self.socket.listen(self.request_queue_size)def server_close(self):"""Called to clean-up the server.May be overridden."""self.socket.close()def fileno(self):"""Return socket file number.Interface required by select()."""return self.socket.fileno()def get_request(self):"""Get the request and client address from the socket.May be overridden."""return self.socket.accept()def shutdown_request(self, request):"""Called to shutdown and close an individual request."""try:#explicitly shutdown.  socket.close() merely releases#the socket and waits for GC to perform the actual close.
            request.shutdown(socket.SHUT_WR)except socket.error:pass #some platforms may raise ENOTCONN here
        self.close_request(request)def close_request(self, request):"""Called to clean up an individual request."""request.close()TCPServer
TCPServer
class ThreadingMixIn:"""Mix-in class to handle each request in a new thread."""# Decides how threads will act upon termination of the# main processdaemon_threads = Falsedef process_request_thread(self, request, client_address):"""Same as in BaseServer but as a thread.In addition, exception handling is done here."""try:self.finish_request(request, client_address)self.shutdown_request(request)except:self.handle_error(request, client_address)self.shutdown_request(request)def process_request(self, request, client_address):"""Start a new thread to process the request."""t = threading.Thread(target = self.process_request_thread,args = (request, client_address))t.daemon = self.daemon_threadst.start()ThreadingMixIn
ThreadingMixIn
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
ThreadingTCPServer

RequestHandler相关源码:

class BaseRequestHandler:"""Base class for request handler classes.This class is instantiated for each request to be handled.  Theconstructor sets the instance variables request, client_addressand server, and then calls the handle() method.  To implement aspecific service, all you need to do is to derive a class whichdefines a handle() method.The handle() method can find the request as self.request, theclient address as self.client_address, and the server (in case itneeds access to per-server information) as self.server.  Since aseparate instance is created for each request, the handle() methodcan define arbitrary other instance variariables."""def __init__(self, request, client_address, server):self.request = requestself.client_address = client_addressself.server = serverself.setup()try:self.handle()finally:self.finish()def setup(self):passdef handle(self):passdef finish(self):passSocketServer.BaseRequestHandler
SocketServer.BaseRequestHandler

实例:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import SocketServerclass MyServer(SocketServer.BaseRequestHandler):def handle(self):# print self.request,self.client_address,self.serverconn = self.requestconn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')Flag = Truewhile Flag:data = conn.recv(1024)if data == 'exit':Flag = Falseelif data == '0':conn.sendall('通过可能会被录音.balabala一大推')else:conn.sendall('请重新输入.')if __name__ == '__main__':server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyServer)server.serve_forever()服务端
服务端
#!/usr/bin/env python
# -*- coding:utf-8 -*-import socketip_port = ('127.0.0.1',8009)
sk = socket.socket()
sk.connect(ip_port)
sk.settimeout(5)while True:data = sk.recv(1024)print 'receive:',datainp = raw_input('please input:')sk.sendall(inp)if inp == 'exit':breaksk.close()客户端
客户端

源码精简:

import socket
import threading
import selectdef process(request, client_address):print request,client_addressconn = requestconn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')flag = Truewhile flag:data = conn.recv(1024)if data == 'exit':flag = Falseelif data == '0':conn.sendall('通过可能会被录音.balabala一大推')else:conn.sendall('请重新输入.')sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sk.bind(('127.0.0.1',8002))
sk.listen(5)while True:r, w, e = select.select([sk,],[],[],1)print 'looping'if sk in r:print 'get request'request, client_address = sk.accept()t = threading.Thread(target=process, args=(request, client_address))t.daemon = Falset.start()sk.close()
View Code

如精简代码可以看出,SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和Threading 两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,当前线程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)。

ForkingTCPServer

ForkingTCPServer和ThreadingTCPServer的使用和执行流程基本一致,只不过在内部分别为请求者建立 “线程”  和 “进程”。

基本使用:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import SocketServerclass MyServer(SocketServer.BaseRequestHandler):def handle(self):# print self.request,self.client_address,self.serverconn = self.requestconn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')Flag = Truewhile Flag:data = conn.recv(1024)if data == 'exit':Flag = Falseelif data == '0':conn.sendall('通过可能会被录音.balabala一大推')else:conn.sendall('请重新输入.')if __name__ == '__main__':server = SocketServer.ForkingTCPServer(('127.0.0.1',8009),MyServer)server.serve_forever()服务端
服务端
#!/usr/bin/env python
# -*- coding:utf-8 -*-import socketip_port = ('127.0.0.1',8009)
sk = socket.socket()
sk.connect(ip_port)
sk.settimeout(5)while True:data = sk.recv(1024)print 'receive:',datainp = raw_input('please input:')sk.sendall(inp)if inp == 'exit':breaksk.close()客户端
客户端

以上ForkingTCPServer只是将 ThreadingTCPServer 实例中的代码:

server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyRequestHandler)
变更为:
server = SocketServer.ForkingTCPServer(('127.0.0.1',8009),MyRequestHandler)

SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 os.fork 两个东西,其实本质上就是在服务器端为每一个客户端创建一个进程,当前新创建的进程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)。

源码剖析参考 ThreadingTCPServer

Twisted

Twisted是一个事件驱动的网络框架,其中包含了诸多功能,例如:网络协议、线程、数据库管理、网络操作、电子邮件等。

事件驱动

简而言之,事件驱动分为二个部分:第一,注册事件;第二,触发事件。

自定义事件驱动框架,命名为:“弑君者”:

#!/usr/bin/env python
# -*- coding:utf-8 -*-# event_drive.py

event_list = []def run():for event in event_list:obj = event()obj.execute()class BaseHandler(object):"""用户必须继承该类,从而规范所有类的方法(类似于接口的功能)"""def execute(self):raise Exception('you must overwrite execute')最牛逼的事件驱动框架
最牛逼的事件驱动框架

程序员使用“弑君者框架”:

#!/usr/bin/env python
# -*- coding:utf-8 -*-from source import event_driveclass MyHandler(event_drive.BaseHandler):def execute(self):print 'event-drive execute MyHandler'event_drive.event_list.append(MyHandler)
event_drive.run()
View Code

上述代码,事件驱动只不过是框架规定了执行顺序,程序员在使用框架时,可以向原执行顺序中注册“事件”,从而在框架执行时可以出发已注册的“事件”。

基于事件驱动Socket

#!/usr/bin/env python
# -*- coding:utf-8 -*-from twisted.internet import protocol
from twisted.internet import reactorclass Echo(protocol.Protocol):def dataReceived(self, data):self.transport.write(data)def main():factory = protocol.ServerFactory()factory.protocol = Echoreactor.listenTCP(8000,factory)reactor.run()if __name__ == '__main__':main()

程序执行流程:

  • 运行服务端程序
  • 创建Protocol的派生类Echo
  • 创建ServerFactory对象,并将Echo类封装到其protocol字段中
  • 执行reactor的 listenTCP 方法,内部使用 tcp.Port 创建socket server对象,并将该对象添加到了 reactor的set类型的字段 _read 中
  • 执行reactor的 run 方法,内部执行 while 循环,并通过 select 来监视 _read 中文件描述符是否有变化,循环中...
  • 客户端请求到达
  • 执行reactor的 _doReadOrWrite 方法,其内部通过反射调用 tcp.Port 类的 doRead 方法,内部 accept 客户端连接并创建Server对象实例(用于封装客户端socket信息)和 创建 Echo 对象实例(用于处理请求) ,然后调用 Echo 对象实例的 makeConnection 方法,创建连接。
  • 执行 tcp.Server 类的 doRead 方法,读取数据,
  • 执行 tcp.Server 类的 _dataReceived 方法,如果读取数据内容为空(关闭链接),否则,出发 Echo 的 dataReceived 方法
  • 执行 Echo 的 dataReceived 方法

从源码可以看出,上述实例本质上使用了事件驱动的方法 和 IO多路复用的机制来进行Socket的处理。

#!/usr/bin/env python
# -*- coding:utf-8 -*-from twisted.internet import reactor, protocol
from twisted.web.client import getPage
from twisted.internet import reactor
import timeclass Echo(protocol.Protocol):def dataReceived(self, data):deferred1 = getPage('http://cnblogs.com')deferred1.addCallback(self.printContents)deferred2 = getPage('http://baidu.com')deferred2.addCallback(self.printContents)for i in range(2):time.sleep(1)print 'execute ',idef execute(self,data):self.transport.write(data)def printContents(self,content):print len(content),content[0:100],time.time()def main():factory = protocol.ServerFactory()factory.protocol = Echoreactor.listenTCP(8000,factory)reactor.run()if __name__ == '__main__':main()异步IO操作
异步IO操作

转载于:https://www.cnblogs.com/wanghzh/p/5571117.html

相关文章:

(转)使用 Spring缓存抽象 支持 EhCache 和 Redis 混合部署

背景&#xff1a;最近项目组在开发本地缓存&#xff0c;其中用到了redis和ehcache&#xff0c;但是在使用注解过程中发现两者会出现冲突&#xff0c;这里给出解决两者冲突的具体方案。 spring-ehcache.xml配置&#xff1a; <?xml version"1.0" encoding"UTF…

终端SVN常用命令

svn help 帮助 svn checkout path 从服务器checkout文件到本地(path是服务器上的目录&#xff0c;简写svn co path) svn add file_name 往代码库添加新的文件 svn commit -m "xxx" 提交添加的文件&#xff0c;或者本地做的修改到服务器端(“xxx”内为提交说明…

程序员怎么赚更多的钱_自由职业技巧:如何感到更加自信和赚更多钱

程序员怎么赚更多的钱Over my 10 years as a freelance developer, many fellow freelancers have asked me for advice. How can they make freelancing work for them?在我作为自由开发者的10年中&#xff0c;许多自由职业者都向我寻求建议。 他们如何让他们从事自由职业&am…

RedHat 7.0及CentOS 7.0禁止Ping的三种方法

作者&#xff1a;荒原之梦原文链接&#xff1a;http://zhaokaifeng.com/?p538前言&#xff1a; “Ping”属于ICMP协议&#xff08;即“Internet控制报文协议”&#xff09;&#xff0c;而ICMP协议是TCP/IP协议的一个子协议&#xff0c;工作在网际层。ICMP协议主要用于传输网络…

关于sql 增删改

1.更改数据库的名称 --更改数据库的名称&#xff0c;逗号前面是之前的&#xff0c;后面是改成的名 sp_renamedb student,xuesheng 2.表中有数据的情况下再添加列、删除列 --后来添加列&#xff0c;只能默认可以为空值 altear table shuiguo add [int] varchar(10) --int加上中括…

使用version遇到的那些坑

公司代码管理使用的SVN, 所以就用到了SVN工具version 公司没给买正版的version, 遇到各种崩溃, 各种坑 1. 更新项目时遇到网络不稳定的情况, 更新失败, 项目中的某个文件就莫名其妙的被锁定了 !!! 如果只是更新一个文件还好说, unlock一下就好了,但是如果你是一个文件夹全部…

docker手册_Docker手册

docker手册The concept of containerization itself is pretty old, but the emergence of the Docker Engine in 2013 has made it much easier to containerize your applications. 容器化本身的概念还很老&#xff0c;但是Docker Engine在2013年的出现使容器化应用程序变得更…

MongoDB修改器的使用1

为什么要使用修改器&#xff1f; 通常我们只会修改文档的一部分&#xff0c;这时候更新整个文档就显得很麻烦&#xff0c;通常是通过原子性的更新修改器来完成。 1."$set"修改器 "$set"用来指定某个字段&#xff0c;如果不存在&#xff0c;则创建。这对部…

4GL之Non-SCROLLING CURSOR

在4gl中CURSOR可以说是每一个程序中都会有的&#xff0c;而CURSOR又分为三种SCROLLING CURSOR、Non-SCROLLING CURSOR、LOCKING CURSOR。 Non-SCROLLING CURSOR的聲明有兩種&#xff0c;一種是先定義好sql語句到一個變量里&#xff1a; DECLARE cursor名 CURSOR FROM 變量…

项目总结三--波纹视图

波纹视图的使用 代码在github&#xff1a;https://github.com/wyon0313/YGMoireAnimation

vlookup示例_VLOOKUP示例–如何在Excel中执行VLOOKUP

vlookup示例Microsoft Excel includes a variety of different functions that help users with calculations of any kind. The functionality of Excel is so comprehensive that average users dont even take advantage of most utilities.Microsoft Excel包括各种不同的功…

MySQL--从库启动复制报错1236

链接:http://blog.csdn.net/yumushui/article/details/42742461 今天在搭建一个MySQL master-slave集群时&#xff0c;执行了change master命令&#xff0c;然后又 start slave 启动slave服务&#xff0c;结果查看salve状态就出现错误了&#xff1a; mysql> show slave stat…

使用Script元素发送JSONP请求

// 根据指定URL发送一个JSONP请求 //然后把解析得到的相应数据传递给回调函数 //在URL中添加一个名为jsonp的查询参数,用于指定该请求的回调函数的名称 function getJSONP(url, callback){//为本次请求创建一个唯一的回调函数名称var cbnum "cb"getJSONP.counter;va…

iOS 崩溃记录

dyld: Library not loaded: /System/Library/Frameworks/UserNotifications.framework/UserNotifications Referenced from: /var/containers/Bundle/Application/AEECAAFB-F14A-43AA-9FB8-8388CAC40122/DouLiao.app/DouLiao Reason: image not found 原因应该是iOS系统版本太…

以太坊Geth几种同步模式

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自链客区块链技术问答社区&#xff0c;未经允许拒绝转载。 以太坊Geth几种同步模式 同步模式分类 –fast Enable fast syncing through state downloads –light Enable light client mode –s…

[转]Membership 到 .NET4.5 之 ASP.NET Identity

本文转自&#xff1a;http://www.cnblogs.com/jesse2013/p/membership-part3.html 我们前面已经讨论过了如何在一个网站中集成最基本的Membership功能&#xff0c;然后深入学习了Membership的架构设计。正所谓从实践从来&#xff0c;到实践从去&#xff0c;在我们把Membership的…

js填充select下拉框并选择默认值

/* 使用json数组填充下拉框并复选 *//* 初始化下拉框数据 */ var jsonStr { "data": [] }; for (var str in JsonStr.data) {jsonStr.data.push({ "value": JsonStr.data[str].value, "text": JsonStr.data[str].text }); }/* 调用BandSelectOb…

关于curl使用记录

因经常需要排除线上用户问题&#xff0c;查看用户数据请求结果&#xff0c;使用到curl命令&#xff0c;但是总是忘记&#xff0c;在此做下记录。 curl post请求命令行如下&#xff1a; curl -d "param0value0&param1value1" "url"

智能合约部署及调用

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自链客区块链技术问答社区&#xff0c;未经允许拒绝转载。 智能合约部署及调用 以太坊区块链技术2.0版本对于行业应用的开发最主要特性就是实现了智能合约&#xff0c;本质上讲智能合约是由事件驱…

POP到指定的界面

int index (int)[[self.navigationController viewControllers]indexOfObject:self]; [self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:(index -2)] animated:YES];

js markdown chart flow

http://knsv.github.io/mermaid/#example-of-a-marked-renderer转载于:https://www.cnblogs.com/studyNT/p/5584399.html

使用Remix编译和部署以太坊智能合约

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自链客区块链技术问答社区&#xff0c;未经允许拒绝转载。 使用Remix编译和部署以太坊智能合约 Remix 是一个开源的 Solidity 智能合约开发环境&#xff0c;提供基本的编译、部署至本地或测试网络…

Java之Array(数组)说明

代码说明&#xff1a; 1 package array;2 3 import java.util.ArrayList;4 import java.util.Arrays;5 import java.util.List;6 7 /**8 * Array使用说明&#xff1a;9 * 内容&#xff1a; 10 * 1、Array实例化&#xff1b; 11 * 2、Array与ArrayList转换&#xff1b; 12 …

创建操作/删除多行数据的UITableView的细节

首先注意需要重写-&#xff08;UITableViewCellEditingStyle&#xff09;tableView:&#xff08;UITableView *&#xff09;tableView editingStyleForRowAtIndexPath:&#xff08;NSIndexPath *&#xff09;indexPath 这里需要注意的是返回的结果应该是 return UITableViewCel…

每天进步一点点——Linux

http://blog.csdn.net/cywosp/article/category/443566/1转载于:https://www.cnblogs.com/zengkefu/p/5586780.html

用Go 构建一个区块链 -- Part 5: 地址

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自链客区块链技术问答社区&#xff0c;未经允许拒绝转载。 比特币地址 这就是一个真实的比特币地址&#xff1a;1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa。这是史上第一个比特币地址&#xff0c;据说属于…

iOS 9 适配系列教程

转自&#xff1a;http://www.cocoachina.com/ios/20150703/12392.html 本文是投稿文章&#xff0c;作者&#xff1a;ChenYilong&#xff08;https://github.com/ChenYilong/iOS9AdaptationTips&#xff09; Demo1_iOS9网络适配_改用更安全的HTTPS iOS9把所有的http请求都改为ht…

AutoMocker单元测试

/// <summary>/// 测试获取所有物流/// </summary>[TestMethod]public void TestExpressController(){var Expresss new List<Express> { new Express{Code"01",Name"测试物流"}}.AsQueryable();var mocker new AutoMocker();mocker.U…

CSS.text不被选中

1、 text{-moz-user-select: none; /*火狐*/-webkit-user-select: none; /*webkit浏览器*/-ms-user-select: none; /*IE10*/-khtml-user-select: none; /*早期浏览器*/-o-user-select: none; /* Opera*/user-select: none;} 2、user-select - CSS3参考手册.html&#xff08;htt…

一个Solidity源文件的布局

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自链客区块链技术问答社区&#xff0c;未经允许拒绝转载。 源文件可以包含任意数量的合约定义&#xff0c;include指令和pragma伪指令。 Pragma 版本 源文件可以&#xff08;并且应该&#xff09;使…