淺談TCP通訊過程
傳輸控制協議(Transmission Control Protocol,TCP)是一種面向連線的、可靠的、基於位元組流的運輸層通訊協議。在簡化的計算機網路OSI模型中,它完成運輸層所指定的功能。TCP提供一種面向連線的、可靠的位元組流服務。
TCP通訊設定主要分為TCP的服務端和客戶端
tcp服務端
- 建立一個tcp流式套接字
- 繫結本機的IP和埠號
- 將套接字轉為監聽套接字
- 套接字等待客戶端請求
- 訊息的收發
程式碼如下
#import RPi.GPIO as GPIO from _socket import socket from socketserver import StreamRequestHandler, ThreadingTCPServer import time import threading class LedTcpServer(ThreadingTCPServer): def __init__(self,serveraddress,ClinetDataHandler): self.daemon_threads = True self.allow_reuse_address = True ThreadingTCPServer.__init__(self,serveraddress,ClientDataHandler) def listClient(self): if (self._wfiles.count()>0): index = 0 for wfile in self._wfiles : print(index+":"+wfile+"\r\n") index += 1 def write(self,socketfile,ledCmd): if (self.wfile.closed!=True): self.wfile.writeline(ledCmd) class ClientDataHandler(StreamRequestHandler): _clientlist = [] _wfiles = [] _clientdata = "" def handle(self): #StreamRequestHandler.handle(self) _Flag = True while _Flag: _clientAdd = self.client_address _wfile = self.wfile if (_wfile not in self._wfiles): self._wfiles.append(_wfile) self._clientlist.append(_clientAdd) msg = "\r\n"+str(_clientAdd)+"連線成功" print(msg) def finish(self): _clientAdd = self.client_address if (_clientAdd in self._clientlist): self.clientlist.remove(_clientAdd) def CmdInterface(): Flag = True while Flag: try: controller = input("cmd>") if (controller!=""): # if (controller.strip().lower()=="on"): # socketfile = input("please choice a socket useage list cmd") # LedTcpServer.write("on") # elif (controller.strip().lower()=="off"): # LedTcpServer.write("off") if (controller.strip().lower()=="help"): print("firset useage list cmd to get the client ip and port") print("then useage cmd index:cmd to controller the led") print("cmd index:1 LedOn") print("cmd index:2 LedOff") print("cmd index:other number do onthing!") elif (controller.strip().lower()=="list"): #print(ClientDataHandler._wfiles) print("please select a index\r\n") print("index:socket") index = 0 #print(ClientDataHandler._clientlist) out = {} for _clientadd in ClientDataHandler._clientlist: out[index] = _clientadd index +=1 print(out) #_wfile.write(b"hello") elif (":" in controller): print(controller) ledControllcmd = controller.split(":") index = ledControllcmd[0] ledcmd = ledControllcmd[1] print(ledcmd) if (str(ledcmd).isnumeric() and str(index).isnumeric()): #ClientDataHandler._wfiles[int(index)].write(b"on1") if (str(ledcmd)=="1"): ClientDataHandler._wfiles[int(index)].write(b"on") if (str(ledcmd)=="2"): ClientDataHandler._wfiles[int(index)].write(b"off") else: break else: print("please input data") except Exception: pass if (__name__=="__main__"): print("服務開啟1") cmd = threading.Thread(target=CmdInterface); cmd.setDaemon(False) cmd.start() address = ('',11235) # 該方法繼承自 TcpServer的 __init__,ThreadingTCPServer繼承自(ThreadingMixIn, TCPServer),serve_forever()為Tcpserver父類的BaseServer的方法 ledServer = LedTcpServer(address,ClientDataHandler) ledServer.serve_forever()
tcp客戶端
建立客戶端套接字要和訪問的伺服器的套接字型別相同。
連線伺服器
和伺服器進行通訊
關閉套接字
程式碼如下
import RPi.GPIO as GPIO import socket import threading class LedNetClient: def __init__(self,remoteHost=('127.0.0.1',11235)): try: client = socket.socket() client.connect(remoteHost) print("啟動成功") Flag = True while Flag: ledcmd = client.recv(1024) ledcmd = ledcmd.strip().decode() # print(ledcmd+"a") if (ledcmd=="on"): print("get server cmd:"+ledcmd) ledcontr = LedOpreator() ledcontr.ledControll("on") elif(ledcmd=="off"): print("get server cmd:"+ledcmd) ledcontr = LedOpreator() ledcontr.ledControll("off") else: pass except Exception: print(Exception) finally: pass class LedOpreator: pin = 11 def __init__(self,pin=11): self.pin = pin GPIO.setmode(GPIO.BOARD) GPIO.setwarnings(False) GPIO.setup(self.pin,GPIO.OUT) def ledControll(self,cmd="on"): if (cmd.lower()=="on"): GPIO.output(self.pin,GPIO.HIGH) elif(cmd.lower()=="off"): GPIO.output(self.pin,GPIO.LOW) else: pass def dosInterface(): Flag = True print("please input the remote server ip and port like this:127.0.0.1:10000") while Flag: remoteHost = input("REMOTEHOST>") if (":" in str(remoteHost)): remoteHost = remoteHost.split(":") ip = remoteHost[0] port = remoteHost[1] hostname =(ip,int(port)) print(hostname) ledclient = LedNetClient(hostname) # ledclient.handlerData(ledclient) elif(remoteHost.strip()=="1"): ledcontr = LedOpreator() ledcontr.ledControll("on") elif(remoteHost.strip()=="2"): ledcontr = LedOpreator() ledcontr.ledControll("off") else: ledclient = LedNetClient() # print("please input the remote server ip and port like this:127.0.0.1:10000") if (__name__=="__main__"): dosui = threading.Thread(target=dosInterface) dosui.setDaemon(False) dosui.start()
Tcp整個通訊過程中的幾次握手
建立連線時的三次握手
TCP是主機對主機層的傳輸控制協議,提供可靠的連線服務,採用三次握手確認建立一個連線:
位碼即tcp標誌位,有6種標示:SYN(synchronous建立聯機) ACK(acknowledgement 確認) PSH(push傳送) FIN(finish結束) RST(reset重置) URG(urgent緊急)
Sequence number(順序號碼) Acknowledge number(確認號碼)
第一次握手:主機A傳送位碼為syn=1,隨機產生seq number=1234567的資料包到伺服器,主機B由SYN=1知道,A要求建立聯機;
第二次握手:主機B收到請求後要確認聯機資訊,向A傳送ack number=(主機A的seq+1),syn=1,ack=1,隨機產生seq=7654321的包
第三次握手:主機A收到後檢查ack number是否正確,即第一次傳送的seq number+1,以及位碼ack是否為1,若正確,主機A會再發送ack number=(主機B的seq+1),ack=1,主機B收到後確認seq值與ack=1則連線建立成功。
完成三次握手,主機A與主機B開始傳送資料。
結束連線的四次握手
第一次,客戶端傳送一個FIN,用來關閉客戶端到伺服器的資料傳送,然後等待伺服器的確認。其中終止標誌位FIN=1,序列號seq=u。
第二次,伺服器收到這個FIN,它傳送一個ACK,確認ack為收到的序號加一。
第三次,關閉伺服器到客戶端的連線,傳送一個FIN給客戶端。
第四次,客戶端收到FIN後,併發回一個ACK報文確認,並將確認序號seq設定為收到序號加一。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。