并发服务器
在实际的应用过程中,一个服务器总是对应不止一个客户端,并及时地为已经连上的客户端提供服务。传统的单进程服务模型已经不能满足这样的需求,因此利用多进程和多线程可以实现这样的需求。
创建TCP服务器的流程
- 创建TCP套接字;
- 将套接字与服务端口绑定;
- listen(),开始监听客户端的请求;
- accept(),接受一个链接请求,得到新的套接字和客户端的地址;
- 使用新的套接字收发数据,通信结束后关闭套接字;
以上步骤是一个基本的模型,在实际编程过程中我们可以发现accept()是一个阻塞函数,即如果没有客户端的请求,程序会一直停在这个地方;recv()函数也是一个阻塞函数,如果没有客户端的数据到来,服务器会一直等到当前连接对应的客户端发送数据。因此一个并发服务器必须解决这些问题,即:如果有客户端链接建立后,创建一个新的进程或者线程用于当前的连接收发数据,父进程中原来的套接字依旧监听是否有其他的客户端请求。
多进程TCP服务器
from socket import *
from multiprocessing import Process
def dealClient(newSocket,cliAddr):
while True:
recvData = newSocket.recv(1024)
if len(recvData):
print('收到来自%s的消息:%s' % (str(cliAddr), recvData))
else:
#如果数据长度为0 说明客户端断开连接,此时跳出循环关闭套接字
print('客户端%s下线了......' % (str(cliAddr)))
break
newSocket.close()
def main():
tcpSocket = socket(AF_INET,SOCK_STREAM)
#设置套接字可以地址重用
tcpSocket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
serverAddr = ('127.0.0.1',8888)
tcpSocket.bind(serverAddr)
tcpSocket.listen(100)
#设置监听队列长度,在linux中这个值没有太大意义,kernel有自己的值
#为了防止服务端异常退出,因此使用try...finally,确保创建的套接字可以正常关闭
try:
while True:
# 在主进程中不断接收新的连接请求
newSocket,cliAddr = tcpSocket.accept()
#创建子进程处理已经建立好的连接,tcpSocket依旧去监听是否有新的请求。
p1 = Process(target=dealClient,args=(newSocket,cliAddr))
p1.start()
newSocket.close()
#创建子进程后,父进程也同样拥有newSocekt,但是子进程使用accept()返回的新套接字发送数据,因此父进程要把newSocket关闭
except:
pass
finally:
tcpSocket.close()
if __name__ == '__main__':
main()
结果:
收到来自('127.0.0.1', 57281)的消息:b'http://www.cmsoft.cn fromA'
收到来自('127.0.0.1', 57288)的消息:b'http://www.cmsoft.cn from B'
收到来自('127.0.0.1', 57295)的消息:b'http://www.cmsoft.cn from C'
客户端('127.0.0.1', 57281)下线了......
客户端('127.0.0.1', 57288)下线了......
客户端('127.0.0.1', 57295)下线了......
利用网络调试助手连接服务器可以测试结果。
多线程TCP服务器
把上面的进程换成线程就是多线程的服务器。
from socket import *
from threading import Thread
def dealClient(newSocket,cliAddr):
while True:
recvData = newSocket.recv(1024)
if len(recvData):
print('收到来自%s的消息:%s' % (str(cliAddr), recvData))
else:
#如果数据长度为0 说明客户端断开连接,此时跳出循环关闭套接字
print('客户端%s下线了......' % (str(cliAddr)))
break
newSocket.close()
def main():
tcpSocket = socket(AF_INET,SOCK_STREAM)
#设置套接字可以地址重用
tcpSocket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
serverAddr = ('127.0.0.1',8888)
tcpSocket.bind(serverAddr)
tcpSocket.listen(100)
#设置监听队列长度,在linux中这个值没有太大意义,kernel有自己的值
#为了防止服务端异常退出,因此使用try...finally,确保创建的套接字可以正常关闭
try:
while True:
# 在线程中不断接收新的连接请求
newSocket,cliAddr = tcpSocket.accept()
#创建子线程处理已经建立好的连接,tcpSocket依旧去监听是否有新的请求。
t1 = Thread(target=dealClient,args=(newSocket,cliAddr))
t1.start()
except:
pass
finally:
tcpSocket.close()
if __name__ == '__main__':
main()