单进程阻塞版
#!/usr/bin/env python
# encoding: utf-8
from socket import *
def main():
# 1.创建Socket对象
tcp_server = socket()
# 2不会出现端口被占用
tcp_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 2.绑定IP地址和端口
tcp_server.bind(("", 9999))
# 3.把主动模式变成被动模式,一旦改变,这个Socket对象只能接,不能拨号
tcp_server.listen(5)
# 4.等待客户端连接
while True:
new_socket, client_info = tcp_server.accept()
print(f"有新的客户{client_info}来了")
# 5.接收消息
raw_data = new_socket.recv(1024)
# 5.1判断消息
if raw_data:
print(f"收到来自{client_info}的消息:{raw_data.decode('utf-8')}")
else:
break
# 6.关闭连接
new_socket.close()
if __name__ == '__main__':
main()
同时只能为一个人服务
多进程阻塞版本
#!/usr/bin/env python
# encoding: utf-8
from socket import *
from multiprocessing import Process
# 处理数据
def rec_data(sock, client_info):
while True:
# 5.接收消息
raw_data = sock.recv(1024)
# 5.1判断消息
if raw_data:
print(f"收到来自{client_info}的消息:{raw_data.decode('utf-8')}")
else:
# 6.关闭连接
sock.close()
break
def main():
# 1.创建Socket对象
tcp_server = socket()
# 2不会出现端口被占用
tcp_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 2.绑定IP地址和端口
tcp_server.bind(("", 7777))
# 3.把主动模式变成被动模式,一旦改变,这个Socket对象只能接,不能拨号
tcp_server.listen(5)
# 4.等待客户端连接
while True:
new_socket, client_info = tcp_server.accept()
print(f"有新的客户{client_info}来了")
# 创建新的进程
p = Process(target=rec_data, args=(new_socket, client_info))
# 启动进程
p.start()
# 不能全使用JOIN
# p.join()
# rec_data(new_socket, client_info)
# 关闭主进程中的new_socket
new_socket.close()
if __name__ == '__main__':
main()
代表作:Apache
new_socket在主进程一定要关闭,因为子进程会复制一份父进程内存空间
多线程阻塞版本
#!/usr/bin/env python
# encoding: utf-8
from socket import *
from threading import Thread
# 处理数据
def rec_data(sock, client_info):
while True:
# 5.接收消息
raw_data = sock.recv(1024)
# 5.1判断消息
if raw_data:
print(f"收到来自{client_info}的消息:{raw_data.decode('utf-8')}")
else:
# 6.关闭连接
sock.close()
break
def main():
# 1.创建Socket对象
tcp_server = socket()
# 2不会出现端口被占用
tcp_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 2.绑定IP地址和端口
tcp_server.bind(("", 7788))
# 3.把主动模式变成被动模式,一旦改变,这个Socket对象只能接,不能拨号
tcp_server.listen(5)
# 4.等待客户端连接
while True:
new_socket, client_info = tcp_server.accept()
print(f"有新的客户{client_info}来了")
# 创建新的进程
t = Thread(target=rec_data, args=(new_socket, client_info))
# 启动进程
t.start()
# 不能全使用JOIN
# p.join()
# rec_data(new_socket, client_info)
# 在多线程中,共享内存空间,不能关闭主线程中的new_socket
# new_socket.close()
if __name__ == '__main__':
main()
代表作:IIS 微软在Window上一个Web服务器
在主线中new_socket不能关闭,因为子线程和主线程共用内存空间
单进程非阻塞版并发服务器
#!/usr/bin/env python
# encoding: utf-8
from socket import *
def main():
# 1.创建Socket对象
tcp_server = socket()
# 设置socket属性为非阻塞
tcp_server.setblocking(False)
# 2不会出现端口被占用
tcp_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 2.绑定IP地址和端口
tcp_server.bind(("", 9990))
# 3.把主动模式变成被动模式,一旦改变,这个Socket对象只能接,不能拨号
tcp_server.listen(5)
# 定义一个列表
socket_lists = []
# 4.等待客户端连接
while True:
try:
new_socket, client_info = tcp_server.accept()
# 设置新对象为非阻塞
new_socket.setblocking(False)
# 把新对象放在列表中
socket_lists.append((new_socket, client_info))
except:
pass
else:
print(f"新的连接{client_info}")
for sock, client in socket_lists:
# 5.接收消息
try:
raw_data = sock.recv(1024)
except:
pass
else:
# 5.1判断消息
if raw_data:
print(f"收到来自{client}的消息:{raw_data.decode('utf-8')}")
else:
# 关闭
sock.close()
# 删除列表中的对象
socket_lists.remove((sock, client))
break
if __name__ == '__main__':
main()
[图片上传失败...(image-18f1a0-1551683757215)]
[图片上传失败...(image-de506c-1551683757215)]
虽然能够实现并发处理,但是CPU会100%,一直死循环,现实中不会使用
IO多路复用
概念
用单进程实现并发请求
select版本
#!/usr/bin/env python
# encoding: utf-8
from socket import *
import select
def main():
# 1.创建Socket对象
tcp_server = socket()
# 2不会出现端口被占用
tcp_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 2.绑定IP地址和端口
tcp_server.bind(("", 7755))
# 3.把主动模式变成被动模式,一旦改变,这个Socket对象只能接,不能拨号
tcp_server.listen(5)
# 4.把主套接字加入到公共监控区
socket_lists = [tcp_server]
# 5.内核监听
while True:
# 5.1 如果socket_lists有响动,这一句就会解阻塞,并返回有响动的对象
read_lists, _, _ = select.select(socket_lists, [], [])
# 5.2 循环遍历
for sock in read_lists:
# 5.2.1判断这个对象就是主对象 前台小姐姐
if sock is tcp_server:
new_socket, client_info = sock.accept()
# 把新加入监视区
socket_lists.append(new_socket)
else:
# 接收消息
raw_data = sock.recv(1024)
# 判断消息
if raw_data:
print(f"收到消息:{raw_data.decode('utf-8')}")
else:
# 关闭连接
sock.close()
# 删除列表
socket_lists.remove(sock)
if __name__ == '__main__':
main()
最大连接数限制:32位系统1024个连接 64位2048个连接
轮询
poll:什么也没有做,只是去掉最大的连接数,内存限制,数量大了,自动就暴了
[图片上传失败...(image-3597ad-1551683757215)]
epoll版本
#!/usr/bin/env python
# encoding: utf-8
from socket import *
import select
def main():
# 1.创建Socket对象
tcp_server = socket()
# 设置socket属性为非阻塞
tcp_server.setblocking(True)
# 2不会出现端口被占用
tcp_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 2.绑定IP地址和端口
tcp_server.bind(("", 9999))
# 3.把主动模式变成被动模式,一旦改变,这个Socket对象只能接,不能拨号
tcp_server.listen(5)
# 4. epool只能在LINUX上使用 创建一个epool对象
epool = select.epoll()
# 5.fd 文件描述符
print(f"tcp_server的文件描述符:{tcp_server.fileno()}")
# 5.加到监听区
epool.register(tcp_server.fileno(), select.EPOLLIN)
# 创建一个字典 用来存新的小姐姐
new_socket_dicts = {}
client_info_dicts = {}
while True:
print(f"当前new_socket_dicts:{new_socket_dicts}")
# 6.1 监听中
epool_lists = epool.poll()
print(f"epool_lists:{epool_lists}")
# 循环 epool_lists
for fd, events in epool_lists:
# 判断是不是主套接字
if fd == tcp_server.fileno():
# 创建新的对象 小姐姐
new_socket, client_info = tcp_server.accept()
# 把新小姐姐丢到监控区
epool.register(new_socket.fileno(), select.EPOLLIN)
# 把新的小姐姐存到字典 new_socket_dicts = {"3":new_socket}
new_socket_dicts[new_socket.fileno()] = new_socket
client_info_dicts[new_socket.fileno()] = client_info
else:
# 接收数据 fd = 3
raw_data = new_socket_dicts[fd].recv(1024)
if raw_data:
print(f"收到数据来自{client_info_dicts[fd]} 消息{raw_data.decode('utf-8')}")
else:
# 关闭连接
new_socket_dicts[fd].close()
# 注销
epool.unregister(fd)
break
if __name__ == '__main__':
main()
文件描述符:一个Socket对象的编号
专业名词
- IO阻塞:只要是IO操作,一定有缓存区,一定是读写操作,默认阻塞,我们去取东西,如果有东西就取出来,如果没有就一直在那里等着,等到有东西为止
- IO非阻塞:我去取东西,如果有东西就把东西取出来,如果没有,我掉头就走
- 轮询:一个一个去问,每一个人都要问到
- 事件通知机制:不再一个一个问,你们有事主动通知我,我再做处理
网路不通解决
- ping: ping 172.16.13.236
- 在服务端运行:netstat -natu | grep "9999" 确定服务有没有启动
- 在客户端:telnet 172.16.13.236 9999 如果不能成功,说明服务端有防火 阿里云或其它云还一个大防火