python 并发服务器1

  • 1.单进程tcp服务器
    • 1.当使用recv接收数据时,如果接收到的数据为空,则意味这客户端已经关闭,因此服务器需要判断recf接收数据是否为空,来判断客户端是否下线
    • 2.单进程tcp服务器只能同时为一个客户端服务
    • from socket import *
    • #创建tcp服务器套接字
    • server_socket = socket(AF_INET,SOCK_STREAM) #创建在不同电脑之间通信的tcp套接字
    • #绑定端口
    • 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()
  • 2.多线程tcp服务器
    • from socket import socket, AF_INET,SOCK_STREAM,SOL_SOCKET,SO_REUSEADDR
    • from threading import Thread
    • #接收消息
    • def recv_data(new_socket,new_address):
      • 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
    • def main():
      • #创建tcp服务器套接字
      • server_socket = socket(AF_INET,SOCK_STREAM)
      • #绑定端口
      • server_socket.bind(("",9999))
      • #设置正常情况退出的服务器下,端口可以重用
      • server_socket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
      • #设置监听,变为被动连接
      • server_socket.listen(3)
      • try:
        • while True:
        • # 等待客户端的链接,返回新的socket和地址
          • new_socket,new_address = server_socket.accept()
          • #接收数据,并且发送数据
          • Thread(target=recv_data,args=(new_socket,new_address)).start()
      • finally:
        • #关闭tcp服务器套接字
        • server_socket.close()
    • if __name__ == "__main__":
      • main()
    • 1.多线程tcp服务器解决了当为一个客户服务的时候,不能为另外的客户服务。但是线程之间共享数据,不安全,
  • 3.多进程tcp服务器
    • 1.解决了当为一个客户服务的时候,不能为另外的客户服务。
    • 2.多进程服务器和多线程逻辑一样,但是多进程,进程个数限制,且占用资源多
  • 4.单进程非堵塞式tcp服务器
    • from socket import *
    • def main():
      • #创建tcp的socket套接字
      • server_socket = socket(AF_INET,SOCK_STREAM)
      • server_socket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
      • #绑定端口
      • server_socket.bind(("",9999))
      • #设置非阻塞,也就是说accept方法不阻塞了,
      • # 但是在没有客户端链接且被执行的时候会报错
      • #有客户端链接的时候正常执行
      • server_socket.setblocking(False)
      • #设置监听
      • server_socket.listen(5)
      • #客户端列表
      • client_lists = []
      • try:
        • #不断调用accept
        • while True:
          • try:
            • new_socket,new_address = server_socket.accept()
          • except Exception as result:
            • print(result)
          • else:
            • print("新的客户%s链接上" % str(new_address))
            • #新链接的new_sokect默认也是阻塞,也设置为非阻塞后,recv为非阻塞
            • new_socket.setblocking(False)
            • client_lists.append((new_socket,new_address))
            • for client_sokect,client_address in client_lists:
              • #接收数据
              • try:
                • recv_data = client_sokect.recv(1024)
              • except Exception as result:
                • print(result)
              • else:
                • 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()
    • 总结:用非堵塞单进程实现了多进程并发服务器的作用,这种确实在访问量不是很大的情况(10个链接作用)下可以采用该方式,但如果访问量到1000以上后,性能就会有问题
      • 一直while True 消耗cpu资源大
  • 5.单进程tcp服务器 select版
    • 1.原理:没有使用多进程和多线程的情况下完成多个套接字的使用。
      • select 能够完成一些套接字的检查,从头到尾检查一遍后,标记哪些套接字是否可以收数据,返回的时候,就返回能接收数据的套接字,返回的是列表。
      • select是由操作系统提供的,效率要高些,非常快的方式检测哪些套接字可以接收数据。select是跨平台的,在window也可以用。
    • 2.代码
      • from socket import *
      • from select import *
      • import sys
      • sever_socket=socket(AF_INET,SOCK_STREAM)
      • sever_socket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
      • sever_socket.bind(("",9999))
      • sever_socket.listen(5)
      • sock_list=[sever_socket]
      • writeable_list=[]
      • try:
        • while True:
          • read_list,write_list,err_list=select(sock_list,writeable_list,sock_list)
          • # print(read_list,write_list,err_list)
          • for sock in write_list:
            • sock.send("hello wrold")
          • for sock in read_list:
            • if sock==sever_socket:
              • new_sock,new_addr=sock.accept()
              • sock_list.append(new_sock)
            • elif sock==sys.stdin:
              • msg=sys.stdin.readline()
              • print("键盘输入",msg)
            • else:
              • recv_msg=sock.recv(1024)
              • if len(recv_msg)>0:
                • print("收到消息",recv_msg.decode("utf-8"))
                • sock.send(recv_msg)
                • writeable_list.append(sock)
              • else:
                • print("客户端关闭")
                • sock.close()
                • sock_list.remove(sock)
      • except:
        • pass
      • finally:
        • sever_socket.close()
    • 3.缺点:
      • 1.单个进程能够监视的文件描述符存在限制,32位1024,64位2048
      • 2.对socket扫描是一次扫描即采用轮询的方式,效率低
      • 3.当套接字比较多的时候,每次select()都要通过遍历列表个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。

猜你喜欢

转载自blog.csdn.net/qq_41654985/article/details/80426605
今日推荐