结合Python的SimpleHTTPServer源码来解析socket通信
何谓socket Unix的计算机处理IO是通过文件的抽象。计算机不同的进程之间也有输入输出,也就是通信。因此这这个通信也是通过文件的抽象文件描述符来进行。 在同一台计算机,进程之间可以这样通信,如果是不同的计算机呢?网络上不同的计算机,也可以通信,那么就得使用网络套接字(socket)。socket就是在不同计算机之间进行通信的一个抽象。他工作于TCP/IP协议中应用层和传输层之间的一个抽象。如下图: 服务器通信 socket 通信实例 server.py import socket HOST = 'localhost' # 服务器主机地址 PORT = 5000 # 服务器监听端口 BUFFER_SIZE = 2048 # 读取数据大小 # 创建一个套接字 sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 绑定主机和端口 sock.bind((HOST,PORT)) # 开启socket监听 sock.listen(5) print 'Server start,listening {}'.format(PORT) while True: # 建立连接,连接为建立的时候阻塞 conn,addr = sock.accept() while True: # 读取数据,数据还没到来阻塞 data = conn.recv(BUFFER_SIZE) if len(data): print 'Server Recv Data: {}'.format(data) conn.send(data) print 'Server Send Data: {}'.format(data) else: print 'Server Recv Over' break conn.close() sock.close() client.py import socket HOST = 'localhost' PORT = 5000 BUFFER_SIZE = 1024 # 创建客户端套接字 sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 连接到服务器 sock.connect((HOST,PORT)) try: message = "Hello" # 发起数据给服务器 sock.sendall(message) amount_received = 0 amount_expected = len(message) while amount_received < amount_expected: # 接收服务器返回的数据 data = sock.recv(10) amount_received += len(data) print 'Client Received: {}'.format(data) except socket.errno,e: print 'Socket error: {}'.format(e) except Exception,e: print 'Other exception: %s'.format(e) finally: print 'Closing connection to the server' sock.close() TCP 三次握手 第一握:首先客户端发送一个syn,请求连接, C:约么? S:约 C:好的 约会 上图也很清晰的表明了三次握手的socket具体过程。
至此,客户端和服务器的socket通信连接建立完成,剩下的就是两个端的连接对象收发数据,从而完成网络通信。
SimpleHTTPServer 曾经为了表示python的简洁优雅,经常会举这样的例子,python可以一行代码开启一个服务器。 $ python -m SimpleHTTPServer 这里的SimpleHTTPServer就是实现了HTTPServer的模块。 SimpleHTTPServer通过调用BaseHTTPServer模块的test方法做为入口。 def test(HandlerClass = SimpleHTTPRequestHandler,ServerClass = BaseHTTPServer.HTTPServer): BaseHTTPServer.test(HandlerClass,ServerClass) test方法做了两件事,第一件就是使用HTTPServer接受一个监听地址和requestClass参数,创建了一个实例对象,调用server_forever方法开启服务。 1.SimpleHTTPRequestHandler class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): server_version = "SimpleHTTP/" + __version__ def do_GET(self): f = self.send_head() if f: self.copyfile(f,self.wfile) f.close() def do_HEAD(self): f = self.send_head() if f: f.close() do_GET 和 do_HEAD 分别实现了http的get请求和head请求的处理。他们调用send_head方法:
def send_head(self): path = self.translate_path(self.path) f = None if os.path.isdir(path): if not self.path.endswith('/'): self.send_response(301) self.send_header("Location",self.path + "/") self.end_headers() return None for index in "index.html","index.htm": index = os.path.join(path,index) if os.path.exists(index): path = index break else: return self.list_directory(path) ctype = self.guess_type(path) try: f = open(path,'rb') except IOError: self.send_error(404,"File not found") return None self.send_response(200) self.send_header("Content-type",ctype) fs = os.fstat(f.fileno()) self.send_header("Content-Length",str(fs[6])) self.send_header("Last-Modified",self.date_time_string(fs.st_mtime)) self.end_headers() return f send_head 方法通过uri的path分析得到客户请求的网路路径。构造head的mime元信息并发送到客户端,然后返回一个打开path的文件句柄。 2.copyfile 3.list_directory def list_directory(self,path): try: list = os.listdir(path) except os.error: self.send_error(404,"No permission to list directory") return None list.sort(key=lambda a: a.lower()) f = StringIO() displaypath = cgi.escape(urllib.unquote(self.path)) f.write('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">') f.write("<html>n<title>Directory listing for %s</title>n" % displaypath) f.write("<body>n<h2>Directory listing for %s</h2>n" % displaypath) f.write("<hr>n<ul>n") for name in list: fullname = os.path.join(path,name) displayname = linkname = name # Append / for directories or @ for symbolic links if os.path.isdir(fullname): displayname = name + "/" linkname = name + "/" if os.path.islink(fullname): displayname = name + "@" # Note: a link to a directory displays with @ and links with / f.write('<li><a href="%s">%s</a>n' % (urllib.quote(linkname),cgi.escape(displayname))) f.write("</ul>n<hr>n</body>n</html>n") length = f.tell() f.seek(0) self.send_response(200) encoding = sys.getfilesystemencoding() self.send_header("Content-type","text/html; charset=%s" % encoding) self.send_header("Content-Length",str(length)) self.end_headers() return f 由此可见,处理客户端的请求,只需要使用 send_reponse, send_header 和 end_headers ,就能向客户端发送reponse。 4.自定义http服务 然后再定义一个CustomHTTPServer继承自HTTPServer,它接受CustomHTTPRequestHadnler作为自己的handler。简单的代码如下: # -*- coding: utf-8 -*- from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer class CustomHTTPRequestHandler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.send_header('Content-type','text/html') self.end_headers() self.wfile.write("hello worldrn") class CustomHTTPServer(HTTPServer): def __init__(self,host,port): HTTPServer.__init__(self,(host,port),CustomHTTPRequestHandler) def main(): server = CustomHTTPServer('127.0.0.1',8000) server.serve_forever() if __name__ == '__main__': main() 使用curl访问可以得到 ➜ ~ curl http://127.0.0.1:8000 hello world ➜ ~ 控制台会打出访问的log。 127.0.0.1 - - [01/Jun/2015 11:42:33] "GET / HTTP/1.1" 200 - 从socket的建立,select的IO模式,再到Server和Handler的组合构建服务。我们已经熟悉了python的基本网络编程。python的web开发中,更多是使用WSGI协议。实现该协议的还有 uWSGI和gunicorn等库。相比那些库,python内部提供了一个wsgiref模块,实现了一个简单wsgi服务--simple_server。 接下来将会通过分析simple_server,更好的掌握WSGI协议。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- Python __init__()方法的简单示例
- python – 如何在django中调试’并非所有临时消息都可以存储
- python – Django错误— index()缺少1个必需的位置参数:’
- Django PostgreSQL:填写范围内的缺失日期
- Z3 python对待x ** 2与x * x不同?
- python – 在pandas dataframe中以相同字符串开头的列的和值
- 创建虚拟环境(Python)中“virtualenv”和“-m venv”之间的
- C++、python和go语言实现的简单客户端服务器代码示例
- 用Python实现数据结构之优先级队列
- django轻松使用富文本编辑器CKEditor的方法