#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2018/6/2 18:29
# @Author : chen
# @File : 服务端.py
import json
import socket
import struct
import subprocess
"""
# 关于struct模块
res = struct.pack('i', 1230)
print(res, type(res), len(res))
# 输出结果:b'\xce\x04\x00\x00' <class 'bytes'> 4
# 而1230转成16进制的结果为4CE,所以struct接收的结果是反向的
obj = struct.unpack('i', res)
print(obj)
# (1230,)
# unpack后,结果就是正向的,所以不需要在意这些细节,只要关注传输的是4字节就可以了
"""
# 服务端需要两个套接字,一个用来发送,另一个用来接收bind,recv
# 客户端只有一个套接字,connect
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # setsockopt(level,optname,value)
# socket模块下的socket类,socket.AF_INET是网络模式
# socket.SOCK_STREAM是代表流模式,其实就是指的tcp
"""
# level定义了哪个选项将被使用。通常情况下是SOL_SOCKET,意思是正在使用的socket选项。它还可以通过设置一个特殊协议号码来设置协议选项,
# 然而对于一个给定的操作系统,大多数协议选项都是明确的,所以为了简便,它们很少用于为移动设备设计的应用程序。
# 这里value设置为1,表示将SO_REUSEADDR标记为TRUE,操作系统会在服务器socket被关闭或服务器进程终止后马上释放该服务器的端口,
# 否则操作系统会保留几分钟该端口。
"""
phone.bind(('127.0.0.1', 9901)) # 0-65535; 0-1024给操作系统使用
phone.listen(5)
print('starting...')
while True:
conn, client_addr = phone.accept() # conn是接收的一个对象accept() -> (socket object, address info)
# 可以将conn理解为三次握手的导向指标(箭头)
print(client_addr)
while True:
try:
# 1.收命令
cmd = conn.recv(8096) # 1、单位:bytes 2、8096代表最大接收8096个bytes
if not cmd: break # 适用于linux操作系统
print('客户端数据', cmd)
# 2.执行命令,拿到结果
obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout = obj.stdout.read()
stderr = obj.stderr.read()
# 3.把命令的结果返回给客户端
# 第一步:制作固定长度的报头
header_dic = {
'file_name': 'a.txt',
'md5': 'xxxxxdd',
'total_size': len(stdout) + len(stderr)
}
# 将字典序列化(字典转成字符格式)
header_json = json.dumps(header_dic)
# 将字符格式编码成二进制
header_bytes = header_json.encode('utf-8')
# 第二步:先发送报头长度; 客户端接收时一次只接收4字节,这样就不会出现粘包现象了
conn.send(struct.pack('i', len(header_bytes))) # 'i' 表示int 整型数据
# 第三步:再发送报头
conn.send(header_bytes) # 发送给网卡,从应用程序内存拷贝到系统内存,当数据为空时,操作系统就不会有任何操作
# 第四步:发送真实数据
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError: # 适用于windows操作系统
break
conn.close() # 一次会话结束
phone.close() # 连接断开
# 粘包的终极解决思路是,自己设定一个协议报头,规定好长度(使用struct模块),然后再在报头中填入字符串长度问题
# 可以避免接收字节长度过长超过int或long型的最大值(比如传输超大文件这种),还能增加更多信息(比如校验md5文件等)
模拟ssh远程socket编程粘包问题_服务端
猜你喜欢
转载自blog.csdn.net/u013193903/article/details/80561376
今日推荐
周排行