Socket 网络通信简介
Socket 是电脑网络中进程间数据流的端点,也是传输层网络通信的 API。HTTP 作为一个应用层的协议,一般是基于传输层的 TCP 协议的。因此我们要在 TCP 协议上构建我们的程序,也就是使用 Socket 传输 HTTP 的消息。如果你对 Socket 网络编程比较熟悉,可以直接跳过这一节。端口、IP 之类的基本概念就不重复了。下边这张图展示了 Socket TCP 通信的步骤:
- 服务器需要新建一个 socket 对象,并将这个对象绑定在某个特定的端口。socket.AF_INET 代表 IPV4 协议族,socket.SOCK_STREAM 代表是 TCP 协议。下一行的 setsockopt 表示这个地址可以重复使用。
- 设置这个 socket 对某个特定的端口进行监听,参数代表等待队列的大小。
- 调用 accept 方法,等待客户端连接到这个端口上。需要注意的是调用了 accept 方法以后当前进程会阻塞在 accept 函数上,直到收到一个新的连接请求。
- 客户端新建 socket 对象。调用 connect 方法去访问特定的地址+端口号。此时当前进程会阻塞在这个位置,直到建立连接。connect 和 accept 建立连接的这个过程,就对应着 TCP 的三次握手过程。
- 一旦连接建立,就可以使用 send 和 recv 互相之间发送消息了。recv 的参数代表接收多少个字节。
- 当通讯结束的时候需要使用 close 关闭 socket。
这个过程大家可以打开两个 shell,一个做为客户端,一个做为服务器端。注意体会一下阻塞的过程,注意 accept 是什么时候返回的。如下图所示:
Python Socket实现的简单http服务器
#!/usr/bin/env python
#coding=utf-8
import socket
import re
HOST = ''
PORT = 8000
#Read index.html, put into HTTP response data
index_content = '''
HTTP/1.x 200 ok
Content-Type: text/html
'''
file = open('index.html', 'r')
index_content += file.read()
file.close()
#Read reg.html, put into HTTP response data
reg_content = '''
HTTP/1.x 200 ok
Content-Type: text/html
'''
file = open('reg.html', 'r')
reg_content += file.read()
file.close()
#Read picture, put into HTTP response data
file = open('T-mac.jpg', 'rb')
pic_content = '''
HTTP/1.x 200 ok
Content-Type: image/jpg
'''
pic_content += file.read()
file.close()
#Configure socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((HOST, PORT))
sock.listen(100)
#infinite loop
while True:
# maximum number of requests waiting
conn, addr = sock.accept()
request = conn.recv(1024)
method = request.split(' ')[0]
src = request.split(' ')[1]
print 'Connect by: ', addr
print 'Request is:\n', request
#deal wiht GET method
if method == 'GET':
if src == '/index.html':
content = index_content
elif src == '/T-mac.jpg':
content = pic_content
elif src == '/reg.html':
content = reg_content
elif re.match('^/\?.*$', src):
entry = src.split('?')[1] # main content of the request
content = 'HTTP/1.x 200 ok\r\nContent-Type: text/html\r\n\r\n'
content += entry
content += '<br /><font color="green" size="7">register successs!</p>'
else:
continue
#deal with POST method
elif method == 'POST':
form = request.split('\r\n')
entry = form[-1] # main content of the request
content = 'HTTP/1.x 200 ok\r\nContent-Type: text/html\r\n\r\n'
content += entry
content += '<br /><font color="green" size="7">register successs!</p>'
######
# More operations, such as put the form into database
# ...
######
else:
continue
conn.sendall(content)
#close connection
conn.close()
chmod +x httpServer.py
, 并运行./httpServer.py
使用浏览器当做客户端访问服务器
在httpServer.py
所在目录有index.html
, reg.html
, T-mac.jpg
get演示
在chrome浏览器中打开http://127.0.0.1:8000/index.html
效果如下
命令行中显示
服务器输出:
Connect by: ('127.0.0.1', 54234)
Request is:
GET /index.html HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7
Connect by: ('127.0.0.1', 54235)
Request is:
GET /T-mac.jpg HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Referer: http://127.0.0.1:8000/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7
Connect by: ('127.0.0.1', 54236)
Request is:
GET /favicon.ico HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Referer: http://127.0.0.1:8000/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7
post演示
在chrome浏览器中打开http://127.0.0.1:8000/reg.html
效果如下
命令行中显示
Connect by: ('127.0.0.1', 54248)
Request is:
GET /reg.html HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7
Connect by: ('127.0.0.1', 54249)
Request is:
GET /favicon.ico HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Referer: http://127.0.0.1:8000/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7
点击提交数据后
Connect by: ('127.0.0.1', 54257)
Request is:
POST / HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Content-Length: 55
Cache-Control: max-age=0
Origin: http://127.0.0.1:8000
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://127.0.0.1:8000/reg.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7
user=mhj&psw=123&repsw=123&sex=nan&tech=java&country=cn
Connect by: ('127.0.0.1', 54258)
Request is:
GET /favicon.ico HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Referer: http://127.0.0.1:8000/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7
Connect by: ('127.0.0.1', 54259)
Request is:
GET /favicon.ico HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Referer: http://127.0.0.1:8000/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh,en-US;q=0.9,en;q=0.8,zh-TW;q=0.7
其中user=mhj&psw=123&repsw=123&sex=nan&tech=java&country=cn
就是我刚刚输入的表单数据
如果我们把 表单中的 method 改成get
此时浏览器会访问http://127.0.0.1:8000/?user=mhj&psw=123&repsw=123&sex=nan&tech=java&country=cn
在这里可以总结一下post 跟 get 提交的一些区别:
get提交,提交的信息都显示在地址栏中;对于敏感数据不安全;由于地址栏存储体积有限而不能提交大容量数据;将信息封装到了请求消息的请求行
中,而post 提交将信息封装到了请求体中。