传输控制协议(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设置为收到序号加一。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。