- 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
- while True:
- 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
- while True:
- 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))
- if len(recv_data) > 0 :
- try:
- 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)
- if sock==sever_socket:
- while True:
- except:
- pass
- finally:
- sever_socket.close()
- 3.缺点:
- 1.单个进程能够监视的文件描述符存在限制,32位1024,64位2048
- 2.对socket扫描是一次扫描即采用轮询的方式,效率低
- 3.当套接字比较多的时候,每次select()都要通过遍历列表个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。
- 1.原理:没有使用多进程和多线程的情况下完成多个套接字的使用。
python 并发服务器1
最新推荐文章于 2023-05-29 15:53:33 发布