自己实现多线程的socket,socketserver源码剖析
1,IO多路復用
三種多路復用的機制:select、poll、epoll
用的多的兩個:select和epoll
簡單的說就是:
1,select和poll所有平臺都支持,epoll只有linux支持
2,select效率不高,epoll效率高
3,IO多路復用用來監聽socket對象內部是否變化
4,要調用select模塊
?
什么是文件描述符:
當程序打開一個現有文件或者創建一個新文件時,內核向進程返回一個文件描述符。文件描述符的有效范圍是 0 到 OPEN_MAX
linux查看OPEN_MAX的方法:ulimit -n。linux默認最大文件描述符為1024,一般可以將它改為65535,可以使用命令ulimit -HSn ?65535。也可以保存到/etc/security/ulimit.conf里
?
?
?
?
python的select函數:
?
def select(rlist, wlist, elist, timeout=None)->(rl,wl,el)
rlist獲取變化的句柄添加到rl中
只要wlist有句柄,都放到wl中
當elist某個句柄發生錯誤時,放到el中
timeout,如果沒有設置超時時間,select一直會卡著,如果設置timeout=1,表示句柄沒有變化時,select會卡1秒,一有變化就執行
?
在socket中
select用來監聽sk連接時候的句柄和收發數據的句柄
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))'''只要有連接進來,就接收,只要有收發消息的句柄發生變化,就收消息,收到消息后可以不直接發送,存到outputs里,wlist==outputs,只要outputs里有句柄,就交給wlist去循環發送'''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]#上面如果沒有數據,這邊就一直發數據給客戶端,直到客戶端接收消息,得到的是一大串for w in wlist:for m in msg[w]:w.sendall(m)outputs.remove(w) #所以發送完這邊要刪除句柄,解決了無限發數據的問題?
socketserver源碼剖析
通過上面的實驗得出規律:一開始建立socket對象到listen這幾步都沒變,直到accept之間在循環使用select檢查sk是否變化,如果有新鏈接進來就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服務器端實現代碼第一步:建立socket對象到listen這幾步肯定在創建構造函數的時候已經做掉了。
第二步:因為它是多線程,select查看有sk變動后,每次有連接進來就分配一個線程給sk,然后accept連接
第三步:在accept之后調用socketserver.BaseRequestHandler來收發消息,在調用MySocket的handle,也就是調用BaseRequestHandler的handle
第四步:forever()
ThreadingTCPServer的繼承關系:
?
?
?
import socket import select import threadingdef handle(sk):'''此處發送'''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簡化 import socketsk=socket.socket() sk.connect(("127.0.0.1",9999)) print(sk.recv(1024)) sk.close() client?
轉載于:https://www.cnblogs.com/euewrqe/p/5880052.html
總結
以上是生活随笔為你收集整理的自己实现多线程的socket,socketserver源码剖析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu设置静态ip地址
- 下一篇: gridview汇出EXCEL (Ex