socket
基于tcp协议socket
服务端
import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买电话 # socket.SOCK_STREAM 流式协议 就是TCP协议 phone.bind(('127.0.0.1', 8080)) # 买电话卡 phone.listen(5) # 开机。 # 5 不是链接数,链接可以产生N个,同一时刻只能监听5个请求。 conn, addr = phone.accept() # 等待接电话 # 阻塞状态 # print(222) print(conn, addr) # conn 代表的是socket通信的对象,一个管道 client_data = conn.recv(1024) # 交流过程 print(client_data) conn.send(client_data.upper()) conn.close() phone.close()
客户端
import socket phone = socket.socket() # 买电话 phone.connect(('127.0.0.1', 8080)) # 拨号 msg = input('>>>').strip() phone.send(msg.encode('utf-8')) server_data = phone.recv(1024) # 限制的是最大接收字节数。 print(server_data) phone.close()
服务端与客户端循环 聊
import socket import subprocess # socket.SOCK_STREAM 流式协议 就是TCP协议 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买电话 phone.bind(('127.0.0.1', 8080)) # 绑定电话卡,写自己的IP (俩括号) phone.listen(5) #开机 5不是限制连接数,而是同一时刻只接受5个请求,可以不写,不写为默认,默认可以开启N个 #运行后停在这里,下边不执行 while 1: conn,addr = phone.accept() #等待接电话 phone.accept() conn和addr分别接收phone..accept()生成的两个参数 print(addr) #打印连接进来的客户端 while 1: try: client_data = conn.recv(1024) #接受的字节数 c = client_data.decode('gbk') obj = subprocess.Popen(c, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) RET = obj.stdout.read() RET1 = obj.stderr.read() print(RET.decode('gbk')) conn.send(RET+RET1) #返回的信息 except Exception: break conn.close() phone.close()
客户端
import socket phone = socket.socket() phone.connect(('127.0.0.1', 8080)) # 拨号,写服务端的IP (俩括号) while 1: fasong = input('>>>') if fasong.upper() == 'Q': break elif not fasong :continue #如果发送为空,则跳过本次循环 not False3 = True phone.send(fasong.encode('gbk')) #刚给服务端发信息 server_data = phone.recv(1024) #接收服务端的信息,1024限制的是最大接受的字节数 print(server_data.decode('gbk')) phone.close()
subprocess模块
subprocess模块用于接收shell界面执行的命令并返回结果
import subprocess obj = subprocess.Popen('ip a', #输入命令 shell=True, #shell为Tree stdout=subprocess.PIPE, #stdout接收正确 stderr=subprocess.PIPE) #stderr接收错误返回 RET = obj.stdout.read() print(RET.decode('utf-8'))
粘包现象
tcp协议
1. 流式协议.数据全部都像水一样连在一起,一次性发送多字节(全部在客户端操作系统的缓存区),客户端每次只取1024字节,剩余字节在缓存区等待下次取
client_data = conn.recv(1024)
server_data = phone.recv(1024)
2.针对于(客户端/服务端)发送的连续的小的数据,对方会一次性接收.
客户端
import socket phone = socket.socket() phone.connect(('127.0.0.1', 8080)) # 拨号,写服务端的IP (俩括号) phone.send(b'hello') #先发送hello phone.send(b'word') #紧接着在发送word while 1: server_data = phone.recv(1024) #接收服务端的信息,1024限制的是最大接受的字节数 print(server_data.decode('gbk')) phone.close()
服务端
import socket import subprocess # socket.SOCK_STREAM 流式协议 就是TCP协议 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买电话 phone.bind(('127.0.0.1', 8080)) # 绑定电话卡,写自己的IP (俩括号) phone.listen(5) #开机 5不是限制连接数,而是同一时刻只接受5个请求,可以不写,不写为默认,默认可以开启N个 #运行后停在这里,下边不执行 conn,addr = phone.accept() #等待接电话 phone.accept() conn和addr分别接收phone..accept()生成的两个参数 print(addr) #打印连接进来的客户端 while 1 : client_data = conn.recv(1024) #接受的字节数 print(client_data) conn.send(client_data.upper()) #返回的信息 conn.close() phone.close() ('127.0.0.1', 53961) #服务端接收客户端的信息连在了一起 b'helloword'
客户端
import socket import time phone = socket.socket() phone.connect(('127.0.0.1', 8080)) # 拨号,写服务端的IP (俩括号) phone.send(b'hello') time.sleep(0.1) #等待0.1秒在发送word phone.send(b'word') while 1: server_data = phone.recv(1024) #接收服务端的信息,1024限制的是最大接受的字节数 print(server_data.decode('gbk')) phone.close()
服务端
import socket import subprocess # socket.SOCK_STREAM 流式协议 就是TCP协议 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买电话 phone.bind(('127.0.0.1', 8080)) # 绑定电话卡,写自己的IP (俩括号) phone.listen(5) #开机 5不是限制连接数,而是同一时刻只接受5个请求,可以不写,不写为默认,默认可以开启N个 #运行后停在这里,下边不执行 conn,addr = phone.accept() #等待接电话 phone.accept() conn和addr分别接收phone..accept()生成的两个参数 print(addr) #打印连接进来的客户端 while 1 : client_data = conn.recv(1024) #接受的字节数 print(client_data) conn.send(client_data.upper()) #返回的信息 conn.close() phone.close() ('127.0.0.1', 53985) #接受的字符分开了 b'hello' b'word'
解决粘包问题
发送固定头部,固定头部包含(数据的总大小) + 数据
struct模块 将一个数据,int 转化为固定的bytes
import struct ret = struct.pack('i',151923212) #将151923212 转换为固定的bytes类型 print(ret,type(ret) ,len(ret),sep='\n') #sep='\n',将分隔符换为\n ret1 = struct.unpack('i',ret) print(ret1) b'\x0c*\x0e\t' <class 'bytes'> 4 #转为固定的4字节 (151923212,)
解决粘包问题代码
low版
一旦数据传入过大则struct模块报错, struct模块不能转译 较长的字符串
服务端:
将数据大小通过struct模块转为固定大小发给客户端
import socket import subprocess import struct # socket.SOCK_STREAM 流式协议 就是TCP协议 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买电话 phone.bind(('127.0.0.1', 8080)) # 绑定电话卡,写自己的IP (俩括号) phone.listen(5) #开机 5不是限制连接数,而是同一时刻只接受5个请求,可以不写,不写为默认,默认可以开启N个 #运行后停在这里,下边不执行 conn,addr = phone.accept() #等待接电话 phone.accept() conn和addr分别接收phone..accept()生成的两个参数 print(addr) #打印连接进来的客户端 while 1 : #以下为粘包解决方法 try: client_data = conn.recv(1024) # 接受的字节数 obj = subprocess.Popen(client_data.decode('utf-8'), #将接收的字节在本地shell执行 并返回结果 shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,) ret = obj.stdout.read() #正确结果 ret1 = obj.stderr.read() #错误结果 ss = len(ret1 + ret) #算返回结果的总长度 ret2 = struct.pack('i',ss) #通过struct将算返回结果的总长度变为固定长度的bytes类型 conn.send(ret2) #发送报头 conn.send(ret) #发送正确结果 conn.send(ret1) #发送错误结果 except Exception: break conn.close() phone.close()
客户端:
客户端根据服务端发来的内容大小取内容
import socket import time import struct phone = socket.socket() phone.connect(('127.0.0.1', 8080)) # 拨号,写服务端的IP (俩括号) while 1: msg = input('>>>').strip() #输入发往服务端的内容,如果是q退出,如果是空从新输入 if msg.upper() == 'Q': break elif not msg: #not + 空就是 True 执行continue 重新输入 continue phone.send(msg.encode('utf-8')) #往服务端发送内容 head = phone.recv(4) #接收报头 报头大小为固定4字节 head_size = struct.unpack('i', head)[0] #直接将报头反解为服务端发送内容的长度,返回是元组,取第一个值 datas = 0 #定义一个datas大小为0 res = b'' #定义一个空的bytes类型的变量 while datas < head_size: #如果datas小于发送的内容的总长度为真 data = phone.recv(1024) #取1024字节 res = res + data #将取出的内容追加到res里 datas += len(data) #datas加上取出内容字节的大小 print(res.decode('gbk')) #读出res里的内容 phone.close()
uper版
服务端:
将服务端发送数据大小写到字典,
将字典转为json,
再将json转为字符串,
取字符串长度给struct模块转为固定大小
将固定大小,bytes类型的字典以及所有数据传给客户端
import socket import subprocess import struct import json # socket.SOCK_STREAM 流式协议 就是TCP协议 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买电话 phone.bind(('127.0.0.1', 8080)) # 绑定电话卡,写自己的IP (俩括号) phone.listen(5) #开机 5不是限制连接数,而是同一时刻只接受5个请求,可以不写,不写为默认,默认可以开启N个 #运行后停在这里,下边不执行 conn,addr = phone.accept() #等待接电话 phone.accept() conn和addr分别接收phone..accept()生成的两个参数 print(addr) #打印连接进来的客户端 while 1 : try: client_data = conn.recv(1024) # 接受的字节数 obj = subprocess.Popen(client_data.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,) ret = obj.stdout.read() ret1 = obj.stderr.read() ss = len(ret1 + ret) head_dict = { #定义一个字典,total_size的值为发送数据的大小,字典里可定义多个键值传递多种信息 'total_size':ss } head_json = json.dumps(head_dict) #将字典转为json格式 head_bytes = head_json.encode('utf-8') #再将json格式转为bytes格式 ret2 = struct.pack('i',len(head_bytes)) #将转为bytes格式的长度通过struct转为固定字节 conn.send(ret2) #发送固定字节 conn.send(head_bytes) #发送bytes类型的字典 conn.send(ret) #发送内容 conn.send(ret1) except Exception: break conn.close() phone.close()
客户端:
将服务端发送的头部大小获取字典大小
通过字典大小获取内容大小
通过内容大小获取内容
import socket import json import struct phone = socket.socket() phone.connect(('127.0.0.1', 8080)) # 拨号,写服务端的IP (俩括号) while 1: msg = input('>>>').strip() if msg.upper() == 'Q': break elif not msg: continue phone.send(msg.encode('utf-8')) he = struct.unpack('i', phone.recv(4))[0] #将头部的大小(phone.recv(4))通过struct模块解析出字典bytes类型的大小 head_dic_bytes = phone.recv(he) #通过解析出字典bytes类型的大小获取bytes类型字典的数据 head_json = head_dic_bytes.decode('utf-8') #将bytes数据反解为json类型 head_doc = json.loads(head_json) #反解json获得字典 datas = 0 res = b'' while datas < head_doc['total_size']: #将字典total_size键对应的内容大小的值取出,获得内容 data = phone.recv(1024) res = res + data datas += len(data) print(res.decode('gbk')) phone.close()
基于UDP的套接字协议
服务端
import socket udp_sk = socket.socket(type=socket.SOCK_DGRAM) # (type=socket.SOCK_DGRAM)基于UDP的套接字TCP为socket.AF_INET, socket.SOCK_STREAM udp_sk.bind(('127.0.0.1', 10000)) #绑定服务器套接字 while 1: msg, addr = udp_sk.recvfrom(1024) #tcp里的accept()里的recv() 是阻塞的 这里的recvfrom(1024)是非阻塞的 print(msg.decode('utf-8')) #打印内容 udp_sk.sendto(b'hi',addr) #返回给客户端的内容
客户端
import socket ip = ('127.0.0.1',10000) #创建个IP udp_sk=socket.socket(type=socket.SOCK_DGRAM) #开启udp的socket while 1: sd = input('>>>').encode('utf-8') udp_sk.sendto(sd,ip) #给服务端发送内容 back_msg,addr=udp_sk.recvfrom(1024) #获取服务端回复的内容 print(back_msg.decode('utf-8'),addr) #打印服务端回复的内容 udp_sk.close()