Python3学习之路~8.2 socket简单实例 实现ssh 发送大量数据
实例1:
利用socket模拟客户端和服务器端各自收发一次数据:
- #Author:Zheng Na
- # 客户端
- import socket
- # 声明socket类型,同时生成socket连接对象
- client = socket.socket() # 默认参数family=AF_INET(表示地址簇为IPV4),type=SOCK_STREAM(表示socket类型为TCP)
- client.connect(('localhost',6969))
- client.send(b"hello world") #注意:Python 2.x中可以发送str类型和bytes类型数据,但是Python3.x中只能发送bytes类型数据
- data = client.recv(1024) # 接收1024字节
- print("recv from server: ",data)
- client.close()
socket_client.py
- #Author:Zheng Na
- # 服务器端
- import socket
- server = socket.socket() # 声明socket类型,同时生成socket连接对象
- server.bind(('localhost',6969)) # 绑定要监听的端口
- server.listen() # 监听
- print("我要开始等信息了")
- # conn就是客户端连接过来而在服务器端为其生成的一个连接实例
- conn,addr = server.accept() # 等待信息传送
- print("conn: ",conn)
- print("addr: ",addr)
- print("信息来了")
- data = conn.recv(1024)
- print("recv from client: ",data)
- conn.send(data.upper())
- server.close()
socket_server.py
首先运行socket_server.py,结果显示:
- 我要开始等信息了
说明此时程序执行到conn,addr = server.accept()卡住,服务器端正在等待信息传送。
接着运行socket_client.py,结果显示:
- recv from server: b'HELLO WORLD'
说明客户端接收到了服务器端发送的数据。
再重新查看socket_server.py运行结果:
- 我要开始等信息了
- conn: <socket.socket fd=332, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969), raddr=('127.0.0.1', 54153)>
- addr: ('127.0.0.1', 54153)
- 信息来了
- recv from client: b'hello world'
说明服务器端接收到了客户端发送的数据。
注意:Python 2.x中可以发送str类型和bytes类型数据,但是Python3.x中只能发送bytes类型数据。
将英文转化成bytes只需要在前面加上b即可,比如:client.send(b"Hello World");
将中文转化成bytes只需要编码一下即可,比如:client.send("我要发送中文".encode("UTF-8")),当然,如果在服务器端需要读取发送的数据,解码即可,比如:data.decode("UTF-8")。
实例2:
好了,接下来我们希望客户端可以一直向服务器端发送数据,修改代码如下:
- #Author:Zheng Na
- # 客户端
- import socket
- client = socket.socket()
- client.connect(('localhost',6969))
- while True:
- msg = input(">>: ").strip()
- client.send(msg.encode("UTF-8"))
- data = client.recv(1024) # 接收1024字节
- print("recv from server: ",data)
- client.close()
socket_client2.py
- #Author:Zheng Na
- # 服务器端
- import socket
- server = socket.socket()
- server.bind(('localhost',6969))
- server.listen() # 监听
- print("我要开始等信息了")
- conn, addr = server.accept()
- print("信息来了")
- while True:
- data = conn.recv(1024)
- print("recv from client: ",data.decode("UTF-8"))
- conn.send(data.upper())
- server.close()
socket_server2.py
在Windows下的PyCharm中,先点击运行socket_server2.py,结果显示:
- 我要开始等信息了
接着运行socket_client2.py,输入1,回车,2,回车,3,回车。结果显示:
- >>: 1
- recv from server: b''
- >>: 2
- recv from server: b''
- >>: 3
- recv from server: b''
- >>:
说明客户端接收到了服务器端发送的数据。
再重新查看socket_server2.py运行结果:
- 我要开始等信息了
- 信息来了
- recv from client: 1
- recv from client: 2
- recv from client: 3
说明服务器端接收到了客户端发送的数据。
最后我们关闭客户端程序,查看服务器端,结果显示:
- Traceback (most recent call last):
- File "D:/python-study/s14/Day08/socket_server2.py", line 17, in <module>
- data = conn.recv(1024)
- ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
实例3:
现在我们按照同样的步骤,把这2段代码在linux执行一下试试,linux下安装Python3.6,以后的实例均在linux环境下测试。
结果发现:前几步结果与在Windows下的结果都一致,但是最后一步,在我们关闭客户端程序后,查看服务器端,结果显示服务器端进入了死循环:
- 我要开始等信息了
- 信息来了
- recv from client: 1
- recv from client: 2
- recv from client: 3
- recv from client:
- recv from client:
- recv from client:
- recv from client:
- ...
说明当客户端关闭后,服务器认为自己一直收到了空字符。在linux和Mac上,Python2和Python3结果都是这样的。
为了解决这个问题,我们将socket_server2.py修改一下代码:加个判断条件,如果接收到的数据为空,就自动跳出循环。
- #Author:Zheng Na
- # 服务器端
- import socket
- server = socket.socket()
- server.bind(('localhost',6969))
- server.listen() # 监听
- print("我要开始等信息了")
- conn, addr = server.accept()
- print("信息来了")
- while True:
- data = conn.recv(1024)
- if not data:
- print("client is lost...")
- break
- print("recv from client: ",data.decode("UTF-8"))
- conn.send(data.upper())
- server.close()
socket_server2.1.py
重新将前面的步骤执行一遍,最后一步,在我们关闭客户端程序后,查看服务器端,结果显示服务器端已自动关闭:
- [root@hadoop my-test-files]# python3 socket_server2.1.py
- 我要开始等信息了
- 信息来了
- recv from client: 1
- recv from client: 2
- recv from client: 3
- client is lost...
- [root@hadoop my-test-files]#
现在我们的程序实现了:客户端可以一直向服务器端发送数据,当客户端关闭后,服务器端也自动关闭了。
实例4:
那么如何让客户端断开后,服务器端仍然持续运行,等待另一个客户端连接呢?修改代码如下(在服务器端再加一个while True,使其一直在等待下一次连接):
- #Author:Zheng Na
- # 服务器端
- import socket
- server = socket.socket()
- server.bind(('localhost',6969))
- server.listen() # 监听
- print("我要开始等信息了")
- while True:
- conn, addr = server.accept()
- print("信息来了")
- while True:
- data = conn.recv(1024)
- if not data:
- print("client is lost...wait next client to connect...")
- break
- print("recv from client: ",data.decode("UTF-8"))
- conn.send(data.upper())
- server.close()
socket_server2.2.py
这段代码中,里面的while True使客户端可以一直跟服务器端发送数据,外面的while True使当客户端断开后,服务器端能够保持连接不断,等待下一个客户端连接。
重新将前面的步骤执行一遍,最后一步,在我们关闭客户端程序后,查看服务器端,结果显示服务器端未关闭:
- [root@hadoop my-test-files]# python3 socket_server2.2.py
- 我要开始等信息了
- 信息来了
- recv from client: 1
- recv from client: 2
- recv from client: 3
- client is lost...wait next client to connect...
此时再执行一遍客户端程序,相当于重新打开一个客户端,输入a,回车,b,回车,c,回车,Ctrl+C关闭客户端。查看服务端结果显示:
- [root@hadoop my-test-files]# python3 socket_server2.2.py
- 我要开始等信息了
- 信息来了
- recv from client: 1
- recv from client: 2
- recv from client: 3
- client is lost...wait next client to connect...
- 信息来了
- recv from client: a
- recv from client: b
- recv from client: c
- client is lost...wait next client to connect...
实例5:
server.listen()可以加参数,一般不超过10。比如server.listen(5)最常见,意思是,程序最多可以挂起5个客户端。即当一个客户端在通信时,最多可以有5个客户端在排队。但是在目前的代码里没法测试,等我们学异步的时候再测吧。
实例6:
上述代码还有缺陷,那就是客户端不能向服务器端发送空字符串,一旦客户端向服务器端发送空字符串就会卡住。
可以在代码中客户端向服务器端发送数据之前加上如下代码解决上述问题
- if len(msg) == :continue # 如果为空,重新输入
即将客户端代码改为如下
- #Author:Zheng Na
- # 客户端
- import socket
- # 声明socket类型,同时生成socket连接对象
- client = socket.socket() # 默认参数family=AF_INET(表示地址簇为IPV4),type=SOCK_STREAM(表示socket类型为TCP)
- client.connect(('localhost',6969))
- while True:
- msg = input(">>: ").strip()
- if len(msg) == 0:continue # 如果为空,重新输入
- client.send(msg.encode("UTF-8"))
- data = client.recv(1024) # 接收1024字节
- print("recv from server: ",data)
- client.close()
socket_client2.1.py
实例7:socket实现简单的ssh
接下来我们尝试模拟ssh客户端,即连到一台机器上执行命令返回结果。
修改代码如下
- #encoding:utf-8 # python2.x
- #Author:Zheng Na
- # 客户端
- import socket
- # 声明socket类型,同时生成socket连接对象
- client = socket.socket() # 默认参数family=AF_INET(表示地址簇为IPV4),type=SOCK_STREAM(表示socket类型为TCP)
- client.connect(('localhost',6969))
- while True:
- msg = input(">>: ").strip() # python3.x
- #msg = raw_input(">>: ").strip() # python2.x
- if len(msg) == 0:continue # 如果为空,重新输入
- client.send(msg.encode("UTF-8"))
- data = client.recv(1024) # 接收1024字节
- print(data.decode()) # python3.x
- #print(data) # python2.x
- client.close()
socket_client2.2-ssh.py
- #encoding:utf-8 # python2.x
- #Author:Zheng Na
- # 服务器端
- import os
- import socket
- server = socket.socket()
- server.bind(('localhost',6969))
- server.listen(5) # 监听
- print("我要开始等信息了")
- while True:
- conn, addr = server.accept()
- print("信息来了")
- while True:
- data = conn.recv(1024)
- if not data:
- print("client is lost...wait next client to connect...")
- break
- print("recv from client: ",data.decode("UTF-8"))
- # 注意:popen()可以执行shell命令,并读取此命令的返回值
- res = os.popen(data.decode("UTF-8")).read() # python3.x
- #res = os.popen(data).read() # python2.x
- conn.send(res.encode("UTF-8")) # python3.x 只能发送bytes
- #conn.send(res) # python2.x 发送str
- server.close()
socket_server2.3-ssh.py
服务器端先启动,当客户端启动后输入命令df,输出结果如下
- [root@hadoop my-test-files]# python3 socket_client2.2.py
- >>: df
- 文件系统 1K-块 已用 可用 已用% 挂载点
- /dev/mapper/cl_linux-root 17811456 7000392 10811064 40% /
- devtmpfs 922660 0 922660 0% /dev
- tmpfs 933632 0 933632 0% /dev/shm
- tmpfs 933632 8764 924868 1% /run
- tmpfs 933632 0 933632 0% /sys/fs/cgroup
- /dev/sda1 1038336 141564 896772 14% /boot
- tmpfs 186728 0 186728 0% /run/user/0
df
very cool , 这样我们就做了一个简单的ssh , 但多试几条命令你就会发现,上面的程序有以下几个问题。
1.不能执行top等类似的 会持续输出的命令,这是因为,服务器端在收到客户端指令后,会一次性通过os.popen执行,并得到结果后返回给客户,但top这样的命令用os.popen执行你会发现永远都不会结束,所以客户端也永远拿不到返回。(真正的ssh是通过select 异步等模块实现的,我们以后会涉及)
- [root@hadoop my-test-files]# python3 socket_client2.2.py
- >>: top
top 卡住
2.不能执行像cd这种没有返回的指令, 因为客户端每发送一条指令,就会通过client.recv(1024)等待接收服务器端的返回结果,但是cd命令没有结果 ,服务器端调用conn.send(data)时是不会发送数据给客户端的。 所以客户端就会一直等着,等到天荒地老,结果就卡死了。解决的办法是,在服务器端判断命令的执行返回结果的长度,如果结果为空,就自己加个结果返回给客户端,如写上"cmd exec success, has no output."
- if len(res)==:
- res = "cmd exec success,has not output!"
3.如果执行的命令返回结果的数据量比较大,会发现,结果返回不全,在客户端上再执行一条命令,结果返回的还是上一条命令的后半段的执行结果,这是为什么呢?这是因为,我们的客户写client.recv(1024), 即客户端一次最多只接收1024个字节,如果服务器端返回的数据是2000字节,那有至少9百多字节是客户端第一次接收不了的,那怎么办呢,服务器端此时不能把数据直接扔了呀,so它会暂时存在服务器的io发送缓冲区里,等客户端下次再接收数据的时候再发送给客户端。 这就是为什么客户端执行第2条命令时,却接收到了第一条命令的结果的原因。
比如输入top -bn 1命令,由于此命令的输出非常大,超过1024字节,而代码限制了输出不能超过1024字节,导致输出只能为1024字节,并且下次输入别的命令是会显示此命令结果剩余未输出的部分。这就相当于服务器把命令的结果放在一个缓冲区里,排队输出。
- >>: top -bn 1
- top - 17:02:36 up 5:19, 3 users, load average: 0.00, 0.01, 0.05
- Tasks: 96 total, 2 running, 92 sleeping, 2 stopped, 0 zombie
- %Cpu(s): 0.1 us, 0.1 sy, 0.0 ni, 99.6 id, 0.2 wa, 0.0 hi, 0.0 si, 0.0 st
- KiB Mem : 1867268 total, 100748 free, 1583232 used, 183288 buff/cache
- KiB Swap: 2097148 total, 2097148 free, 0 used. 101496 avail Mem
- PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
- 3971 root 20 0 157620 2044 1516 R 0.7 0.1 0:01.28 top
- 1 root 20 0 125124 3604 2428 S 0.0 0.2 0:02.94 systemd
- 2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd
- 3 root 20 0 0 0 0 S 0.0 0.0 0:00.86 ksoftirqd/0
- 6 root 20 0 0 0 0 S 0.0 0.0 0:01.70 kworker/u256:0
- 7 root rt 0 0 0 0 S 0.0 0.0 0:00.00 migration/0
- 8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_bh
- 9 root 20 0 0
- >>: df
- 0 0 R 0.0 0.0 0:06.94 rcu_sched
- 10 root rt 0 0 0 0 S 0.0 0.0 1:08.00 watchdog/0
- 12 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 khelper
- 13 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kdevtmpfs
- 14 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 netns
- 15 root 20 0 0 0 0 S 0.0 0.0 0:00.00 khungtaskd
- 16 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 writeback
- 17 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kintegrityd
- 18 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 bioset
- 19 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kblockd
- 20 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 md
- 26 root 20 0 0 0 0 S 0.0 0.0 0:00.01 kswapd0
- 27 root 25 5 0 0 0 S 0.0 0.0 0:00.00 ksmd
- 28 root 39 19 0 0 0 S 0.0 0.0 0:05.56
- >>: df
- khugepaged
- 29 root 20 0 0 0 0 S 0.0 0.0 0:00.00 fsnotify_mark
- 30 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 crypto
- 38 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kthrotld
- 40 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kmpath_rdacd
- 41 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kpsmoused
- 43 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 ipv6_addrconf
- 62 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 deferwq
- 94 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kauditd
- 275 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 ata_sff
- 276 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 mpt_poll_0
- 277 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 mpt/0
- 285 root 20 0 0 0 0 S 0.0 0.0 0:00.00 scsi_eh_0
- 286 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 scsi_tmf_0
- >>: df
- 287 root 20 0 0 0 0 S 0.0 0.0 0:00.01 scsi_eh_1
- 290 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 scsi_tmf_1
- 292 root 20 0 0 0 0 S 0.0 0.0 0:00.00 scsi_eh_2
- 294 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 scsi_tmf_2
- 295 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 ttm_swap
- 368 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kdmflush
- 369 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 bioset
- 378 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kdmflush
- 379 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 bioset
- 394 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfsalloc
- 395 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs_mru_cache
- 396 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-buf/dm-0
- 397 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-data/dm-0
- 398
- >>:
top -bn 1 输出不全
为了解决这个问题,我直接在客户端把client.recv(1024)改大一点,比如将客户端代码修改代码为:
- data = client.recv(1073741824) # 接收1G字节
- #encoding:utf-8 # python2.x
- #Author:Zheng Na
- # 客户端
- import socket
- # 声明socket类型,同时生成socket连接对象
- client = socket.socket() # 默认参数family=AF_INET(表示地址簇为IPV4),type=SOCK_STREAM(表示socket类型为TCP)
- client.connect(('localhost',6969))
- while True:
- msg = input(">>: ").strip() # python3.x
- #msg = raw_input(">>: ").strip() # python2.x
- if len(msg) == 0:continue # 如果为空,重新输入
- client.send(msg.encode("UTF-8"))
- data = client.recv(1073741824) # 接收1G数据
- print(data.decode()) # python3.x
- #print(data) # python2.x
- client.close()
- socket_client2.3.py
socket_client2.3-ssh.py
修改过后,top -bn 1命令输出如下
- [root@hadoop my-test-files]# python3 socket_client2.3.py
- >>: top -bn 1
- top - 17:45:02 up 6:01, 3 users, load average: 0.00, 0.01, 0.05
- Tasks: 95 total, 2 running, 93 sleeping, 0 stopped, 0 zombie
- %Cpu(s): 0.1 us, 0.1 sy, 0.0 ni, 99.6 id, 0.2 wa, 0.0 hi, 0.0 si, 0.0 st
- KiB Mem : 1867268 total, 99452 free, 1584028 used, 183788 buff/cache
- KiB Swap: 2097148 total, 2097148 free, 0 used. 100288 avail Mem
- PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
- 9 root 20 0 0 0 0 S 0.1 0.0 0:06.97 rcu_sched
- 1 root 20 0 125124 3604 2428 S 0.0 0.2 0:03.11 systemd
- 2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd
- 3 root 20 0 0 0 0 S 0.0 0.0 0:00.88 ksoftirqd/0
- 6 root 20 0 0 0 0 S 0.0 0.0 0:01.70 kworker/u256:0
- 7 root rt 0 0 0 0 S 0.0 0.0 0:00.00 migration/0
- 8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_bh
- 10 root rt 0 0 0 0 S 0.0 0.0 1:11.38 watchdog/0
- 12 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 khelper
- 13 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kdevtmpfs
- 14 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 netns
- 15 root 20 0 0 0 0 S 0.0 0.0 0:00.00 khungtaskd
- 16 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 writeback
- 17 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kintegrityd
- 18 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 bioset
- 19 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kblockd
- 20 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 md
- 26 root 20 0 0 0 0 S 0.0 0.0 0:00.01 kswapd0
- 27 root 25 5 0 0 0 S 0.0 0.0 0:00.00 ksmd
- 28 root 39 19 0 0 0 S 0.0 0.0 0:05.57 khugepaged
- 29 root 20 0 0 0 0 S 0.0 0.0 0:00.00 fsnotify_mark
- 30 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 crypto
- 38 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kthrotld
- 40 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kmpath_rdacd
- 41 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kpsmoused
- 43 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 ipv6_addrconf
- 62 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 deferwq
- 94 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kauditd
- 275 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 ata_sff
- 276 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 mpt_poll_0
- 277 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 mpt/0
- 285 root 20 0 0 0 0 S 0.0 0.0 0:00.00 scsi_eh_0
- 286 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 scsi_tmf_0
- 287 root 20 0 0 0 0 S 0.0 0.0 0:00.01 scsi_eh_1
- 290 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 scsi_tmf_1
- 292 root 20 0 0 0 0 S 0.0 0.0 0:00.00 scsi_eh_2
- 294 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 scsi_tmf_2
- 295 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 ttm_swap
- 368 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kdmflush
- 369 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 bioset
- 378 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kdmflush
- 379 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 bioset
- 394 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfsalloc
- 395 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs_mru_cache
- 396 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-buf/dm-0
- 397 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-data/dm-0
- 398 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-conv/dm-0
- 399 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-cil/dm-0
- 400 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-reclaim/dm-
- 401 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-log/dm-0
- 402 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-eofblocks/d
- 403 root 20 0 0 0 0 S 0.0 0.0 0:03.70 xfsaild/dm-0
- 477 root 20 0 37012 3552 3244 S 0.0 0.2 0:05.23 systemd-journal
- 497 root 20 0 192680 1408 988 S 0.0 0.1 0:00.00 lvmetad
- 501 root 20 0 44756 2976 1260 S 0.0 0.2 0:00.39 systemd-udevd
- 575 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-buf/sda1
- 576 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-data/sda1
- 577 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-conv/sda1
- 578 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-cil/sda1
- 579 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-reclaim/sda
- 580 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-log/sda1
- 581 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 xfs-eofblocks/s
- 582 root 20 0 0 0 0 S 0.0 0.0 0:00.64 xfsaild/sda1
- 593 root 16 -4 55476 1744 1344 S 0.0 0.1 0:00.15 auditd
- 611 root 20 0 24252 1708 1408 S 0.0 0.1 0:02.60 systemd-logind
- 614 dbus 20 0 24600 1696 1308 S 0.0 0.1 0:00.37 dbus-daemon
- 623 root 20 0 442684 10844 6532 S 0.0 0.6 0:00.46 NetworkManager
- 625 polkitd 20 0 534208 12060 4648 S 0.0 0.6 0:00.09 polkitd
- 629 root 20 0 126280 1572 956 S 0.0 0.1 0:00.03 crond
- 634 chrony 20 0 115940 1912 1516 S 0.0 0.1 0:00.33 chronyd
- 875 root 20 0 287496 5056 3800 S 0.0 0.3 0:00.53 rsyslogd
- 876 root 20 0 105996 4136 3156 S 0.0 0.2 0:00.10 sshd
- 878 root 20 0 560344 16624 5920 S 0.0 0.9 0:03.19 tuned
- 1368 root 20 0 115644 1764 1440 S 0.0 0.1 0:00.03 mysqld_safe
- 2437 root 20 0 89536 2160 1128 S 0.0 0.1 0:00.13 master
- 2584 postfix 20 0 89708 4036 3036 S 0.0 0.2 0:00.01 qmgr
- 2616 mysql 20 0 10.866g 1.386g 6368 S 0.0 77.8 1:28.59 mysqld
- 2705 root 20 0 110092 852 724 S 0.0 0.0 0:00.00 agetty
- 2836 root 20 0 113360 15952 3460 S 0.0 0.9 0:00.05 dhclient
- 3757 root 20 0 0 0 0 S 0.0 0.0 0:00.15 kworker/u256:1
- 3761 root 20 0 148388 5864 4484 S 0.0 0.3 0:01.11 sshd
- 3779 root 20 0 0 0 0 R 0.0 0.0 0:01.72 kworker/0:1
- 3781 root 20 0 115536 2208 1692 S 0.0 0.1 0:00.02 bash
- 3883 root 20 0 148388 5784 4464 S 0.0 0.3 0:00.12 sshd
- 3885 root 20 0 115536 2204 1692 S 0.0 0.1 0:00.01 bash
- 3903 postfix 20 0 89640 4008 3012 S 0.0 0.2 0:00.08 pickup
- 4042 root 20 0 148388 5784 4464 S 0.0 0.3 0:00.10 sshd
- 4044 root 20 0 115536 2216 1692 S 0.0 0.1 0:00.01 bash
- 4064 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:1H
- 4065 root 20 0 0 0 0 S 0.0 0.0 0:00.01 kworker/0:2
- 4087 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0H
- 4089 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0
- 4101 root 20 0 137020 7788 3008 S 0.0 0.4 0:00.06 python3
- 4102 root 20 0 135040 6744 2864 S 0.0 0.4 0:00.04 python3
- 4103 root 20 0 157620 2048 1516 R 0.0 0.1 0:00.00 top
- >>: df
- 文件系统 1K-块 已用 可用 已用% 挂载点
- /dev/mapper/cl_linux-root 17811456 7000408 10811048 40% /
- devtmpfs 922660 0 922660 0% /dev
- tmpfs 933632 0 933632 0% /dev/shm
- tmpfs 933632 8792 924840 1% /run
- tmpfs 933632 0 933632 0% /sys/fs/cgroup
- /dev/sda1 1038336 141564 896772 14% /boot
- tmpfs 186728 0 186728 0% /run/user/0
top -bn 1 输出完整
实例8:发送&接收大量数据
虽然上一个实例中我们通过data = client.recv(1073741824)设置了客户端一次可以接收1G数据,但是实际上客户端是不可能一次接收这么多数据的。
因为socket每次接收和发送都有最大数据量限制的,毕竟网络带宽也是有限的呀,不能一次发太多,发送的数据最大量的限制 就是缓冲区能缓存的数据的最大量,这个缓冲区的最大值在不同的系统上是不一样的, 我实在查不到一个具体的数字,但测试的结果是,在linux上最大一次可接收10mb左右的数据,不过官方的建议是不超过8k,也就是8192,并且数据要可以被2整除,不要问为什么 。anyway , 如果一次只能接收最多不超过8192的数据 ,那服务端返回的数据超过了这个数字怎么办呢?比如让服务器端打开一个5mb的文件并返回,客户端怎么才能完整的接受到呢?那就只能循环收取啦。
在开始解决上面问题3之前,我们要考虑,客户端要循环接收服务器端的大量数据返回直到一条命令的结果全部返回为止, 但问题是客户端知道服务器端返回的数据有多大么?答案是不知道,那既然不知道服务器的要返回多大的数据,那客户端怎么知道要循环接收多少次呢?答案是不知道。那咋办? 总不能靠猜吧?呵呵。。。 当然不能,那只能让服务器在发送数据之前主动告诉客户端,要发送多少数据给客户端,然后再开始发送数据。
先简单测试接收数据量大小
- #encoding:utf-8 # python2.x
- #Author:Zheng Na
- # 服务器端
- # import time
- import os,subprocess
- import socket
- server = socket.socket()
- server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
- server.bind(('localhost',6969)) # 绑定ip port
- server.listen(5) # 监听
- while True:
- print("等待客户端的连接...")
- conn, addr = server.accept() # 接受并建立与客户端的连接,程序在此处开始阻塞,直到有客户端连接进来...
- print("新连接:",addr)
- while True:
- data = conn.recv(1024)
- if not data:
- print("客户端断开了...")
- break # 这里断开就会再次回到第一次外层的loop
- print("收到命令:",data.decode("UTF-8"))
- # 注意:popen()可以执行shell命令,并读取此命令的返回值
- #res = os.popen(data.decode("UTF-8")).read() # python3.x里socket发送的只有bytes,os.popen又只能接受str,所以要decode一下
- #res = os.popen(data).read() # python2.x
- res = subprocess.Popen(data,shell=True,stdout=subprocess.PIPE).stdout.read() #跟上面那条命令的结果是一样的
- if len(res)==0:
- res = "cmd exec success,has not output!"
- conn.send(str(len(res)).encode("UTF-8")) # 发送数据之前先告诉客户端要发多少数据给它
- # time.sleep(0.5)
- conn.sendall(res) # python3.x 只能发送bytes;发送端也有最大数量限制,所以这里用sendall,相当于重复循环电源conn.send(),直至数据发送完毕·
- server.close()
socket_server3-test-len-res.py
- #encoding:utf-8 # python2.x
- #Author:Zheng Na
- # 客户端
- import socket
- # 声明socket类型,同时生成socket连接对象
- client = socket.socket() # 默认参数family=AF_INET(表示地址簇为IPV4),type=SOCK_STREAM(表示socket类型为TCP)
- client.connect(('localhost',6969))
- while True:
- msg = input(">>: ").strip() # python3.x
- #msg = raw_input(">>: ").strip() # python2.x
- if len(msg) == 0:continue # 如果为空,重新输入
- client.send(msg.encode("UTF-8"))
- res_return_size = client.recv(1024)
- print("getting cmd result,",res_return_size.decode("UTF-8"))
- total_rece_size = int(res_return_size)
- print(total_rece_size)
- client.close()
socket_client3-test-len-res.py
PS:上面服务端代码在创建socket实例后,新加了如下一行代码
- server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #允许socket重用地址
结果输出:报错
- [root@hadoop my-test-files]# python3 socket_client3-test-len-res.py
- >>: df
- getting cmd result, 539文件系统 1K-块 已用 可用 已用% 挂载点
- /dev/mapper/cl_linux-root 17811456 7062976 10748480 40% /
- devtmpfs 922660 0 922660 0% /dev
- tmpfs 933632 0 933632 0% /dev/shm
- tmpfs 933632 8736 924896 1% /run
- tmpfs 933632 0 933632 0% /sys/fs/cgroup
- /dev/sda1 1038336 141564 896772 14% /boot
- tmpfs 186728 0 186728 0% /run/user/0
- Traceback (most recent call last):
- File "socket_client3-test-len-res.py", line 23, in <module>
- total_rece_size = int(res_return_size)
- ValueError: invalid literal for int() with base 10: b'539\xe6\x96\x87\xe4\xbb\xb6\xe7\xb3\xbb\xe7\xbb\x9f 1K-\xe5\x9d\x97 \xe5\xb7\xb2\xe7\x94\xa8 \xe5\x8f\xaf\xe7\x94\xa8 \xe5\xb7\xb2\xe7\x94\xa8% \xe6\x8c\x82\xe8\xbd\xbd\xe
结果输出报错
看程序执行报错了, 我在客户端本想只接收服务器端命令的执行结果,但实际上却连命令结果也跟着接收了。 这是为什么呢?服务器不是只send了结果的大小么?不应该只是个数字么?命令结果不是第2次send的时候才发送的么??
哈哈,这里就引入了一个重要的概念,“粘包”, 即服务器端你调用时send 2次,但你send调用时,数据其实并没有立刻被发送给客户端,而是放到了系统的socket发送缓冲区里,等缓冲区满了、或者数据等待超时了,数据才会被send到客户端,这样就把好几次的小数据拼成一个大数据,统一发送到客户端了,这么做的目地是为了提高io利用效率,一次性发送总比连发好几次效率高嘛。 但也带来一个问题,就是“粘包”,即2次或多次的数据粘在了一起统一发送了。就是我们上面看到的情况。
我们在这里必须要想办法把粘包分开, 因为不分开,你就没办法取出来服务器端返回的命令执行结果的大小呀。so ,那怎么分开呢?首先你是没办法让缓冲区强制刷新把数据发给客户端的。 你能做的只有一个,就是让缓冲区超时,超时了,系统就不会等缓冲区满了,会直接把数据发走,因为不能一个劲的等后面的数据呀,等太久,会造成数据延迟了,那可是极不好的。so,如何让缓冲区超时呢?
答案就是:
1. time.sleep(0.5),经多次测试,让服务器程序sleep至少0.5就会造成缓冲区超时。哈哈,这么玩很有可能会被老板开除的,虽然我们觉得0.5s不多,但是对数据实时要求高的业务场景,比如股票交易,过了0.5s股票价格可以就涨跌很多。so,这个是很low的方法。
2. 比较好的方法就是,不用sleep,服务器端每发送一个数据给客户端,就立刻等待客户端进行回应,即调用 conn.recv(1024), 由于recv在接收不到数据时是阻塞的,这样就会造成,服务器端接收不到客户端的响应,就不会执行后面的conn.sendall(命令结果)的指令,收到客户端响应后,再发送命令结果时,缓冲区就已经被清空了,因为上一次的数据已经被强制发到客户端了。好机智,看下面代码实现。
- #encoding:utf-8 # python2.x
- #Author:Zheng Na
- # 服务器端
- import os,subprocess
- import socket
- server = socket.socket() # 获得socket实例
- server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
- server.bind(('localhost',6969)) # 绑定ip port
- server.listen(5) # 监听
- while True:
- print("等待客户端的连接...")
- conn, addr = server.accept() # 接受并建立与客户端的连接,程序在此处开始阻塞,直到有客户端连接进来...
- print("新连接:",addr)
- while True:
- data = conn.recv(1024)
- if not data:
- print("客户端断开了...")
- break # 这里断开就会再次回到第一次外层的loop
- print("收到命令:",data.decode("UTF-8"))
- # 注意:popen()可以执行shell命令,并读取此命令的返回值
- #res = os.popen(data.decode("UTF-8")).read() # python3.x里socket发送的只有bytes,os.popen又只能接受str,所以要decode一下
- #res = os.popen(data).read() # python2.x
- res = subprocess.Popen(data,shell=True,stdout=subprocess.PIPE).stdout.read() #跟上面那条命令的结果是一样的
- if len(res)==0:
- res = "cmd exec success,has not output!"
- conn.send(str(len(res)).encode("UTF-8")) # 发送数据之前先告诉客户端要发多少数据给它
- print("等待客户ack应答...")
- client_final_ack = conn.recv(1024) # 等待客户端响应
- print("客户应答:",client_final_ack.decode("UTF-8"))
- print(type(res))
- conn.sendall(res) # python3.x 只能发送bytes;发送端也有最大数量限制,所以这里用sendall,相当于重复循环电源conn.send(),直至数据发送完毕·
- server.close()
socket_server4-bigdata.py
- #encoding:utf-8 # python2.x
- #Author:Zheng Na
- # 客户端
- import socket
- import sys
- # 声明socket类型,同时生成socket连接对象
- client = socket.socket() # 默认参数family=AF_INET(表示地址簇为IPV4),type=SOCK_STREAM(表示socket类型为TCP)
- client.connect(('localhost',6969))
- while True:
- msg = input(">>: ").strip() # python3.x
- #msg = raw_input(">>: ").strip() # python2.x
- if len(msg) == 0:continue # 如果为空,重新输入
- client.send(msg.encode("UTF-8"))
- res_return_size = client.recv(1024)
- print("getting cmd result,",res_return_size.decode("UTF-8"))
- total_rece_size = int(res_return_size)
- print("total size,",total_rece_size)
- client.send("准备好接收了,发吧".encode("UTF-8"))
- received_size = 0 # 已接收到的数据
- cmd_res = b''
- f = open("test_copy.txt","wb") # 把接收到的结果存下来,一会儿看看收到的数据对不对
- while received_size != total_rece_size: # 代表还没收完
- data = client.recv(1024)
- received_size += len(data) # 为什么不是直接加1024,还判断len干嘛,注意,实际收到的data有可能比1024少
- cmd_res +=data
- else:
- print("数据收完了",received_size)
- # print(cmd_res.decode())
- f.write(cmd_res) # 把接收到的结果存下来,一会看看收到的数据对不对
- #print(data.decode()) # 命令执行结果
- client.close()
socket_client4-bigdata.py
- [root@hadoop my-test-files]# python3 socket_client4-bigdata.py
- >>: cat test.txt
- getting cmd result, 5028317
- total size, 5028317
- 数据收完了 5028317
- >>:
- [root@hadoop my-test-files]# ll test*
- -rw-r--r-- 1 root root 5028317 12月 5 18:28 test.txt
- -rw-r--r-- 1 root root 5028317 12月 5 18:33 test-copy.txt
- test-copy.txt打开后内容与原文件一致
执行结果
接下来我们再写一个传输视频的实例
- #encoding:utf-8 # python2.x
- #Author:Zheng Na
- # 服务器端
- import os,subprocess
- import socket
- server = socket.socket() # 获得socket实例
- server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
- server.bind(('localhost',6969)) # 绑定ip port
- server.listen(5) # 监听
- while True:
- print("等待客户端的连接...")
- conn, addr = server.accept() # 接受并建立与客户端的连接,程序在此处开始阻塞,直到有客户端连接进来...
- print("新连接:",addr)
- while True:
- data = conn.recv(1024)
- if not data:
- print("客户端断开了...")
- break # 这里断开就会再次回到第一次外层的loop
- print("收到命令:",data.decode("UTF-8")) #这里随便输入几个字母即可
- f = open("video-send.avi")
- res = f.read()
- conn.send(str(len(res)).encode("UTF-8")) # 发送数据之前先告诉客户端要发多少数据给它
- print("等待客户ack应答...")
- client_final_ack = conn.recv(1024) # 等待客户端响应
- print("客户应答:",client_final_ack.decode("UTF-8"))
- # print(type(res))
- conn.sendall(res) # python3.x 只能发送bytes;发送端也有最大数量限制,所以这里用sendall,相当于重复循环电源conn.send(),直至数据发送完毕·
- server.close()
socket_server5-bigdata-video.py
- #encoding:utf-8 # python2.x
- #Author:Zheng Na
- # 客户端
- import socket
- import sys
- # 声明socket类型,同时生成socket连接对象
- client = socket.socket() # 默认参数family=AF_INET(表示地址簇为IPV4),type=SOCK_STREAM(表示socket类型为TCP)
- client.connect(('localhost',6969))
- while True:
- msg = input(">>: ").strip() # python3.x
- #msg = raw_input(">>: ").strip() # python2.x
- if len(msg) == 0:continue # 如果为空,重新输入
- client.send(msg.encode("UTF-8"))
- res_return_size = client.recv(1024)
- print("getting cmd result,",res_return_size.decode("UTF-8"))
- total_rece_size = int(res_return_size)
- print("total size,",total_rece_size)
- client.send("准备好接收了,发吧".encode("UTF-8"))
- received_size = 0 # 已接收到的数据
- cmd_res = b''
- f = open("video-receive.avi","wb") # 把接收到的结果存下来,一会儿看看收到的数据对不对
- while received_size != total_rece_size: # 代表还没收完
- data = client.recv(1024)
- received_size += len(data) # 为什么不是直接加1024,还判断len干嘛,注意,实际收到的data有可能比1024少
- cmd_res +=data
- else:
- print("数据收完了",received_size)
- f.write(cmd_res) # 把接收到的结果存下来,一会看看收到的数据对不对
- client.close()
socket_client5-bigdata-video.py
- [root@hadoop my-test-files]# python3 socket_client5-bigdata-video.py
- >>: aaa
- getting cmd result, 63855708
- total size, 63855708
- 数据收完了 63855708
- >>:
- [root@hadoop my-test-files]# ll video*
- -rw-r--r-- 1 root root 63855708 12月 5 18:59 video-receive.avi
- -rw-r--r-- 1 root root 63855708 12月 4 18:26 video-send.avi
- video-receive.avi打开后可正常播放
执行结果
Python3学习之路~8.2 socket简单实例 实现ssh 发送大量数据的更多相关文章
- Python3学习之路~8.3 socket 服务端与客户端
通过8.2的实例1-6,我们可以总结出来,socket的服务端和客户端的一般建立步骤: 服务端 步骤:1创建实例,2绑定,3监听,4阻塞,5发送&接收数据,6关闭. #Author:Zheng ...
- Python3学习之路~8.1 socket概念及参数介绍
一 socket介绍 TCP/IP 基于TCP/IP协议栈的网络编程是最基本的网络编程方式,主要是使用各种编程语言,利用操作系统提供的套接字网络编程接口,直接开发各种网络应用程序. socket概念 ...
- Python3学习之路~9.1 paramiko模块:实现ssh执行命令以及传输文件
我们一般使用linux的时候,都是在Windows上安装一个ssh客户端连接上去.那么从一台linux如何连接到另一条linux呢?使用ssh命令即可,因为每台linux机器自己都有一个ssh客户端. ...
- Python3学习之路~6.3 类变量 VS 实例变量
类变量 VS 实例变量 #Author:Zheng Na # 实例里面可以查询.增加.删除.修改实例变量 class Role: # 类名 # 类变量 name = '我是类name' n=1 n_l ...
- Python3学习之路~0 目录
目录 Python3学习之路~2.1 列表.元组操作 Python3学习之路~2.2 简单的购物车程序 Python3学习之路~2.3 字符串操作 Python3学习之路~2.4 字典操作 Pytho ...
- 安卓Socket连接实现连接实现发送接收数据,openwrt wifi转串口连接单片机实现控制
安卓Socket连接实现连接实现发送接收数据,openwrt wifi转串口连接单片机实现控制 socket 连接采用流的方式进行发送接收数据,采用thread线程的方式. 什么是线程? 详细代码介 ...
- Python3学习之路~5.8 shelve模块
shelve模块是一个简单的k,v将内存数据通过文件持久化的模块,可以持久化任何pickle可支持的python数据格式 import shelve import datetime name = [& ...
- 微软企业库5.0 学习之路——第六步、使用Validation模块进行服务器端数据验证
前端时间花了1个多星期的时间写了使用jQuery.Validate进行客户端验证,但是那仅仅是客户端的验证,在开发项目的过程中,客户端的信息永远是不可信的,所以我们还需要在服务器端进行服务器端的验证已 ...
- Java后台创建Socket服务接收硬件终端发送的数据
最近项目中有遇到后台接收硬件终端发送的数据并解析存储的需求,代码总结如下(有时间再来一一讲解,最近比较忙): @Override public void start() { ExecutorServi ...
随机推荐
- 仿迅雷播放器教程 -- 封装VLC (5)
虽然上个教程中10多行代码便做出了一个播放器,但如果加上快进快退等功能的话,代码都会挤在一团,阅读性很差,所以这个版本将对VLC进行封装,由于第一个教程已经进行了ffmpeg的封装,所以这里将 ...
- golang模板语法
https://www.cnblogs.com/Pynix/p/4154630.html https://blog.csdn.net/huwh_/article/details/77140664 ht ...
- 排序算法--冒泡排序(Bubble Sort)_C#程序实现
排序算法--冒泡排序(Bubble Sort)_C#程序实现 排序(Sort)是计算机程序设计中的一种重要操作,也是日常生活中经常遇到的问题.例如,字典中的单词是以字母的顺序排列,否则,使用起来非常困 ...
- 基于IOS上MDM技术相关资料整理及汇总
(转自:http://www.mbaike.net/special/1542.html) 一.MDM相关知识:MDM (Mobile Device Management ),即移动设备管理.在21世纪 ...
- Spark RDD Action 简单用例(二)
foreach(f: T => Unit) 对RDD的所有元素应用f函数进行处理,f无返回值./** * Applies a function f to all elements of this ...
- 泡泡一分钟:Stabilize an Unsupervised Feature Learning for LiDAR-based Place Recognition
Stabilize an Unsupervised Feature Learning for LiDAR-based Place Recognition Peng Yin, Lingyun Xu, Z ...
- HDU 5950 - Recursive sequence - [矩阵快速幂加速递推][2016ACM/ICPC亚洲区沈阳站 Problem C]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5950 Farmer John likes to play mathematics games with ...
- 下载文件的协议:HTTP、FTP、P2P
本篇学习笔记以HTTP.FTP.P2P叙述与网上下载文件有关的协议 需要掌握的要点: 下载一个文件可以使用 HTTP 或 FTP,这两种都是集中下载的方式,而 P2P 则换了一种思路,采取非中心化下载 ...
- PCIe 复位:Clod reset、warm reset、Hot reset、Function level reset
2015年09月06日 17:06:01 yijingjing17 阅读数:9029 标签: PCIEReSet复位Clod resetwarm reset 更多 个人分类: PCIe ...
- Chrome浏览器如何调试移动端网页信息
Chrome浏览器如何调试移动端网页信息 2017年08月12日 12:42:20 阅读数:835 最近在弄项目,用WebView加载一个页面,想追踪页面中一个按钮的点击事件.这个可能就需要调试这个页 ...