加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

基于并发服务器几种实现方法(总结)

发布时间:2020-05-24 17:50:29 所属栏目:Python 来源:互联网
导读:今天主题是实现并发服务器,实现方法有多种版本,先从简单的单进程代码实现到多进程,多线程的实现,最终引入一些高级模块来实现并发TCP服务器。

今天主题是实现并发服务器,实现方法有多种版本,先从简单的单进程代码实现到多进程,多线程的实现,最终引入一些高级模块来实现并发TCP服务器。

说到TCP,想起吐槽大会有个段子提到三次握手,也只有程序猿(媛)能get。

UDP服务器数据传输不可靠,这里就忽略了。

>>:

简单的单进程TCP服务器

假代码:

#创建tcp服务器套接字

#绑定端口

#设置正常情况退出的服务器下,端口可以重用

#设置监听,变为主动监听

# 等待客户端的链接,返回新的socket和地址

#关闭tcp服务器套接字

from socket import socket,AF_INET,SOCK_STREAM,SOL_SOCKET,SO_REUSEADDR
#创建tcp服务器套接字
server_socket = socket(AF_INET,SOCK_STREAM)
#绑定端口
server_socket.bind(("",9999))
#设置正常情况退出的服务器下,端口可以重用
server_socket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
#设置监听,变为主动监听
server_socket.listen(5)
while True:
 # 等待客户端的链接,返回新的socket和地址
 new_socket,new_address = server_socket.accept()
 #接收数据,并且发送数据
 try:
 while True:
 recv_data = new_socket.recv(1024)
 #当有客户端关闭后,recv解除阻塞,并且返回长度为0
 if len(recv_data) > 0:
 recv_content = recv_data.decode("gb2312")
 print("收到:%s的信息是:%s" % (str(new_address),recv_content))
 new_socket.send("thank you!".encode("gb2312"))
 else:
 print("客户端%s已经关闭" % (str(new_address)))
 break
 finally:
 new_socket.close()
 print("关闭%s客户端" % (str(new_address)))
#关闭tcp服务器套接字
server_socket.close()

多进程TCP服务器

from socket import socket,SO_REUSEADDR
from multiprocessing import Process
#在子进程中接收消息
def recv_data(new_socket,new_address):
 while True:
  recv_data = new_socket.recv(1024)
  # 当有客户端关闭后,并且返回长度为0
  if len(recv_data) > 0:
   recv_content = recv_data.decode("gb2312")
   print("收到:%s的信息是:%s" % (str(new_address),recv_content))
   new_socket.send("thank you!".encode("gb2312"))
  else:
   print("客户端%s已经关闭" % (str(new_address)))
   break
 #关闭与客户端的连接
 print("关闭与客户端的连接")
 new_socket.close()
def main():
 #创建tcp服务器套接字
 server_socket = socket(AF_INET,SOCK_STREAM)
 #绑定端口
 server_socket.bind(("",8888))
 #设置正常情况退出的服务器下,端口可以重用
 server_socket.setsockopt(SOL_SOCKET,1)
 #设置监听,变为被动连接
 server_socket.listen(3)
 try:
  while True:
   # 等待客户端的链接,返回新的socket和地址
   new_socket,new_address = server_socket.accept()
   #接收数据,并且发送数据
   Process(target=recv_data,args=(new_socket,new_address)).start()
   #因为主进程和子进程不共享数据
   #如果我们直接关闭new_socket,只是关闭主进程的new_socket,而子进程的不受影响
   new_socket.close()
 finally:
  #关闭tcp服务器套接字
  server_socket.close()
if __name__ == "__main__":
 main()

多进程TCP服务器

from socket import socket,而子进程的不受影响
   new_socket.close()
 finally:
  #关闭tcp服务器套接字
  server_socket.close()
if __name__ == "__main__":
 main()

多线程TCP服务器

from socket import socket,SO_REUSEADDR
from threading import Thread
#接收消息
def recv_data(new_socket,recv_content))
   new_socket.send("thank you!".encode("gb2312"))
  else:
   print("客户端%s已经关闭" % (str(new_address)))
   break
def main():
 #创建tcp服务器套接字
 server_socket = socket(AF_INET,9999))
 #设置正常情况退出的服务器下,并且发送数据
   Thread(target=recv_data,new_address)).start()
 finally:
  #关闭tcp服务器套接字
  server_socket.close()
if __name__ == "__main__":
 main()

多任务协程实现 ――

greenlet和gevent

#coding=utf-8
from greenlet import greenlet
import time
def test1():
 while True:
  print "---A--"
  gr2.switch()
  time.sleep(0.5)
def test2():
 while True:
  print "---B--"
  gr1.switch()
  time.sleep(0.5)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
#切换到gr1中运行
gr1.switch()
import gevent
#函数
def f(n):
 for i in range(n):
  print("%s:%s" % (gevent.getcurrent(),i))

f1 = gevent.spawn(f,5)
f2 = gevent.spawn(f,5)
f3 = gevent.spawn(f,5)
#让主线程等待三个协程执行完毕,否则没有机会执行
f1.join()
f2.join()
f3.join()
#可以看到,3个greenlet是依次运行而不是交替运行。要让greenlet交替运行,可以通过gevent.sleep()交出控制权。
#coding=utf-8
import gevent
def f(n):
 for i in range(n):
  print gevent.getcurrent(),i
  #用来模拟一个耗时操作,注意不是time模块中的sleep
  gevent.sleep(1)
g1 = gevent.spawn(f,5)
g2 = gevent.spawn(f,5)
g3 = gevent.spawn(f,5)
#下面三行代码意思:主线程等待各个协成支持完,否则协成没有机会执行
g1.join()
g2.join()
g3.join()

单进程TCP服务器 ――

非堵塞式

from socket import AF_INET,socket,SOL_SOCKET
def main():
 #创建tcp的socket套接字
 server_socket = socket(AF_INET,SOCK_STREAM)
 server_socket.setsockopt(SOL_SOCKET,1)
 #绑定端口
 server_socket.bind(("",9999))
 #设置非阻塞,也就是说accept方法不阻塞了,# 但是在没有客户端链接且被执行的时候会报错
 #有客户端链接的时候正常执行
 server_socket.setblocking(False)
 #设置监听
 server_socket.listen(5)
 #客户端列表
 client_lists = []
 try:
  #不断调用accept
  while True:
   try:
   # print("accept--111")
   new_socket,new_address = server_socket.accept()
   print("accept--2222")
   except Exception as result:
   # print(result)
   pass
   else:
   print("新的客户%s链接上" % str(new_address))
   #新链接的new_sokect默认也是阻塞,也设置为非阻塞后,recv为非阻塞
   new_socket.setblocking(False)
   client_lists.append((new_socket,new_address))
   # print(111)
   for client_sokect,client_address in client_lists:
   #接收数据
   try:
    recv_data = client_sokect.recv(1024)
   except Exception as result:
    # print(result)
    pass
   else:
    # print("正常数据:%s" %recv_data)
    if len(recv_data) > 0 :
     print("收到%s:%s" % (str(client_address),recv_data))
     client_sokect.send("thank you!".encode("gb2312"))
    else:
     #客户端已经端口,要把该客户端从列表中异常
     client_lists.remove((client_sokect,new_address))
     client_sokect.close()
     print("%s已经断开" % str(new_address))
 finally:
  #关闭套接字
  server_socket.close()
if __name__ == "__main__":
 main()

单进程TCP服务器 ――

select版

select 原理

其他语言(c或者c++)也有使用select实现多任务服务器。

select 能够完成一些套接字的检查,从头到尾检查一遍后,标记哪些套接字是否可以收数据,返回的时候,就返回能接收数据的套接字,返回的是列表。select是由操作系统提供的,效率要高些,非常快的方式检测哪些套接字可以接收数据。select是跨平台的,在window也可以用。

io多路复用:没有使用多进程和多线程的情况下完成多个套接字的使用。

from socket import AF_INET,SOL_SOCKET
from select import select
import sys
def main():
 #创建tcp的socket套接字
 server_socket = socket(AF_INET,9999))
 #设置监听
 server_socket.listen(5)
 #客户端列表
 socket_lists = [server_socket,sys.stdin]
 wirte_list = []
 #是否退出
 is_run = False
 try:
  while True:
   #检测列表client_lists那些socket可以接收数据,#检测列表[]那些套接字(socket)可否发送数据
   #检测列表[]那些套接字(socket)是否产生了异常
   print("select--111")
   #这个select函数默认是堵塞,当有客户端链接的时候解除阻塞,# 当有数据可以接收的时候解除阻塞,当客户端断开的时候解除阻塞
   readable,wirteable,excep = select(socket_lists,wirte_list,[])
   # print("select--2222")
   # print(111)
   for sock in wirteable:
   #这个会一直发送,因为他是处于已经发的状态
   sock.send("thank you!".encode("gb2312"))
   for sock in readable:
   #接收数据
   if sock == server_socket:
    print("sock == server_socket")
    #有新的客户端链接进来
    new_socket,new_address = sock.accept()
    #新的socket添加到列表中,便于下次socket的时候能检查到
    socket_lists.append(new_socket)
   elif sock == sys.stdin:
    cmd = sys.stdin.readline()
    print(cmd)
    is_run = cmd
   else:
    # print("sock.recv(1024)....")
    #此时的套接字sock是直接可以取数据的
    recv_data = sock.recv(1024)
    if len(recv_data) > 0:
     print("从[%s]:%s" % (str(new_address),recv_data))
     sock.send(recv_data)
     #把链接上有消息接收的socket添加到监听写的列表中
     wirte_list.append(sock)
    else:
     print("客户端已经断开")
     #客户端已经断开,要移除
     sock.close()
     socket_lists.remove(sock)
   #是否退出程序
   if is_run:
   break
 finally:
  #关闭套接字
  server_socket.close()

if __name__ == "__main__":
 main()

单进程TCP服务器 ――

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读