socket:
我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。
socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。
客户端代码:
#创建服务端
import socket
#创建服务端
response=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #流式协议=》tcp协议
socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #utp协议
#绑定ip地址
#linux系统ifconfig查看ip地址
#windows系统ipconfig查看ip地址
#在服务器上直接连接0.0.0.0就可以公网ip也可以
#ip:127.0.0.1测试时使用
#0-65535,1024之前被系统使用
response.bind(("127.0.0.1",8080))
#设置半连接池的大小
response.listen(5)
#等待连接请求
#双次赋值,就是这个函数返回两个值为一个元组形式,把元组的第一个值赋给conn第二个值赋给clint_addr
conn,client_addr=response.accept()
print(conn)
print("客户端的ip和端口:",client_addr)
#5收\发消息
data=conn.recv(1024) #最大接收数据量1024bytes,收到的bytes类型
print("客户端发来的消息:",data.decode("utf-8"))
conn.send(data.upper())
#6.关闭电话连接conn
conn.close()
#7.关闭服务端
conn.close()
服务端:
#这是客户端
import socket
get=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#连接服务端
get.connect(("127.0.0.1",8080))
#通信
get.send("hello 编程人".encode("utf-8"))
#关闭的连接
get.close()
#linux系统ifconfig查看ip地址
#windows系统ipconfig查看ip地址
查询效果:
实现服务端与客户端连接通信简单操作的代码:
服务端:
#创建服务端
import socket
import time
#管理者函数:
#def manger():
#主要功能是收到管理者的信息然后暂停函数
#创建服务端
response=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #流式协议=》tcp协议
#socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #utp协议
#绑定ip地址
#linux系统ifconfig查看ip地址
#windows系统ipconfig查看ip地址
#在服务器上直接连接0.0.0.0就可以公网ip也可以
#ip:127.0.0.1测试时使用
#0-65535,1024之前被系统使用
ip_place=("127.0.0.1",8080)
response.bind(ip_place)
#设置半连接池的大小
response.listen(5)
print("服务端启动完成,监听地址为:"+str(ip_place))
#等待连接请求
#双次赋值,就是这个函数返回两个值为一个元组形式,把元组的第一个值赋给conn第二个值赋给clint_addr
conn,client_addr=response.accept()
print(conn)
print("客户端的ip和端口:",client_addr)
#5收\发消息
c=0
while True:
conn, client_addr = response.accept()
while True:
try:
data = conn.recv(1024) # 最大接收数据量1024bytes,收到的bytes类型
#这是linux的防触发装置
if not data:
break
#当远程主机断开时linux主机会返回空然后服务端就会阻塞
#而windows系统会直接断开并且提示错误
print("客户端发来的消息:",data.decode("utf-8"))
if data.decode("utf-8") == "mzy":
print("正在进行操作")
conn.send(data)
except:
#延迟二十秒会停止,中间连接20次
c+=1
time.sleep(1)
print("此连接已终止")
if c==20:
break
#6.关闭电话连接conn
conn.close()
#7.关闭服务端
conn.close()
客户端:
#这是客户端
import socket
get=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#连接服务端
get.connect(("127.0.0.1",8080))
#通信
while True:
quest=input("》》》》")
data = get.send(quest.encode("utf-8"))
if not data:
print("请输入非空字符")
continue
#收取服务端消息
data1=get.recv(1024)
print(data1.decode("utf-8"))
#关闭的连接
get.close()
subprocess模块
Python subprocess模块
subprocess
#基于subprocess模块实现对远程主机终端的操作
#粘包问题:
1.tcp是流式协议,数据像水流一样,没有边界
2.收数据远远大于recv的参数值,会与下一次结果混淆在一起
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。
1·TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
2·UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
3·tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头,实验略
4·udp的recvfrom是阻塞的,一个recvfrom(x)必须对唯一一个sendinto(y),收完了x个字节的数据就算完成,若是y>x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠
tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。
#解决方法:
每次收干净
代码实现:
终端代码:
#当远程主机断开时linux主机会返回空然后服务端就会阻塞
#而windows系统会直接断开并且提示错误
print("客户端发来的消息:",data.decode("utf-8"))
#这个是终端使用的方法
#linux是utf-8解码
cmd=subprocess.Popen(
data.decode("utf-8"),
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
print("hello")
if cmd.poll()!=0:
conn.send("正在运行".strip().encode("utf-8"))
#print(cmd.stdout)
#subprocess.getoutput()
stdout_res = cmd.stdout.read() # 正确结果
stderr_res = cmd.stderr.read() # 错误结果
#这里有一个瑕疵就是如果运行的一个程序类似于python程序会卡顿在这里暂无解决方法,后来学了多线程可以尝试更改
if len((stderr_res+stdout_res).decode("utf-8"))==0:
conn.send("传入为空集或者导入程序".strip().encode("utf-8"))
else:
conn.send(stderr_res + stdout_res)
print(stderr_res + stdout_res)
#文件保存机制
with open("1.mp4",mode="rb") as f:
for line in f:
总结代码如下:
服务端:
#from socket import *
#创建服务端
import socket
import time
import subprocess
import struct
import json
#管理者函数:
#def manger():
#主要功能是收到管理者的信息然后暂停函数
#创建服务端
response=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #流式协议=》tcp协议
#socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #utp协议
#绑定ip地址
#linux系统ifconfig查看ip地址
#windows系统ipconfig查看ip地址
#在服务器上直接连接0.0.0.0就可以公网ip也可以
#ip:127.0.0.1测试时使用
#0-65535,1024之前被系统使用
ip_place=("127.0.0.1",8081)
response.bind(ip_place)
#设置半连接池的大小
response.listen(5)
print("服务端启动完成,监听地址为:"+str(ip_place))
#等待连接请求
#解压赋值,就是这个函数返回两个值为一个元组形式,把元组的第一个值赋给conn第二个值赋给clint_addr
#5收\发消息
c=0
while True:
conn, client_addr = response.accept()
print("客户端的ip和端口:", client_addr)
while True:
try:
data = conn.recv(1024) # 最大接收数据量1024bytes,收到的bytes类型
#这是linux的防触发装置
if len(data)==0:
break
#当远程主机断开时linux主机会返回空然后服务端就会阻塞
#而windows系统会直接断开并且提示错误
print("客户端发来的消息:",data.decode("utf-8"))
#这个是终端使用的方法
#linux是utf-8解码
cmd=subprocess.Popen(
data.decode("utf-8"),
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
print("hello")
if cmd.poll()==0:
conn.send("非正常运行".strip().encode("utf-8"))
break
#print(cmd.stdout)
#subprocess.getoutput()
stdout_res = cmd.stdout.read() # 正确结果
stderr_res = cmd.stderr.read() # 错误结果
print(len(stdout_res)+len(stderr_res)) #这是数据的总大小
##send函数发的是数据必须用encode来转换一下
#1先发头信息(固定长度的bytes):对数据描述信息
#这是一个自定义协议
# header_dic={
# "filename":"a.txt",
# "total_size":len(stdout_res)+len(stderr_res),
# "md5":"123123xi12"
# }
# json_str=json.dump(header_dic)
# json_str_bytes=json_str.encode("utf-8")
# #发送头的长度
# header_num = struct.pack('i', len(json_str_bytes))
# conn.send(header_num)
# #字典里面有数据长度
header=struct.pack('i',len(stdout_res)+len(stderr_res))
conn.send(header)
#这是一个失败的方法conn.send(str(len(stdout_res)+len(stderr_res)).encode("utf-8"))
#这里有一个瑕疵就是如果运行的一个程序类似于python程序会卡顿在这里暂无解决方法,后来学了多线程可以尝试更改
if len(stderr_res)+len(stdout_res)==0:
conn.send("传入为空集或者导入程序".strip().encode("utf-8"))
else:
conn.send(stderr_res + stdout_res)
print((stderr_res + stdout_res).decode("utf-8"))
#文件保存机制
# with open("1.mp4",mode="rb") as f:
# for line in f:
except:
#延迟二十秒会停止,中间连接20次
c+=1
time.sleep(1)
print("此连接已终止")
if c==5:
conn.close()
break
#6.关闭电话连接conn
conn.close()
#7.关闭服务端
conn.close()
客户端:
from socket import *
import struct
import time
import json
client=socket(AF_INET,SOCK_STREAM)
client.connect(("127.0.0.1",8081))
while True:
msg=input("请输入命令>>:").strip()
if len(msg)==0:
continue
client.send(msg.encode("utf-8"))
#为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节,这是应用层定的标准
header=client.recv(4)
#返回的是一个元组的形式其中第一个数据就是报头的长度
toatal_size=struct.unpack('i',header)[0]
# #这里是接收端的头的长度
# json_str_bytes=client.recv(toatal_size)
# json_str=json_str_bytes.decode("utf-8")
# #获得数据字典
# header_dic=json.loads(json_str)
print(toatal_size)
recv_size=0
cmd_ress=b''
while recv_size<toatal_size:
cmd_res = client.recv(1024)
recv_size+=len(cmd_res)
cmd_ress+=cmd_res
# else:
# #没有被break结束后加上else的操作
# print("")
try:
if cmd_ress.decode("utf-8"):
print(cmd_ress.decode("utf-8"))
except:
print(cmd_ress.decode("gbk"))
socketserver模块
基于tcp的套接字,关键就是两个循环,一个链接循环,一个通信循环
socketserver模块中分两大类:server类(解决链接问题)和request类(解决通信问题)
server类:
request类:
继承关系:
总的代码:
服务端:
import socketserver
import time
import subprocess
import struct
import json
class MyRequestHandle(socketserver.BaseRequestHandler):
def handle(self):
print(self.request)
print("以连接客户端地址为:",self.client_address)
c=0
while True:
try:
data = self.request.recv(1024) # 最大接收数据量1024bytes,收到的bytes类型
# 这是linux的防触发装置
if len(data) == 0:
break
# 当远程主机断开时linux主机会返回空然后服务端就会阻塞
# 而windows系统会直接断开并且提示错误
print("客户端发来的消息:", data.decode("utf-8"))
# 这个是终端使用的方法
# linux是utf-8解码
cmd = subprocess.Popen(
data.decode("utf-8"),
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
print("hello")
if cmd.poll() == 0:
self.request.send("非正常运行".strip().encode("utf-8"))
break
# print(cmd.stdout)
# subprocess.getoutput()
stdout_res = cmd.stdout.read() # 正确结果
stderr_res = cmd.stderr.read() # 错误结果
print(len(stdout_res) + len(stderr_res)) # 这是数据的总大小
##send函数发的是数据必须用encode来转换一下
# 1先发头信息(固定长度的bytes):对数据描述信息
# 这是一个自定义协议
# header_dic={
# "filename":"a.txt",
# "total_size":len(stdout_res)+len(stderr_res),
# "md5":"123123xi12"
# }
# json_str=json.dump(header_dic)
# json_str_bytes=json_str.encode("utf-8")
# #发送头的长度
# header_num = struct.pack('i', len(json_str_bytes))
# conn.send(header_num)
# #字典里面有数据长度
header = struct.pack('i', len(stdout_res) + len(stderr_res))
self.request.send(header)
# 这是一个失败的方法conn.send(str(len(stdout_res)+len(stderr_res)).encode("utf-8"))
# 这里有一个瑕疵就是如果运行的一个程序类似于python程序会卡顿在这里暂无解决方法,后来学了多线程可以尝试更改
if len(stderr_res) + len(stdout_res) == 0:
self.request.send("传入为空集或者导入程序".strip().encode("utf-8"))
else:
self.request.send(stderr_res + stdout_res)
print((stderr_res + stdout_res).decode("utf-8"))
# 文件保存机制
# with open("1.mp4",mode="rb") as f:
# for line in f:
except:
# 延迟二十秒会停止,中间连接20次
c += 1
time.sleep(1)
print("此连接已终止")
if c == 5:
self.request.close()
break
s=socketserver.ThreadingTCPServer(("127.0.0.1",8888),MyRequestHandle)
s.serve_forever()
#server_forever() 等同于:while true:
# conn,client_addr=server.accept()
# 启动一个线程(conn,client_addr)
#第二件事:拿到连接对象,与其进行通讯循环===》handle
客户端:
from socket import *
import struct
import time
import json
client=socket(AF_INET,SOCK_STREAM)
client.connect(("127.0.0.1",8888))
while True:
msg=input("请输入命令>>:").strip()
if len(msg)==0:
continue
client.send(msg.encode("utf-8"))
#为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节,这是应用层定的标准
header=client.recv(4)
#返回的是一个元组的形式其中第一个数据就是报头的长度
toatal_size=struct.unpack('i',header)[0]
# #这里是接收端的头的长度
# json_str_bytes=client.recv(toatal_size)
# json_str=json_str_bytes.decode("utf-8")
# #获得数据字典
# header_dic=json.loads(json_str)
print(toatal_size)
recv_size=0
cmd_ress=b''
while recv_size<toatal_size:
cmd_res = client.recv(1024)
recv_size+=len(cmd_res)
cmd_ress+=cmd_res
# else:
# #没有被break结束后加上else的操作
# print("")
try:
if cmd_ress.decode("utf-8"):
print(cmd_ress.decode("utf-8"))
except:
print(cmd_ress.decode("gbk"))
注意:其实这个直接套方法就可以了
class MyRequestHandle(socketserver.BaseRequestHandler):
def handle(self):
print(self.request)
print("以连接客户端地为:",self.client_address)
##########目标代码###############
###############################
s=socketserver.ThreadingTCPServer(("127.0.0.1",8888),MyRequestHandle)
s.serve_forever()
引用了一个很好的博客https://www.cnblogs.com/linhaifeng/articles/6129246.html#_label8