网络编程 - 1.简单的套接字通信/2.加上通信循环/3.bug修复/4.加上链接循环/5.模拟ssh远程执行命令
1.简单的套接字通信
服务端
1 ''' 2 服务端 接电话 3 客户端 打电话 4 1.先启动服务端 5 2.服务端有两种套接字 6 1.phone 用来干接收链接的 7 2.conn 用来干收发消息的 8 9 ''' 10 import socket 11 # 1.买手机 12 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 基于网络通信的 基于tcp通信的套接字 13 # print(phone) 14 # <socket.socket fd=416, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> 15 16 # 2.绑定手机卡(IP地址) 运行这个软件的电脑IP地址 ip和端口都应该写到配置文件中 17 phone.bind(('127.0.0.1',8080)) # 端口0-65535 0-1024 给操纵系统 18 19 # 3.开机 20 phone.listen(5) # 5 代表最大挂起的链接数 21 22 # 4.等电话链接 23 print('starting...') 24 # res = phone.accept() #底层 就是 tcp 三次握手 25 # print(res) 26 # (<socket.socket fd=476, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 58886)>, ('127.0.0.1', 58886)) 27 conn,client_addr = phone.accept() # conn 电话线 拿到可以收发消息的管道 conn对象 28 29 # 5.收发消息 30 data = conn.recv(1024) # 1024个字节 1.单位:bytes 2.1024代表最大接收1024个bytes 31 print(data) 32 33 conn.send(data.upper()) 34 35 # 6.挂电话 36 conn.close() 37 38 # 7.关机 39 phone.close()
客户端
1 ''' 2 服务端 接电话 3 客户端 打电话 4 1.客户端有一种套接字: 5 phone 用来建连接请求 并用来收发消息 6 ''' 7 import socket 8 # 1.买手机 客户端的phone 相当于服务端的 conn 9 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 基于网络通信的 基于tcp通信的套接字 10 # print(phone) 11 12 # 2.拨号 (服务端的ip 和服务端的 端口) 13 phone.connect(('127.0.0.1',8080)) #phone 拿到可以发收消息的管道 phone 对象 14 15 # 3.发收消息 bytes型 16 phone.send('hello'.encode('utf-8')) 17 data = phone.recv(1024) 18 print(data) 19 20 # 4.关闭 21 phone.close()
2.加上通信循环
服务端
1 import socket 2 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 3 phone.bind(('127.0.0.1',8080)) 4 phone.listen(5) 5 print('strating...') 6 conn,client_addr = phone.accept() 7 print(client_addr) 8 while True: # 通信循环 9 data = conn.recv(1024) 10 print('客户端数据:',data) 11 conn.send(data.upper()) 12 13 conn.close() 14 phone.close()
客户端
1 import socket 2 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 3 phone.connect(('127.0.0.1',8080)) 4 while True: # 通信循环 5 msg = input('msg>>>:').strip() 6 phone.send(msg.encode('utf-8')) 7 data = phone.recv(1024) 8 print(data) 9 10 phone.close()
3.bug修复
端口已存在 重用一下:
http://www.cnblogs.com/linhaifeng/articles/6129246.html
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
还有一种 linux 操作系统 修改内核参数 让os 尽可能快的回收端口
有时os回收端口慢
客户端断开: linux 解决办法:if not data:break
wwindows 解决办法:try...except
问题:
这个是由于你的服务端仍然存在四次挥手的time_wait状态在占用地址
windows解决办法:
#加入一条socket配置,重用ip和端口
phone=socket(AF_INET,SOCK_STREAM)
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
phone.bind(('127.0.0.1',8080))
linux 解决办法:
发现系统存在大量TIME_WAIT状态的连接,通过调整linux内核参数解决,
vi /etc/sysctl.conf
编辑文件,加入以下内容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
然后执行 /sbin/sysctl -p 让参数生效。
net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间
服务端
1 import socket 2 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 3 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 4 phone.bind(('127.0.0.1',8080)) #如果端口已被占用 就换一个 5 phone.listen(5) 6 print('strating...') 7 conn,client_addr = phone.accept() # conn 三次握手的成果 双向链接 8 print(client_addr) 9 while True: # 通信循环 10 try: # try...except 出异常适合windows 出异常这里指客户端断开 11 data = conn.recv(1024) # 如何客户端 断开了 linux 就会死循环 12 # if not data:break # 出异常才会有 # 这里适合linux 出异常指客户端断开 13 print('客户端数据:',data) 14 conn.send(data.upper()) 15 except ConnectionResetError: # 适用windows os 16 break 17 18 conn.close() 19 phone.close()
客户端
send 可以发 空 # 发给了os的内存 在调用网卡 发送数据
recv 不可以 收空 # 到了os的内存 在传给了应用程序内存
所以 客户端 就卡住了 if not msg:continue 卡住原因 os 不会发''数据
1 import socket 2 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 3 4 phone.connect(('127.0.0.1',8080)) 5 while True: # 通信循环 6 msg = input('msg>>>:').strip() # '' 7 if not msg:continue 8 # 这个调用了硬件 应用程序里的内存传给了os的内存,由os 调用网卡传数据 9 phone.send(msg.encode('utf-8')) # b'' 10 # print('has send') # 打印了 证明可以 发 空 '' 11 # 和os要数据,你帮我调用网卡 收数据 12 data = phone.recv(1024) 13 # print('has recv') 14 # print(data) 15 print(data.decode('utf-8')) #解码 16 17 phone.close()
4.加上链接循环
服务端
1 ''' 2 服务端:不断的接收客户端的 连接 3 服务端accept 这里不是并发 的效果 但可以一个一个接收客户端的连接 4 phone.listen(5) 监听5个 5 ''' 6 import socket 7 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 8 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 9 phone.bind(('127.0.0.1',8080)) 10 phone.listen(0) 11 print('strating...') 12 while True: #连接循环 没有并发 但可一个一个 接收客户端 干通信活的同时不能干连接 13 conn,client_addr = phone.accept() # 现在没并发 只能一个一个 14 print(client_addr) 15 16 while True: 17 try: 18 data = conn.recv(1024) 19 if not data:break 20 print('客户端数据:',data) 21 conn.send(data.upper()) 22 except ConnectionResetError: 23 break 24 conn.close() 25 26 phone.close()
客户端1
1 import socket 2 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 3 phone.connect(('127.0.0.1',8080)) 4 while True: 5 msg = input('msg>>>:').strip() # '' 6 if not msg:continue 7 phone.send(msg.encode('utf-8')) # b'' 8 data = phone.recv(1024) 9 print(data.decode('utf-8')) #解码 10 11 phone.close()
客户端2
1 import socket 2 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 3 phone.connect(('127.0.0.1',8080)) 4 while True: 5 msg = input('msg>>>:').strip() # '' 6 if not msg:continue 7 phone.send(msg.encode('utf-8')) # b'' 8 data = phone.recv(1024) 9 print(data.decode('utf-8')) #解码 10 11 phone.close()
5.模拟ssh远程执行命令
什么叫命令?
windows:
dir:查看某一个文件夹下的子文件名与子文件夹名
ipconfig:查看本地网卡的ip信息
tasklist:查看运行的进程
Linux:
ls
ifconfig
ps aux
如何执行系统命令: 并拿到执行结果
import os
os.system # 只能拿到 运行结果 0 执行成功 非0 失败
一般用:
import subprocess
subprocess.Popen('dir d:',shell=True) # shell 启了一个cmd
把命令结果丢到管道里面:
subprocess.Popen('dir d:',shell=True,
stdout=subprocess.PIPE)
1 # import os 2 # res = os.system('dir d:') 3 # print(os.system('dir d:')) 4 # # print(res) 5 6 import subprocess 7 obj=subprocess.Popen('dir d:ss',shell=True, 8 stdout=subprocess.PIPE, # 正确的结果 9 stderr=subprocess.PIPE) # 错误的结果 10 print(obj) # 执行的结果 是bytes 11 print('stdout 1--:',obj.stdout.read().decode('gbk')) # linux 是 utf-8 windows 是 gbk 12 print('stdout 2--:',obj.stdout.read().decode('gbk')) # 因为管道没有了 13 print('stdout 3--:',obj.stderr.read().decode('gbk')) # 错误管道里有 原因 拿不到数据
服务端
1 import subprocess 2 import socket 3 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 4 # phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 重用ip和端口 任然存在4次挥手的状态 解决办法 5 phone.bind(('127.0.0.1',8080)) 6 phone.listen(5) 7 print('strating...') 8 while True: 9 conn,client_addr = phone.accept() 10 print(client_addr) 11 12 while True: 13 try: 14 # 1.收命令 15 cmd = conn.recv(1024) 16 if not cmd:break 17 print('客户端数据:',cmd) 18 19 # 2.执行命令,拿到结果 20 obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, # 客户端用 utf-8发的 21 stdout=subprocess.PIPE, 22 stderr=subprocess.PIPE) 23 stdout = obj.stdout.read() 24 stderr = obj.stderr.read() 25 26 # 3.把命令的结果返回给客户端 27 print(len(stdout)+len(stderr)) 28 conn.send(stdout+stderr) # 有效率问题的 这里 之后 可以优化 29 except ConnectionResetError: 30 break 31 conn.close() 32 33 phone.close()
客户端
1 import socket 2 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 3 phone.connect(('127.0.0.1',8080)) 4 while True: 5 # 1.发命令 6 cmd = input('msg>>>:').strip() # dir ls 7 if not cmd:continue 8 phone.send(cmd.encode('utf-8')) 9 10 # 2.拿到命令的结果,并打印 11 data = phone.recv(1024) # 这里是个坑 有可能会大于1024 接收数据量的最大限制 12 # data = phone.recv(526) # 这里是个坑 有可能会大于1024 接收数据量的最大限制 13 14 print(data.decode('gbk')) # linux:utf-8 windows:gbk 15 16 phone.close()