使用多进程实现http服务器
我们将上次的简单http服务器代码复制过来,在他的基础上进行修改,
我们只需要多进程执行发送寒素即可,在main中修改:
import socket
import re
import multiprocessing
def dump_data(cli_socket):
recv_data = cli_socket.recv(1024).decode('utf-8')
recv_data_lines = recv_data.splitlines()
print(recv_data_lines)
ret = re.match(r'[^/]+(/[^ ]*)',recv_data_lines[0])
if ret:
f_name = ret.group(1)
if f_name=="/":
f_name = '/index.html'
try:
file = open('F:'+f_name,'rb')
except:
resp_data = 'HTTP/1.1 404 NOT FOUND\r\n'
resp_data += 'Content-Type:text/html;charset:uft-8\r\n'
resp_data += '\r\n'
resp_data += '404'
cli_socket.send(resp_data.encode('utf-8'))
else:
html_con = file.read()
file.close()
resp_data = 'HTTP/1.1 200 OK\r\n'
resp_data += 'Content-Type:text/html;charset:uft-8\r\n'
resp_data += '\r\n'
cli_socket.send(resp_data.encode('utf-8'))
cli_socket.send(html_con)
cli_socket.close()
def main():
tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server.bind(("",7891))
tcp_server.listen(128)
print('等待')
while True:
cli_socket,cli_addr = tcp_server.accept()
# 多进程实现调用该方法
p = multiprocessing.Process(target=dump_data,args =(cli_socket,))
p.start()
# 这里也需要调用 cli_socket.close(),子进程关闭,主进程关闭。
cli_socket.close()
tcp_server.close()
if __name__ == "__main__":
main()
使用多线程实现http服务器
很简单,把进程修改成线程就可以了。这里就不需要cli_socket.close()了。
import socket
import re
import multiprocessing
def dump_data(cli_socket):
recv_data = cli_socket.recv(1024).decode('utf-8')
recv_data_lines = recv_data.splitlines()
print(recv_data_lines)
ret = re.match(r'[^/]+(/[^ ]*)',recv_data_lines[0])
if ret:
f_name = ret.group(1)
if f_name=="/":
f_name = '/index.html'
try:
file = open('F:'+f_name,'rb')
except:
resp_data = 'HTTP/1.1 404 NOT FOUND\r\n'
resp_data += 'Content-Type:text/html;charsetuft-8'
resp_data += '\r\n'
resp_data += '404'
cli_socket.send(resp_data.encode('utf-8'))
else:
html_con = file.read()
file.close()
resp_data = 'HTTP/1.1 200 OK\r\n'
resp_data += '\r\n'
cli_socket.send(resp_data.encode('utf-8'))
cli_socket.send(html_con)
cli_socket.close()
def main():
tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server.bind(("",7891))
tcp_server.listen(128)
print('等待')
while True:
cli_socket,cli_addr = tcp_server.accept()
# 多进程实现调用该方法
p = multiprocessing.Process(target=dump_data,args =(cli_socket,))
p.start()
# 这里也需要调用 cli_socket.close(),子进程关闭,主进程关闭。
cli_socket.close()
tcp_server.close()
if __name__ == "__main__":
main()
用gevent实现http服务器
这里就不贴全部代码了,在前面添加:
from gevent import monkey
monkey.patch_all()
main方法修改成:
def main():
tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server.bind(("",7891))
tcp_server.listen(128)
print('等待')
while True:
cli_socket,cli_addr = tcp_server.accept()
# gevent实现
gevent.spawn(dump_data,cli_socket)
tcp_server.close()
即可。
单进程,单线程,非堵塞实现并发
上面只有以用多线程,多进程,是因为会发生堵塞的情况,那我们用单进程,单线程,能不能实现不不堵塞不就好了。
设置套接字非堵塞:套接字.setblocking(False),但是不堵塞后,没有收到数据,就会报错的。报错我们处理错误就好了。
实例:
import socket
import re
import time
def main():
tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server.bind(("",7891))
tcp_server.listen(128)
# 设置非堵塞
tcp_server.setblocking(False)
# 创建一个列表
server_tcp_list = list()
while True:
time.sleep(1)
try:
cli_socket,cli_addr = tcp_server.accept()
except Exception as e:
print('等待连接')
else:
cli_socket.setblocking(False)
print('来了一个客户端')
server_tcp_list.append(cli_socket)
for cli_soc in server_tcp_list:
try:
recv_data = cli_soc.recv(1024).decode('utf-8')
except Exception as e:
print('等待发送过来数据')
else:
if recv_data:
print('有数据')
else:
print('服务完成')
cli_soc.close()
server_tcp_list.remove(cli_soc)
tcp_server.close()
if __name__ == "__main__":
main()
长连接与短连接
在HTTP协议1.0中是短连接,在1.1中是长连接
什么是长连接和短连接。
短连接:我们向服务器请求一个数据,先发送请求,再断开,如果再想要一个数据,就再次请求,断开。
长链接:在一起连接和断开中,请求多个数据。
我们上面的例子看是http1.1协议,是长链接,但是我们每次请求后都调用了close(),也就变成了短连接。
长链接实现:
import socket
import re
def dump_data(cli_socket,recv_data):
recv_data_lines = recv_data.splitlines()
print(recv_data_lines)
ret = re.match(r'[^/]+(/[^ ]*)',recv_data_lines[0])
if ret:
f_name = ret.group(1)
if f_name=="/":
f_name = '/index.html'
try:
file = open('F:'+f_name,'rb')
except:
resp_data = 'HTTP/1.1 404 NOT FOUND\r\n'
resp_data += '\r\n'
resp_data += '404'
cli_socket.send(resp_data.encode('utf-8'))
else:
html_con = file.read()
file.close()
resp_data = 'HTTP/1.1 200 OK\r\n'
# 设置编码
resp_data += 'Content-Type:text/html;charset:uft-8\r\n'
# 设置发送的长度,不关闭套接字不知道客户端数据是否发送完毕,设置body长度,body发送完,就发送完毕
resp_data += 'Content-Length:%d\r\n'%(len(html_con))
resp_data += '\r\n'
cli_socket.send(resp_data.encode('utf-8'))
cli_socket.send(html_con)
def main():
tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_server.bind(("",7891))
tcp_server.listen(128)
# 设置非堵塞
tcp_server.setblocking(False)
# 创建一个列表
server_tcp_list = list()
print('等待')
while True:
try:
cli_socket,cli_addr = tcp_server.accept()
except Exception as e:
pass
else:
cli_socket.setblocking(False)
server_tcp_list.append(cli_socket)
for cli_soc in server_tcp_list:
try:
recv_data = cli_soc.recv(1024).decode('utf-8')
except Exception as e:
pass
else:
if recv_data:
dump_data(cli_soc,recv_data)
else:
cli_soc.close()
server_tcp_list.remove(cli_soc)
tcp_server.close()
if __name__ == "__main__":
main()
实现并发服务器的 另一种方式
epoll:
感兴趣的可以去了解下,他的效率很高。nginx服务器一定用到了epoll。gevent内部也用到了。