程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python 一個服務器監聽多個客戶端請求

編輯:Python

學習Python網絡通信的時候發現書上只有一個服務端對應一個客戶端的情形,於是自己想自己動手實現一個服務端響應多個客戶端。

首先建立服務器的socket來監聽客戶端的請求:

tcpSerSock=socket(AF_INET,SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(5)

這樣服務器的監聽socket就建好了。

接下來的思路是,如果要監聽多個客戶端,則
tcpSerSock.accept() #(accept()是阻塞式的)
必須放進一個while循環(不放進循環監聽一次就沒了)。然而,這裡就有問題了。如果按照一對一的那種處理順序,客戶端A連進來後,程序順序向下執行,服務端要再寫一個while循環,用來處理客戶端A的請求,如果此時客戶端B再來請求,服務器將接收不到。於是,很自然想到,將監聽和處理放到不同的線程進行處理。我選擇將監聽放入主線程,將處理放進子線程。該服務器功能為接收到客戶端的數據加上時間戳後返回給客戶端。服務端完整代碼如下:

#coding=utf-8
#!/usr/bin/env python
'''
author:Mr.Jing
created on Fri Sep 22 14:29:03 2017
platfrom:win10,python2.7
'''
from socket import *
from time import ctime
import threading
import time
HOST=''
PORT=2159
BUFSIZ=1024
ADDR = (HOST,PORT)
tcpSerSock=socket(AF_INET,SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(5)
socks=[] #放每個客戶端的socket
def handle():
while True:
for s in socks:
data = s.recv(BUFSIZ)
if not data:
socks.remove(s)
continue
s.send('[%s],%s' % (ctime(), data)) #加上時間戳返回
t = threading.Thread(target=handle) #子線程
if __name__ == '__main__':
t.start()
print u'我在%s線程中 ' % threading.current_thread().name #本身是主線程
print 'waiting for connecting...'
while True:
clientSock,addr = tcpSerSock.accept()
print 'connected from:', addr
socks.append(clientSock)

兩個客戶端A和B同時連接,服務器輸出:




可以看到服務端是可以相應多個客戶端的。

客戶端A發條消息試試?

服務器返回來了帶時間戳的消息,goodjob。

那另一個呢?


很棒。服務器都能做出響應。

再發一條試試?


發現服務器沒響應了,這是怎麼回事呢?

經過仔細勘察發現是因為這句:

data = s.recv(BUFSIZ)

因為recv方法是阻塞的,也就是說,當輪訓到某個客戶端,比如A,這句等待A發消息,不發消息就不往下走,所以此時B再去發消息的話服務器就收不到了。

那該怎麼辦呢?

很容易想到,將其設為非阻塞就好了。但上網找了好久,雖然找到可以用setblocking(0)  將套接字設為非阻塞,然而具體怎麼用卻很少有人講清楚。查資料後終於搞明白,非阻塞的recv方法會繼續向下執行,若超時得不到數據則會拋出異常。

修改後的代碼如下:

#coding=utf-8
#!/usr/bin/env python
'''
author:Mr.Jing
created on Fri Sep 22 14:29:03 2017
platfrom:win10,python2.7
'''
from socket import *
from time import ctime
import threading
import time
HOST=''
PORT=2159
BUFSIZ=1024
ADDR = (HOST,PORT)
tcpSerSock=socket(AF_INET,SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(5)
socks=[] #放每個客戶端的socket
def handle():
while True:
for s in socks:
try:
data = s.recv(BUFSIZ) #到這裡程序繼續向下執行
except Exception, e:
continue
if not data:
socks.remove(s)
continue
s.send('[%s],%s' % (ctime(), data))
t = threading.Thread(target=handle) #子線程
if __name__ == '__main__':
t.start()
print u'我在%s線程中 ' % threading.current_thread().name #本身是主線程
print 'waiting for connecting...'
while True:
clientSock,addr = tcpSerSock.accept()
print 'connected from:', addr
clientSock.setblocking(0)
socks.append(clientSock)

此時再去嘗試:


客戶端代碼:客戶端代碼

客戶端隨便輸都會得到響應。任務達成!

ps:剛寫博客,不是太習慣這個編輯器,排版略丑,請忽略~


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved