网络编程2
TCP三次握手和四次挥手
TCP协议
tcp把链接作为最基本的对象,每一条TCP链接都有两个端点,这种端点我们叫做套接字(socket),它的定义为端口号拼接到IP地址即构成了套接字.
TCP链接的建立-三次握手
如果客户端需要到服务端下载数据,客户端和服务端必须建立链接,这个链接就是三次握手
- 最开始的时候客户端和服务端都是处理关闭状态的,客户端主动打开,服务端被动打开
- 客户端会向服务端发起一个请求,里面会有一个SYN报头
- 服务端收到客户端的请求后,会响应客户端,并且把SYN报头改装后返回客户端,并且还有一个ACK报头
- 客户端收到服务端的响应后开始进入建立连接状态,再发一条请求,请求带上ACK给服务端,让服务端也进入连接状态
为什么要用三次握手
- 一次
客户端直接进入连接状态,发送请求给服务端进入连接状态
缺点:服务端收到请求后,无法找到对应的客户端
- 两次
- 客户端暂不进入连接状态,发送一条请求给服务端
- 服务端直接进入连接状态,发送一条确认请求给客户端,客户端收到后直接进去连接状态
缺点:服务端收到客户端的请求后,进入连接状态,占用一个端口,如发送请求后客户端未响应,则端口占用短期内一直存在,黑客可以通过此种原理伪造虚拟客户端发起请求,短时间内大量占用服务端的端口,造成服务器宕机,因此2次握手存在风险
TCP链接的结束-四次挥手
- 数据传输完毕后,双方都需要释放链接,最开始的时候,客户端和服务端都是处于established状态,然后客户端主动关闭,服务器端被动关闭
- 客户端会发送请求带上FIN报头给服务端,要求断开链接
- 服务端接收到客户端的请求后,返回一个带有ACK报头的请求表示已经确认了(此时FIN报头没有返回,并且服务端还有可能需要传输部分数据)
- 服务端数据传输结束之后,,服务端又会发送一次带报头FIN的请求,告诉客户端这边数据已经发送完了,需要客户端确认
- 客户端收到服务端的确认请求后,回复贷后ACK的报头,告知服务端关闭链接,同时经过一定时间后客户端自行关闭(2msl)
- 如果只有三次链接,但是客户端可能在第二次连接后就挂掉了,客户端没有收到第2-3步之间的数据传输
客户端+服务端实现聊天功能
服务端
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1', 8000))
server.listen(5)
print('start...')
while True:
conn, client_address = server.accept()
username = conn.recv(1024)
username = username.decode('utf-8')
while True:
data = conn.recv(1024)
data = data.decode('utf-8')
print(f'{username}:{data}')
msg = input('输入:')
conn.send(msg.encode('utf-8'))
客户端
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 8000))
username = input('请输入您的用户名')
client.send(username.encode('utf-8'))
while True:
msg = input('请输入:')
client.send(msg.encode('utf-8'))
data = client.recv(1024)
data = data.decode('utf-8')
print(f'淘宝客服助手:{data}')
SSH服务端和客户端
ssh服务端
import socket, subprocess
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('192.168.11.34', 8002))
server.listen(5)
print('start...')
while True:
conn, client_address = server.accept()
print(client_address)
while True:
cmd = conn.recv(1024)
cmd = cmd.decode('utf-8')
print(cmd)
pipeline = subprocess.Popen(cmd,
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
stderr = pipeline.stderr.read()
stdout = pipeline.stdout.read()
conn.send(stderr)
conn.send(stdout)
ssh客户端
import socket
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('192.168.11.34',8002))
while True:
cmd=input('请输入命令')
client.send(cmd.encode('utf-8'))
data=client.recv(10240)
print(data.decode('gbk'))# 因为通过subprocess执行的命令字符编码随系统,所以解码要用gbk