1. 程式人生 > >淺談TCP通訊過程

淺談TCP通訊過程

傳輸控制協議(Transmission Control Protocol,TCP)是一種面向連線的、可靠的、基於位元組流的運輸層通訊協議。在簡化的計算機網路OSI模型中,它完成運輸層所指定的功能。TCP提供一種面向連線的、可靠的位元組流服務。
TCP通訊設定主要分為TCP的服務端和客戶端
tcp服務端

  1. 建立一個tcp流式套接字
  2. 繫結本機的IP和埠號
  3. 將套接字轉為監聽套接字
  4. 套接字等待客戶端請求
  5. 訊息的收發
    程式碼如下
#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設定為收到序號加一。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。