python使用socket實現TCP協議長連接框架
分析多瞭協議就會發現,很多的應用,特別是遊戲類和IM類應用,它們的協議會使用長連接的方式,來保持客戶端與服務器的聯系,這些長連接,通常是TCP承載的。
如果我們要模擬這個客戶端的行為,根據不同應用服務器的實現情況,有些長連接不是必須的,但有些長連接,就必須去實現它。例如最近分析的某應用,雖然它主要使用HTTP協議進行交互,但它在TCP長連接中傳輸瞭一些必須的信息,如果不實現長連接,就會有很多信息無法處理。
在python中,很容易實現HTTP協議,當然,也容易實現TCP協議,它的TCP實現,使用socket
庫就可以瞭,隻是需要註意,TCP長連接中通常傳輸的是十六進制數據,協議非標準的,需要自行根據協議分析結果來封裝數據格式。
這裡以一個使用到TCP長連接的協議為樣例,來給出協議的TCP長連接框架,大傢有需要可以參考實現,當然,代碼也是從樣例中摘出來的,並不是完整的。
TCP長連接框架,首先是外部的包裝,初始化一些參數,例如長連接使用到的ip端口及socket套接字等:
self.longip='im.langren001.com' self.longport= 6656 self.threadLock = threading.Lock() self.sockmain = socket.socket(socket.AF_INET, socket.SOCK_STREAM); self.longlinktcpstart2() tlonglink = threading.Thread(target=lrsuser.longlinktcpth2,name='mainlink_'+ self.playinfo['uid'], args=(self,)) tlonglink.start() self.threadinfo.append(tlonglink)
這個裡面調用瞭兩個函數,一個是longlinktcpstart2
函數,作用是建立socket連接,並對一些連接建立初始時的交互進行實現,另一個是longlinktcpth2函數,是一個線程,實現對連接內的數據進行收發處理。一般來說,這兩個可以在一起實現,但為瞭方便socket異常斷開的處理,分成瞭兩個函數。
longlinktcpstart2的實現如下:
def longlinktcpstart2(self): server_address = (self.longip, int(self.longport)) self.savelogs('longlinktcpstart2', 'Connecting to %s:%d.' % server_address) self.sockmain.connect(server_address) self.databuf = b'' message = genbaseinfo.genalive() self.sockmain.sendall(message) message = genbaseinfo.genfirstdata() if len(message)==0: self.savelogs('longlinktcpstart2', 'genfirstdata error ') return False self.sockmain.sendall(message) self.longlinkcnt=2 cnt = 0 while (cnt < 2): try: buf = self.sockmain.recv(2048) sz = len(buf) self.savelogs('longlinktcpstart2', "recv data len "+str(sz) ) if sz > 0: self.databuf +=buf self.dealdatabuf() if cnt == 0: alivemsg = genbaseinfo.genalive() self.sockmain.sendall(alivemsg) self.savelogs('longlinktcpstart2', "sendalive") regtime=int(round(time.time() * 1000))-random.randint(14400000,25200000) regtime=regtime*1000 pcode = self.versionstr + '.0' message = genbaseinfo.genseconddata() if len(message) == 0: self.savelogs('longlinktcpstart2', 'genseconddata error ') return False self.sockmain.sendall(message) self.longlinkcnt = self.longlinkcnt + 1 elif cnt == 1: pcode = self.versionstr + '.0' message = genbaseinfo.genotherdata() if len(message) == 0: self.savelogs('longlinktcpstart2', 'genthirddata error ') return False self.sockmain.sendall(message) self.longlinkcnt = self.longlinkcnt + 1 cnt = cnt + 1 else: self.savelogs('longlinktcpstart2', 'recv data alive') except: # socket.error self.savelogs('longlinktcpstart2', 'socket error,do connect fail') return False return True
這裡面的genbaseinfo
相關的函數可以忽略,是用來生成發送的消息數據的實現,用自己的函數去替換即可。dealdatabuf
函數是用來處理收到的消息數據實現,這兩個都要根據具體的協議分析情況去實現,註意,生成的用來發送的數據和接收到的需要處理的數據,都需要按十六進制處理,這裡不做詳述。
線程longlinktcpth2是一個循環,協議不退出,循環不結束,實現如下:
def longlinktcpth2(self): tmalive = 0; r_inputs = set() r_inputs.add(self.sockmain) w_inputs = set() w_inputs.add(self.sockmain) e_inputs = set() e_inputs.add(self.sockmain) tm=int(round(time.time())) self.savelogs('longlinktcpth2', 'enter' ) while (self.quitflag==0): try: r_list, w_list, e_list = select.select(r_inputs, w_inputs, e_inputs, 1) for event in r_list: try: buf = event.recv(2048) sz = len(buf) self.savelogs('longlinktcpth2', "loop recv data len:"+ str(sz) ) if sz > 0: self.databuf += buf self.dealdatabuf() alivemsg = genbaseinfo.genalive() self.sockmain.sendall(alivemsg) self.savelogs('longlinktcpth2', "sendalive") else: self.savelogs('longlinktcpth2', "遠程斷開連接,do reconnect") r_inputs.clear() time.sleep(3) self.sockmain = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.longlinktcpstart2() r_inputs = set() r_inputs.add(self.sockmain) w_inputs = set() w_inputs.add(self.sockmain) e_inputs = set() e_inputs.add(self.sockmain) except Exception as e: self.savelogs('longlinktcpth2', str(e)) self.threadLock.acquire() if (len(self.msglist) > 0): msg = self.msglist.pop(0) self.threadLock.release() self.sockmain.sendall(msg) self.savelogs('longlinktcpth2',"send a msg") else: self.threadLock.release() tmnow=int(round(time.time())) if tmnow-tm>30: message = genbaseinfo.genotherdata() if len(message) == 0: self.savelogs('longlinktcpth2', 'genalivedata error ') return False self.sockmain.sendall(message) self.savelogs('longlinktcpth2', "send alivemsg"+str(self.longlinkcnt)) self.longlinkcnt = self.longlinkcnt + 1 #這個要一條連接統一,不能亂,回頭加鎖 tm=tmnow if len(w_list) > 0: # 產生瞭可寫的事件,即連接完成 self.savelogs('longlinktcpth2',str(w_list)) w_inputs.clear() # 當連接完成之後,清除掉完成連接的socket if len(e_list) > 0: # 產生瞭錯誤的事件,即連接錯誤 self.savelogs('longlinktcpth2', str(e_list)) e_inputs.clear() # 當連接有錯誤發生時,清除掉發生錯誤的socket except OSError as e: self.savelogs('longlinktcpth2', 'socket error,do reconnect') time.sleep(3) self.sockmain = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.longlinktcpstart2() r_inputs = set() r_inputs.add(self.sockmain) w_inputs = set() w_inputs.add(self.sockmain) e_inputs = set() e_inputs.add(self.sockmain) self.savelogs('longlinktcpth2', 'leave')
由於這個代碼主要是在windows
上使用,因此,longlinktcpth2
線程采用瞭select來實現,而沒有使用epoll。在循環中,對異常進行瞭處理,如果發生異常,連接被斷開,則調用longlinktcpstart2重新連接,而不退出循環,其餘的和longlinktcpstart2
裡面一致。
由於TCP連接是流的概念,因此,需要對數據進行緩存拼接,這就是上面代碼中databuf的作用,防止每次收到的數據不完整或者太多,方便後續的處理,這才是一個合格的碼農的信仰的自我升華。
到此這篇關於python使用socket實現TCP協議長連接框架的文章就介紹到這瞭,更多相關python使用socket實現TCP協議長連接框架內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- python socket多線程實現客戶端與服務器連接
- Python實現UDP與TCP通信的示例詳解
- python實現套接字創建
- Python實現多任務版的udp聊天器
- Python中的socket網絡模塊介紹