一、用套接字实现简单通信
1.1服务端
import socket
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP地址和端口号
soc.bind(('192.168.11.195',8080))
#处于监听状态
soc.listen(5)
#准备接收数据
conn,addr = soc.accept()
#接收数据,最大为1024字节
data= conn.recv(1024)
# 打印客户端发来的信息
print('客户端发来的数据:',data)
#发送信息
conn.send(b'sjdiuamjnd')
# 关闭通信
conn.close()
#关闭连接
soc.close()
1.2 客户端
import socket
soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
soc.send(b'adndhbv')
#接收1024个字节
data = soc.recv(1024)
#打印接收的数据
print('服务端接受的数据:',data)
#关闭连接
soc.close()
二、用套接字实现通信循环
由于简单通信,客户端和服务器发一次数据就端来连接了,我们现在要用一种方法让他实现,客户端一直向服务器发消息,并且在服务器上显示内容而连接不断开。
2.1服务端
import socket
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP地址和端口号
soc.bind(('192.168.11.195',8080))
#处于监听状态
soc.listen(5)
#准备接收数据
conn,addr = soc.accept()
#接收数据,最大为1024字节
while True:
try:
data= conn.recv(1024) #每次都会卡到这里,有数据发过来的时候才会执行
# 打印客户端发来的信息
print('客户端发来的数据:',data)
except Exception: #异常处理
break
conn.close() # 关闭通信
#关闭连接
soc.close()
2.2客户端
import socket
soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
while True: #加入循环
data = input('请输入你要发送的数据>>>')
data= bytes(data,encoding='utf8') #将数据转化为bytes格式
soc.send(data)
#关闭连接
soc.close()
三、用套接字实现连接循环
由于通信循环也只是一个客户端和一个服务器进行交互,如果有其他的客户端想要连接这个服务端,就连不进去,我们怎样去实现一台服务器可以和不仅是一个客户端建立连接呢?我们就需要连接循环,只要一个客户端和服务器断了,服务器还可以等待其他客户端来连接它。
### 3.1服务端
import socket
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP地址和端口号
soc.bind(('192.168.11.195',8080))
#处于监听状态
soc.listen(5) #半连接池的大小
#准备接收数据
while True: #加入循环
print('等待客户端连接>>>>')
conn,addr = soc.accept() #每次都会卡到这里,有客户端连接才能执行下一步
print(f'客户端{addr}连接成功')
#接收数据,最大为1024字节
while True:
try:
data= conn.recv(1024) #每次都会卡到这里,有数据发过来的时候才会执行
# 打印客户端发来的信息
print('客户端发来的数据:',data)
# 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
if len(data) == 0:
break
except Exception: #异常处理
break
conn.close() # 关闭通信
#关闭连接
soc.close()
-------------------------------------------------------------------------------------
等待客户端连接>>>>
客户端('192.168.11.195', 51460)连接成功
客户端发来的数据: b'xcv'
客户端发来的数据: b'c'
等待客户端连接>>>>
客户端('192.168.11.195', 51467)连接成功
客户端发来的数据: b'zxcv'
### 3.2 客户端
import socket
soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
while True: #加入循环
data = input('请输入你要发送的数据>>>')
data= bytes(data,encoding='utf8') #将数据转化为bytes格式
soc.send(data)
#关闭连接
soc.close()
四、模拟SSH(远程执行命令)功能
4.1 subprocess模块
-
什么是subprocess模块?
执行系统命令的模块
import subprocess #导入模块
#执行系统dir命令,把执行的正确结果放到stdout管道中,把错误的命令放在stderr管道中(dir:查看当前文件夹下的所有文件,tasklist:查看当前正在运行的系统任务)
obj=subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
#拿到正确结果的管道,读出里面的内容
yes = obj.stdout.read()
no = obj.stdeer.read()
#windows的命令是gbk编码,所以用gbk解码
print('错误信息',str(yes,encoding= 'gbk'))
print('错误信息',str(no,encoding= 'gbk'))
4.2服务端
import socket
import subprocess
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP地址和端口号
soc.bind(('192.168.11.195',8080))
#处于监听状态
soc.listen(5)
#准备接收数据
while True: #加入循环
print('等待客户端连接>>>>')
conn,addr = soc.accept() #每次都会卡到这里,有客户端连接才能执行下一步
print(f'客户端{addr}连接成功')
#接收数据,最大为1024字节
while True:
try:
data= conn.recv(1024) #每次都会卡到这里,有数据发过来的时候才会执行
# 打印客户端发来的信息
print('客户端发来的数据:',data)
# 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
if len(data) == 0:
break
print(data)
obj = subprocess.Popen(str(data,encoding='utf8'),shell = True,stdout = subprocess.PIPE,stderr = subprocess.PIPE)
msg = obj.stdout.read() #将数据读出来
conn.send(msg) #将读取的数据通过网络发送给c端
print(111)
except Exception: #异常处理
break
conn.close() # 关闭通信
#关闭连接
soc.close()
4.3客户端
import socket
soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
while True: #加入循环
data_inp = input('请输入你要发送的数据>>>') #输入需要执行的命令
data_inp= bytes(data_inp,encoding='utf8') #将数据转化为bytes格式
soc.send(data_inp) #将转化好的数据发送到服务端
data_rec = soc.recv(1024) #接收来自服务端的数据数据
print(str(data_rec,encoding='gbk')) #因为Windows是gbk编码,所以用gbk解码
#关闭连接
soc.close()
五、粘包问题
我们先来看看一个粘包的例子
服务器
---------------------------------------------------------------------------
import socket
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP地址和端口号
soc.bind(('192.168.11.195',8080))
#处于监听状态
soc.listen(5)
#准备接收数据
while True: #加入循环
print('等待客户端连接>>>>')
conn,addr = soc.accept() #每次都会卡到这里,有客户端连接才能执行下一步
print(f'客户端{addr}连接成功')
#接收数据,最大为1024字节
while True:
try:
data= conn.recv(1024) #每次都会卡到这里,有数据发过来的时候才会执行
# 打印客户端发来的信息
print('客户端发来的数据:',data)
# 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
if len(data) == 0:
break
except Exception: #异常处理
break
conn.close() # 关闭通信
#关闭连接
soc.close()
-----------------------------------------------------------------
等待客户端连接>>>>
客户端('192.168.11.195', 51569)连接成功
客户端发来的数据: b'aaa'
客户端发来的数据: b'bbbccc'
客户端
---------------------------------------------------------------------------------------
import socket
soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
while True: #加入循环
data = input('请输入你要发送的数据>>>')
data= bytes(data,encoding='utf8') #将数据转化为bytes格式
soc.send(b'aaa')
soc.send(b'bbb')
soc.send(b'ccc')
#关闭连接
soc.close()
5.1 发生粘包的两种情况
- 发送端需要等缓冲区满才发送出去,造成粘包(发送时间间隔很短,数据又很小,会合到一起,产生粘包)
- 接收端不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次手的时候还是从缓冲区呐上次一流的数据,产生粘包)
5.2 解决粘包的处理办法
1)发送端在发送数据前让接收端知道字节流(数据)的大小,为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据。
服务器
-----------------------------------------------------------------------------------------
import socket
import subprocess
import struct
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP地址和端口号
soc.bind(('192.168.11.195',8080))
#处于监听状态
soc.listen(5)
#准备接收数据
while True: #加入循环
print('等待客户端连接>>>>')
conn,addr = soc.accept() #每次都会卡到这里,有客户端连接才能执行下一步
print(f'客户端{addr}连接成功')
#接收数据,最大为1024字节
while True:
try:
data= conn.recv(1024) #每次都会卡到这里,有数据发过来的时候才会执行
# 打印客户端发来的信息
print('客户端发来的数据:',data)
# 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
if len(data) == 0:
break
print(data)
obj = subprocess.Popen(str(data,encoding='utf8'),shell = True,stdout = subprocess.PIPE,stderr = subprocess.PIPE)
msg = obj.stdout.read() #将数据读出来
# 先取出要发送数据长度length #接下来是给客户端数据
length = len(msg)
# head 是固定四个字节
data_head = struct.pack('i', length) #将数据的长度的信息打包压缩成4个字节,作为数据头,先发过去
# 发了数据头
conn.send(data_head)
# 发送真正的内容
conn.send(msg) #将读取的数据通过网络发送给c端
except Exception: #异常处理
break
conn.close() # 关闭通信
#关闭连接
soc.close()
客户端
-------------------------------------------------------------------------------------
import socket
import struct ##把一个数字打包成固定长度的4字节
soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
while True: #加入循环
data_inp = input('请输入你要发送的数据>>>') #输入需要执行的命令
data_inp= bytes(data_inp,encoding='utf8') #将数据转化为bytes格式
soc.send(data_inp) #将转化好的数据发送到服务端
#接下来是收服务器发来的数据
data_head = soc.recv(4) #先收四个字节,作为数据头部
# 对数据头解压缩,得到数据的长度有多长,因为解压缩出来的数据是一个元组,所以要取出第一个数据才知道数据的长度
length= struct.unpack('i',data_head)[0]
count = 0
data_total = b'' #数据先定义为空
while count<length:
if length<1024: #如果接收的数据小于1024,直接接收数据大小,也就是把所有数据都接收到
data = soc.recv(length)
break
else:#如果接收的数据小于1024
if length-count>=1024:#总数据长度-count(目前收到多少,count就是多少) 如果还大于1024 ,再收1024
data= soc.recv(1024)
else:#总数据长度-count(目前收到多少,count就是多少) 如果小于1024,只收剩下的部分就可以
data= soc.recv(length-count)
data_total += data
count +=len(data)
print(str(data_total,encoding='gbk')) #因为Windows是gbk编码,所以用gbk解码
#关闭连接
soc.close()import socket
import struct ##把一个数字打包成固定长度的4字节
soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
while True: #加入循环
data_inp = input('请输入你要发送的数据>>>') #输入需要执行的命令
data_inp= bytes(data_inp,encoding='utf8') #将数据转化为bytes格式
soc.send(data_inp) #将转化好的数据发送到服务端
#接下来是收服务器发来的数据
data_head = soc.recv(4) #先收四个字节,作为数据头部
# 对数据头解压缩,得到数据的长度有多长,因为解压缩出来的数据是一个元组,所以要取出第一个数据才知道数据的长度
length= struct.unpack('i',data_head)[0]
count = 0
data_total = b'' #数据先定义为空
while count<length:
if length<1024: #如果接收的数据小于1024,直接接收数据大小,也就是把所有数据都接收到
data = soc.recv(length)
break
else:#如果接收的数据小于1024
if length-count>=1024:#总数据长度-count(目前收到多少,count就是多少) 如果还大于1024 ,再收1024
data= soc.recv(1024)
else:#总数据长度-count(目前收到多少,count就是多少) 如果小于1024,只收剩下的部分就可以
data= soc.recv(length-count)
data_total += data
count +=len(data)
print(str(data_total,encoding='gbk')) #因为Windows是gbk编码,所以用gbk解码
#关闭连接
soc.close()
2)我们可以把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,然后用struck将序列化后的数据长度打包成4个字节(4个字节足够用了)
-
发送时:
先发报头长度
再编码报头内容然后发送
最后发真实内容
-
接收时:
先手报头长度,用struct取出来
根据取出的长度收取报头内容,然后解码,反序列化
从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容
服务端
--------------------------------------------------------------------------------
import socket
import subprocess
import struct
import json
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP地址和端口号
soc.bind(('192.168.11.195',8080))
#处于监听状态
soc.listen(5)
#准备接收数据
while True: #加入循环
print('等待客户端连接>>>>')
conn,addr = soc.accept() #每次都会卡到这里,有客户端连接才能执行下一步
print(f'客户端{addr}连接成功')
#接收数据,最大为1024字节
while True:
try:
data= conn.recv(1024) #每次都会卡到这里,有数据发过来的时候才会执行
# 打印客户端发来的信息
print('客户端发来的数据:',data)
# 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
if len(data) == 0:
break
print(data)
obj = subprocess.Popen(str(data,encoding='utf8'),shell = True,stdout = subprocess.PIPE,stderr = subprocess.PIPE)
msg = obj.stdout.read() #将数据读出来
# 先取出要发送数据长度length #接下来是给客户端传数据)
dic= {'size':len(msg)} #定义一个字典,把数据长度放到字典里面
dic_bytes = (json.dumps(dic).encode('utf8')) #将字典序列化,转为bytes格式
# head 字典的长度是固定四个字节的长度
data_head = struct.pack('i', len(dic_bytes)) #将数据字典的长度的信息打包压缩成4个字节,作为数据头,先发过去
# print(data_head) #b'x0fx00x00x00'
# 发送数据头
conn.send(data_head) #发送4个字节的长度
# 发送真正的数据头部
conn.send(dic_bytes)
#发送真正的数据内容
conn.send(msg) #将读取的数据通过网络发送给c端
except Exception: #异常处理
break
conn.close() # 关闭通信
#关闭连接
soc.close()
客户端
-------------------------------------------------------------------------------------
import socket
import struct ##把一个数字打包成固定长度的4字节
import json
soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
while True: #加入循环
data_inp = input('请输入你要发送的数据>>>') #输入需要执行的命令
data_inp= bytes(data_inp,encoding='utf8') #将数据转化为bytes格式
soc.send(data_inp) #将转化好的数据发送到服务端(接下来其实就是把这一句做一个改造,让他更牛x)
#接下来是收服务器发来的数据
head_dic_len = soc.recv(4) #先收四个字节,这四个字节是头部字典的长度
# 对头部字典解压缩,得到字典的长度有多长,因为解压缩出来的数据是一个元组,所以要取出第一个数据才知道数据的长度
dic_len= struct.unpack('i',head_dic_len)[0]
dic_byte = soc.recv(dic_len) # byte 字典的长度,#收真正的头部字典
data_head = json.loads(dic_byte) #反序列化
print(data_head) #是一个字典{'size': 17396}
length = data_head['size']
count = 0
data_total = b'' #数据先定义为空
print(length) #17396
while count<length:
if length<1024: #如果接收的数据小于1024,直接接收数据大小,也就是把所有数据都接收到
data = soc.recv(length)
break
else:#如果接收的数据小于1024
if length-count>=1024:#总数据长度-count(目前收到多少,count就是多少) 如果还大于1024 ,再收1024
data= soc.recv(1024)
else:#总数据长度-count(目前收到多少,count就是多少) 如果小于1024,只收剩下的部分就可以
data= soc.recv(length-count)
data_total += data
count +=len(data)
print(str(data_total,encoding='gbk')) #因为Windows是gbk编码,所以用gbk解码
#关闭连接
soc.close()