學(xué)習(xí)python網(wǎng)絡(luò)通信的時(shí)候發(fā)現(xiàn)書上只有一個(gè)服務(wù)端對(duì)應(yīng)一個(gè)客戶端的情形,于是自己想自己動(dòng)手實(shí)現(xiàn)一個(gè)服務(wù)端響應(yīng)多個(gè)客戶端。
首先建立服務(wù)器的socket來監(jiān)聽客戶端的請(qǐng)求:
1
2
3
|
tcpsersock = socket(af_inet,sock_stream) tcpsersock.bind(addr) tcpsersock.listen( 5 ) |
這樣服務(wù)器的監(jiān)聽socket就建好了。
接下來的思路是,如果要監(jiān)聽多個(gè)客戶端,則
1
|
tcpsersock.accept() #(accept()是阻塞式的) |
必須放進(jìn)一個(gè)while循環(huán)(不放進(jìn)循環(huán)監(jiān)聽一次就沒了)。然而,這里就有問題了。如果按照一對(duì)一的那種處理順序,客戶端a連進(jìn)來后,程序順序向下執(zhí)行,服務(wù)端要再寫一個(gè)while循環(huán),用來處理客戶端a的請(qǐng)求,如果此時(shí)客戶端b再來請(qǐng)求,服務(wù)器將接收不到。于是,很自然想到,將監(jiān)聽和處理放到不同的線程進(jìn)行處理。我選擇將監(jiān)聽放入主線程,將處理放進(jìn)子線程。該服務(wù)器功能為接收到客戶端的數(shù)據(jù)加上時(shí)間戳后返回給客戶端。服務(wù)端完整代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
#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 = [] #放每個(gè)客戶端的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)) #加上時(shí)間戳返回 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) |
兩個(gè)客戶端a和b同時(shí)連接,服務(wù)器輸出:
可以看到服務(wù)端是可以相應(yīng)多個(gè)客戶端的。
客戶端a發(fā)條消息試試?
服務(wù)器返回來了帶時(shí)間戳的消息,goodjob。
那另一個(gè)呢?
很棒。服務(wù)器都能做出響應(yīng)。
再發(fā)一條試試?
發(fā)現(xiàn)服務(wù)器沒響應(yīng)了,這是怎么回事呢?
經(jīng)過仔細(xì)勘察發(fā)現(xiàn)是因?yàn)檫@句:
1
|
data = s.recv(bufsiz) |
因?yàn)閞ecv方法是阻塞的,也就是說,當(dāng)輪訓(xùn)到某個(gè)客戶端,比如a,這句等待a發(fā)消息,不發(fā)消息就不往下走,所以此時(shí)b再去發(fā)消息的話服務(wù)器就收不到了。
那該怎么辦呢?
很容易想到,將其設(shè)為非阻塞就好了。但上網(wǎng)找了好久,雖然找到可以用setblocking(0) 將套接字設(shè)為非阻塞,然而具體怎么用卻很少有人講清楚。查資料后終于搞明白,非阻塞的recv方法會(huì)繼續(xù)向下執(zhí)行,若超時(shí)得不到數(shù)據(jù)則會(huì)拋出異常。
修改后的代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
#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 = [] #放每個(gè)客戶端的socket def handle(): while true: for s in socks: try : data = s.recv(bufsiz) #到這里程序繼續(xù)向下執(zhí)行 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) |
此時(shí)再去嘗試:
客戶端隨便輸都會(huì)得到響應(yīng)。任務(wù)達(dá)成!
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://blog.csdn.net/qq_34062683/article/details/78063035