TCP三次握手、四次挥手图
三次握手
- 第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN(c)。此时客户端处于 SYN_Send 状态。
第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s)。同时会把客户端的 ISN + 1 作为 ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_REVD 的状态。
- 第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 establised 状态。
服务器收到 ACK 报文之后,也处于 establised 状态,此时,双方已建立起了链接。
四次挥手
刚开始双方都处于 establised 状态,假如是客户端先发起关闭请求,则:
- 第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。
- 第二次握手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。
- 第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
- 第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态。服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
简明理解三次握手
第一次握手:Client什么都不能确认;Server确认了对方发送正常
第二次握手:Client确认了:自己发送、接收正常,对方发送、接收正常;Server确认了:自 己接收正常,对方发送正常
第三次握手:Client确认了:自己发送、接收正常,对方发送、接收正常;Server确认了:自己发送、接收正常,对方发送接收正常
基于TCP开发一款远程CMD程序
客户端连接服务器后,可以向服务器发送命令
服务器收到命令后执行,无论执行是否成功,无论执行几遍,都将执行结果返回给客户端
注意: 执行系统指令使用subprocess模块完成.
subprocess模块
- 创建popen类的实例化对象
obj = Subprocess.Popen("pwd",shell=True,stdout=subprocess.PIPE) - shell 开启shell
- stdout 重定向结果输出管道
* PIPE 将结果转移到当前主进程 - stdout.read() 获取命令执行的结果,指定结果后会将执行结果封装到指定的对象中,然后通过对象,read()获取执行命令的结果,如果不定义stdout会将结果进行标准输出
# 例子
[[email protected] /python]$ cat python-subprocess.py
#coding=utf-8
import subprocess
popen = subprocess.Popen("pwd",shell=True,stdout=subprocess.PIPE)
print(popen.stdout.read().strip())
[[email protected] /python]$
[[email protected] /python]$ python3 python-subprocess.py
b'/python'
[[email protected] /python]$
# 服务端编写
# coding=utf-8
import socket
import subprocess
server = socket.socket()
address = ("192.168.32.130", 8888)
server.bind(address)
server.listen(5)
while True:
conn, addr = server.accept()
while True:
try:
data = conn.recv(1024).decode("utf-8")
if len(data) == 0:
continue
if data == "q":
break
# 执行命令
obj = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE)
cmd_result = obj.stdout.read().strip()
# 获取一个新的bytes的对象
result_len = bytes(str(len(cmd_result)),"utf-8")
# 将数据全部发送给客户端,先发送长度
conn.sendall(result_len)
conn.recv(1024)
conn.sendall(cmd_result)
except Exception as e:
break
conn.close()
# 客户端编写
# coding=utf-8
import socket
client = socket.socket()
address = ("192.168.32.130", 8888)
client.connect(address)
while True:
send_msg = input("请发送命令>>")
if send_msg == "q":
break
client.send(bytes(send_msg,"utf-8"))
result_len = int(str(client.recv(1024),"utf-8"))
client.sendall(bytes("ok","utf-8"))
data = bytes()
while len(data) != result_len:
recv = client.recv(1024)
data += recv
print(str(data,"utf-8"))
client.close()
终端打印结果
# 先运行服务端
[[email protected] /python]$ python3 socket_server.py
# 客户端执行
请发送命令>>pwd
/python
请发送命令>>ifconfig
eth0 Link encap:Ethernet HWaddr 00:0C:29:B8:5C:AD
inet addr:192.168.32.130 Bcast:192.168.32.255 Mask:255.255.255.0
inet6 addr: fe80::20c:29ff:feb8:5cad/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1960879 errors:0 dropped:0 overruns:0 frame:0
TX packets:1227274 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:2217925730 (2.0 GiB) TX bytes:298187065 (284.3 MiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:4398906 errors:0 dropped:0 overruns:0 frame:0
TX packets:4398906 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:774863790 (738.9 MiB) TX bytes:774863790 (738.9 MiB)
请发送命令>>