基于socket实现简单套接字通信
基于socket实现简单套接字通信
服务端:
import socket
# 1. 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 绑定手机卡
phone.bind(("127.0.0.1", 8080))
# 3. 开机
phone.listen(5) # 最大挂起链接数
# 4. 等电话链接
print("等待连接中...")
conn, client_addr = phone.accept()
print("建立连接成功,客户端地址为:", client_addr)
# 5. 收,发消息
data = conn.recv(1024) # 1. 单位:bytes 2. 1024代表最大接收1024 bytes
print("来自客户端的数据:", data)
print("处理数据,返回给客户端...")
conn.send(data.upper())
# 6. 挂电话
conn.close()
# 7. 关机
phone.close()
启动服务端后会等待连接,如下:
等待连接中...
客户端:
import socket
# 1. 买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 拨号
phone.connect(("127.0.0.1", 8080))
# 3. 发、收消息
print("发送数据 hello 给服务器端...")
phone.send("hello".encode("utf-8"))
data = phone.recv(1024)
print("服务端发过来的数据:", data)
# 4. 挂电话
phone.close()
启动客户端,会将 hello发动到服务端,服务端处理后返回回来进行打印,运行后如下:
发送数据 hello 给服务器端...
服务端发过来的数据: b'HELLO'
此时,服务端的运行结果如下:
等待连接中...
建立连接成功,客户端地址为: ('127.0.0.1', 4802)
来自客户端的数据: b'hello'
处理数据,返回给客户端...
加上通信循环
服务端:
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.bind(("127.0.0.1", 8080))
phone.listen(5)
print("等待连接中...")
conn, client_addr = phone.accept()
print("建立连接成功,客户端地址为:", client_addr)
while True:
data = conn.recv(1024) # 1. 单位:bytes 2. 1024代表最大接收1024 bytes
print("来自客户端的数据:", data)
print("处理数据,返回给客户端...")
conn.send(data.upper())
conn.close()
phone.close()
客户端:
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(("127.0.0.1", 8080))
while True:
msg = input(">>>: ").strip()
print("发送数据 hello 给服务器端...")
phone.send(msg.encode("utf-8"))
data = phone.recv(1024)
print("服务端发过来的数据:", data)
phone.close()
运行服务端:
等待连接中...
建立连接成功,客户端地址为: ('127.0.0.1', 4878)
来自客户端的数据: b'hello'
处理数据,返回给客户端...
来自客户端的数据: b'world'
处理数据,返回给客户端...
运行客户端:
>>>: hello
发送数据 hello 给服务器端...
服务端发过来的数据: b'HELLO'
>>>: world
发送数据 hello 给服务器端...
服务端发过来的数据: b'WORLD'
>>>:
处理bug
以上加上循环后可能还会出现一些bug:
1. 当端口占用或在使用中时,会出现异常
2. 当客户端直接回车时,客户端会卡主。原因是应用调用send实际上是交给操作系统去发送这个空字符串,操作系统按照tcp协议去发送,由于空字符不符合协议,所以根本就没有发。
3. 当客户端直接停掉时,服务端在Windows情况下会报错,在linux情况下会死循环
针对上面的3个问题,对程序作出修改:
服务端:
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 当端口占用时,重用链接。另一个方式是修改内核参数,只限linux系统
phone.bind(("127.0.0.1", 8080))
phone.listen(5)
print("等待连接中...")
conn, client_addr = phone.accept()
print("建立连接成功,客户端地址为:", client_addr)
while True:
try:
data = conn.recv(1024) # 1. 单位:bytes 2. 1024代表最大接收1024 bytes
if not data: break # 如果接收到的数据是空时,表示客户端已经断掉了,直接break,适用于linux系统
print("来自客户端的数据:", data)
print("处理数据,返回给客户端...")
conn.send(data.upper())
except ConnectionResetError: # 使用于Windows系统,利用try...except来捕捉异常
break
conn.close()
phone.close()
客户端:
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(("127.0.0.1", 8080))
while True:
msg = input(">>>: ").strip()
if not msg: continue # 如果直接回车是空字符串,返回重新输入
print("发送数据 hello 给服务器端...")
phone.send(msg.encode("utf-8"))
data = phone.recv(1024)
print("服务端发过来的数据:", data)
phone.close()
链接循环
上面服务端就能实现服务一个客户端,当客户端断掉时,服务端也断掉了,显然不符合常理。我们来改一下,使服务端能够服务多个客户端,使用链接循环,实际上就是加一个while循环,这里我们还不能实现并发,后边再学,但这里可以实现多个客户端一个一个的服务,当有多个客户端连接的时候,需等待之前的客户端断掉才能够被服务。
修改服务端代码如下:
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 当端口占用时,重用链接。另一个方式是修改内核参数,只限linux系统
phone.bind(("127.0.0.1", 8080))
phone.listen(5)
while True: # 链接循环,这里还不能实现并发,后边再学,但这里可以实现多个客户端一个一个的服务,当有多个客户端连接的时候,需等待之前的客户端断掉才能够被服务
print("等待连接中...")
conn, client_addr = phone.accept()
print("建立连接成功,客户端地址为:", client_addr)
while True: # 通信循环
try:
data = conn.recv(1024) # 1. 单位:bytes 2. 1024代表最大接收1024 bytes
if not data: break # 如果接收到的数据是空时,表示客户端已经断掉了,直接break,适用于linux系统
print("来自客户端的数据:", data)
print("处理数据,返回给客户端...")
conn.send(data.upper())
except ConnectionResetError: # 使用于Windows系统,利用try...except来捕捉异常
break
conn.close()
phone.close()
如何看效果,客户端拷贝多个,先启动服务端,然后启动其他的客户端,会看到服务端只能同时服务一个但是当停止一个客户端时,另一个客户端就会被服务了。
模式ssh执行命令
补充:
在服务器上执行命令有两种方式:使用os模块和subprocess模块,但是os模块不能满足本程序的需求。
来看看os模块:
如下,查看C盘下的文件或目录:
import os
os.system("dir C:\\")
输出结果(忽略乱码,是pychrm编码在Windows环境下的设置问题):
������ C �еľ�û�б�ǩ��
�������� 84E2-ADD7
C:\ ��Ŀ¼
2018/02/27 10:36 11,264 6d79d5ad-aa2d-4d41-953a-910c1dc3fcbf.bak
2017/10/25 12:41 <DIR> AMD
2018/02/06 23:12 <DIR> apache-tomcat-8.5.28
2017/11/14 20:54 1,797,880 APlayer.dll
2016/12/13 15:35 407,184 APlayerUI.dll
2017/11/10 20:28 926,352 DapCtrl.dll
2018/02/26 03:15 <DIR> ffmpeg-20180226-f4709f1-win64-static
2017/10/25 12:28 <DIR> Intel
2017/10/25 21:18 <DIR> LenovoDrivers
2018/06/27 22:49 0 MiFlashvcom.ini
2018/01/05 12:41 <DIR> PerfLogs
2018/08/09 10:58 <DIR> Program Files
2018/08/18 17:32 <DIR> Program Files (x86)
2017/12/10 20:16 <DIR> Python27
2018/05/20 16:49 <DIR> Python36
2018/05/20 15:40 <DIR> SnapPlugin
2017/10/25 12:34 <DIR> Users
2018/08/02 23:57 <DIR> Windows
5 ���ļ� 3,142,680 �ֽ�
13 ��Ŀ¼ 24,049,496,064 �����ֽ�
os.system() 只能将执行的结果打印到终端,不能够返回给其他的人,比如客户端。
你说给执行结果附一个值,不就可以返回了吗?如下:
import os
res = os.system("dir C:\\")
print(res) # 实际上输出为:0 ,表示上面的命令执行成功。非0 表示执行失败
实际上是不行的。
所以我们要用subprocess:
服务器端改为如下:
import socket
import subprocess
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
phone.bind(("127.0.0.1", 8080))
phone.listen(5)
while True:
print("等待连接...")
conn, client_addr = phone.accept()
print("建立连接,客户端地址为:", client_addr)
while True:
try:
# 1. 接收命令
cmd = conn.recv(1024)
if not cmd: break
# 2. 执行命令,拿到结果
obj = subprocess.Popen(cmd.decode("GBK"), # 使用GBK解码
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout = obj.stdout.read() # 讲对象中的数据读出来
stderr = obj.stderr.read() # 讲对象中的数据读出来
# 3. 返回结果
conn.send(stdout + stderr) # 实际上是产出一个新的空间
except ConnectionResetError:
break
conn.close()
phone.close()
客户端改为如下:
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(("127.0.0.1", 8080))
while True:
cmd = input(">>>: ").strip()
if not cmd: continue
phone.send(cmd.encode("GBK")) # 使用GBK编码,因为我在Windows上运行
data = phone.recv(1024)
print(data.decode("GBK")) # 使用GBK解码
phone.close()
最后运行结果如下:
>>>: dir
驱动器 D 中的卷是 Lenovo
卷的序列号是 D862-9EC2
D:\PycharmProjects\python_fullstack_middle\第三模块·面向对象&网络编程基础\第2章·网络编程\02 加上循环\04 模拟ssh执行命令 的目录
2018/08/19 12:43 <DIR> .
2018/08/19 12:43 <DIR> ..
2018/08/19 12:42 368 客户端.py
2018/08/19 12:43 1,128 服务端.py
2018/08/19 12:28 54 补充.py
3 个文件 1,550 字节
2 个目录 26,375,634,944 可用字节
>>>: dir C:\\
文件名、目录名或卷标语法不正确。
>>>: dir c:\
驱动器 C 中的卷没有标签。
卷的序列号是 84E2-ADD7
c:\ 的目录
2018/02/27 10:36 11,264 6d79d5ad-aa2d-4d41-953a-910c1dc3fcbf.bak
2017/10/25 12:41 <DIR> AMD
2018/02/06 23:12 <DIR> apache-tomcat-8.5.28
2017/11/14 20:54 1,797,880 APlayer.dll
2016/12/13 15:35 407,184 APlayerUI.dll
2017/11/10 20:28 926,352 DapCtrl.dll
2018/02/26 03:15 <DIR> ffmpeg-20180226-f4709f1-win64-static
2017/10/25 12:28 <DIR> Intel
2017/10/25 21:18 <DIR> LenovoDrivers
2018/06/27 22:49 0 MiFlashvcom.ini
2018/01/05 12:41 <DIR> PerfLogs
2018/08/09 10:58 <DIR> Program Files
2018/08/18 17:32 <DIR> Program Files (x86)
2017/12/10 20:16 <DIR> Python27
2018/05/20 16:49 <DIR> Python36
2018/05/20 15:40 <DIR> SnapPlugin
2017/10/25 12:34 <DIR> Users
2018/08/02 23:57 <DIR> Windows
5 个文件
>>>:
共有 0 条评论