(图片来自WIKI)
超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。我们打开浏览器输入网址www.google.com
,不对!刚才那个是一个不存在的网址,我们还是用www.phpgao.com
为例吧。我们(客户端)将老高的域名输入浏览器,浏览器就会为我们呈现老高的网页,首先我们能确定作为客户端,在此期间必定与老高的服务器发生了某种关系!但是具体发送了什么呢?作为一名WEB开发人员,这是我们必须知道的。
下面我们简单的模拟一下刚才的过程。
如何使用以下代码? 在python的交互式环境中逐行输入命令。即在命令行中输入
python
模拟
DNS解析
import socket
host = 'www.phpgao.com'
# 查询域名的IP
server_ip = socket.gethostbyname(host)
print 'Get server\'s IP --> %s' % server_ip
# Get server's IP --> xxx.62.107.xx
建立TCP链接
# socket.SOCK_STREAM代表TCP
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
port = 80
s.connect((server_ip, port))
print '%s:%d Connected' % (server_ip, port)
发送http请求
print 'Send %d bytes' % s.send('GET / HTTP/1.1\r\n')
print 'Send %d bytes' % s.send('Host: ' + host + '\r\n')
# 发送结束标志
print 'Send %d bytes' % s.send('\r\n')
接收http响应
data = s.recv(200)
print repr(data)
print data
关闭链接
s.close
组合
让我们把刚才的请求组合起来,并加一点注释
# coding=UTF-8
import socket
host = 'www.phpgao.com'
# 查询域名的IP
server_ip = socket.gethostbyname(host)
print 'Get server\'s IP --> %s' % server_ip
# socket.SOCK_STREAM代表TCP
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
port = 80
s.connect((server_ip, port))
print '%s:%d Connected' % (server_ip, port)
print 'Send %d bytes' % s.send('GET / HTTP/1.1\r\n')
print 'Send %d bytes' % s.send('Host: ' + host + '\r\n')
# 结束标志
print 'Send %d bytes' % s.send('\r\n')
data = s.recv(200)
print 'We have received %d bytes' % len(data)
print '-----raw-----\n%s\n\n-------' % repr(data)
print '----formated----\n%s\n-------' % data
s.close
分析
以上代码的结果如下
Get server's IP --> xxx.62.107.xx
xxx.62.107.xx:80 Connected
Send header
-------raw---
'GET / HTTP/1.1\r\nHost: www.phpgao.com\r\n\r\n'
-----formated----
GET / HTTP/1.1
Host: www.phpgao.com
Total 40 bytes
We have received 200 bytes
-----raw-----
'HTTP/1.1 200 OK\r\nServer: nginx\r\nDate: Wed, 24 Jun 2015 00:23:37 GMT\r\nContent-Type: text/html; charset=UTF-8\r\nTransfer-Encoding: chunked\r\nConnection: keep-alive\r\nX-Cache-CFC: - 1435105417.911\r\n\r\n8cfc\r'
-------
----formated----
HTTP/1.1 200 OK
Server: nginx
Date: Wed, 24 Jun 2015 00:23:37 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Cache-CFC: - 1435105417.911
8cfc
-------
可以看到:
我们发送出去的http协议很言简意赅,即规定了动作GET
,路径/
,协议版本HTTP 1.1
和主机信息www.phpgao.com
。
我们接收到http协议就相对复杂一点,有响应状态码、服务器软件信息、响应发送时间GMT格式、内容的元信息、Connection表示链接的持续性,最后的X-Cache-CFC是老高自定义的响应头,表明缓存是否命中。
你知道最后的8cfc是什么吗? 答案:十六进制数,表示发送文件的大小,typecho程序特有。
一些问题
关于recv阻塞
为了接收全部HTML响应,我们使用以下代码。
total_data=''
while 1:
print 'Receiving data!'
data = s.recv(1024)
if not data: break
total_data += data
print 'We have received %d bytes' % len(data)
因为recv阻塞,程序会卡在data = s.recv(1024)
,过很久(连接超时)才能让程序走到if not data:
。
如何解决呢?
在请求头部中设置链接为立刻关闭
header = 'GET /favicon.ico HTTP/1.1\r\n' + 'Host: ' + host + '\r\nConnection: close\r\n' + '\r\n'
设置recv为非阻塞
s.connect((server_ip, port))
def recv_timeout(the_socket, timeout=2):
#make socket non blocking
the_socket.setblocking(0)
#total data partwise in an array
total_data=[];
data='';
#beginning time
begin=time.time()
while 1:
#if you got some data, then break after timeout
if total_data and time.time()-begin > timeout:
break
#if you got no data at all, wait a little longer, twice the timeout
elif time.time()-begin > timeout*2:
break
#recv something
try:
data = the_socket.recv(8192)
if data:
total_data.append(data)
#change the beginning time for measurement
begin=time.time()
else:
#sleep for sometime to indicate a gap
time.sleep(0.1)
except:
pass
#join all parts to make final string
return ''.join(total_data)
#get reply and print
print recv_timeout(s)
Reference: