自己实现多线程的socket,socketserver源码剖析
1,IO多路復(fù)用
三種多路復(fù)用的機(jī)制:select、poll、epoll
用的多的兩個(gè):select和epoll
簡(jiǎn)單的說就是:
1,select和poll所有平臺(tái)都支持,epoll只有l(wèi)inux支持
2,select效率不高,epoll效率高
3,IO多路復(fù)用用來監(jiān)聽socket對(duì)象內(nèi)部是否變化
4,要調(diào)用select模塊
?
什么是文件描述符:
當(dāng)程序打開一個(gè)現(xiàn)有文件或者創(chuàng)建一個(gè)新文件時(shí),內(nèi)核向進(jìn)程返回一個(gè)文件描述符。文件描述符的有效范圍是 0 到 OPEN_MAX
linux查看OPEN_MAX的方法:ulimit -n。linux默認(rèn)最大文件描述符為1024,一般可以將它改為65535,可以使用命令ulimit -HSn ?65535。也可以保存到/etc/security/ulimit.conf里
?
?
?
?
python的select函數(shù):
?
def select(rlist, wlist, elist, timeout=None)->(rl,wl,el)
rlist獲取變化的句柄添加到rl中
只要wlist有句柄,都放到wl中
當(dāng)elist某個(gè)句柄發(fā)生錯(cuò)誤時(shí),放到el中
timeout,如果沒有設(shè)置超時(shí)時(shí)間,select一直會(huì)卡著,如果設(shè)置timeout=1,表示句柄沒有變化時(shí),select會(huì)卡1秒,一有變化就執(zhí)行
?
在socket中
select用來監(jiān)聽sk連接時(shí)候的句柄和收發(fā)數(shù)據(jù)的句柄
import socket import select sk=socket.socket() sk.bind(("127.0.0.1",9999)) sk.listen(2) inputs=[sk] outputs=[] msg={} while True:rlist,wlist,e=select.select(inputs,outputs,[],1)print(len(inputs),len(rlist),len(wlist),len(outputs))'''只要有連接進(jìn)來,就接收,只要有收發(fā)消息的句柄發(fā)生變化,就收消息,收到消息后可以不直接發(fā)送,存到outputs里,wlist==outputs,只要outputs里有句柄,就交給wlist去循環(huán)發(fā)送'''for r in rlist:if r==sk:conn,addr=r.accept()inputs.append(conn)else:try:rt=r.recv(1024)outputs.append(r)print(rt)msg.setdefault(r, [])msg[r].append(rt)except:inputs.remove(r)del msg[r]#上面如果沒有數(shù)據(jù),這邊就一直發(fā)數(shù)據(jù)給客戶端,直到客戶端接收消息,得到的是一大串for w in wlist:for m in msg[w]:w.sendall(m)outputs.remove(w) #所以發(fā)送完這邊要?jiǎng)h除句柄,解決了無限發(fā)數(shù)據(jù)的問題?
socketserver源碼剖析
通過上面的實(shí)驗(yàn)得出規(guī)律:一開始建立socket對(duì)象到listen這幾步都沒變,直到accept之間在循環(huán)使用select檢查sk是否變化,如果有新鏈接進(jìn)來就accept,之后就能通信了。
import socketserverclass MyServer(socketserver.BaseRequestHandler):def handle(self):# print self.request,self.client_address,self.serverconn = self.requestwhile True:recv_data=conn.recv(5)conn.sendall(recv_data)if __name__ == '__main__':server = socketserver.ThreadingTCPServer(('127.0.0.1',8009),MyServer)print(server.server_address)server.serve_forever() 這是socketserver服務(wù)器端實(shí)現(xiàn)代碼第一步:建立socket對(duì)象到listen這幾步肯定在創(chuàng)建構(gòu)造函數(shù)的時(shí)候已經(jīng)做掉了。
第二步:因?yàn)樗嵌嗑€程,select查看有sk變動(dòng)后,每次有連接進(jìn)來就分配一個(gè)線程給sk,然后accept連接
第三步:在accept之后調(diào)用socketserver.BaseRequestHandler來收發(fā)消息,在調(diào)用MySocket的handle,也就是調(diào)用BaseRequestHandler的handle
第四步:forever()
ThreadingTCPServer的繼承關(guān)系:
?
?
?
import socket import select import threadingdef handle(sk):'''此處發(fā)送'''sk.sendall(bytes("hello",encoding="utf-8"))sk=socket.socket() sk.bind(("127.0.0.1",9999)) sk.listen(5) while True:rlist,w,e=select.select([sk],[],[],1)for r in rlist:conn,addr=r.accept()th=threading.Thread(target=handle,args=(conn,))th.daemon=Falseth.start() sk.close() socketserver簡(jiǎn)化 import socketsk=socket.socket() sk.connect(("127.0.0.1",9999)) print(sk.recv(1024)) sk.close() client?
轉(zhuǎn)載于:https://www.cnblogs.com/euewrqe/p/5880052.html
總結(jié)
以上是生活随笔為你收集整理的自己实现多线程的socket,socketserver源码剖析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu设置静态ip地址
- 下一篇: gridview汇出EXCEL (Ex