socker通信-struct模块-粘包问题
Socket概念
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
一、socker层 (在程序中就是一个模块功能可以直接导入使用)
Socker 是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口,其实就i是一个门面模式,把复杂的协议放在socker后面。
IP地址: 127.0.0.1是本机回还地址,只能自己识别自己,其他人无法访问,用于python代码客户端和服务端的测试
二、 套接字(socker)的发展史
1:基于文件类型的套接字家族:AF_UNIX(一切皆文件)
2:基于网络类型的套接字家族:AF_INET(被用于ipv6)
三、tcp协议和udp协议
TCP:可靠的面向连接的协议(如:打电话),传输效率低于全双工通信
UDP:不可靠的、无连接的服务,传输效率高,一对一,一对多
TCP和TCP间的通信
基于TCP协议的socket
server端
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8898)) #把地址绑定到套接字
sk.listen() #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024) #接收客户端信息
print(ret) #打印客户端信息
conn.send(b'hi') #向客户端发送信息
conn.close() #关闭客户端套接字
sk.close() #关闭服务器套接字(可选)
client端
import socket
sk = socket.socket() # 创建客户套接字
sk.connect(('127.0.0.1',8898)) # 尝试连接服务器
sk.send(b'hello!')
ret = sk.recv(1024) # 对话(发送/接收)
print(ret)
sk.close() # 关闭客户套接字
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
基于UDP协议的socket
server端
import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM) #创建一个服务器的套接字
udp_sk.bind(('127.0.0.1',9000)) #绑定服务器套接字
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi',addr) # 对话(接收与发送)
udp_sk.close() # 关闭服务器套接字
client端
import socket
ip_port=('127.0.0.1',9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)
udp是无链接的,启动服务之后可以直接接受消息,不需要提前建立链接
四、套接字(socker)的使用
client(客户端)关键字:connect send recv
server(服务端)关键字:bind listen accept recv send conn.slose()
TCP协议是基于连接的,必须先启动服务端,然后在启动客户端去连接服务端:
server:服务端
import socket """"
实现服务端24小时不间断的服务功能,固定的IP和port
"""
server = socket.socket() # 生成一个对象
server.bind(('127.0.0.1',8080)) # 绑定ip和port
server.listen(5) # 半连接池
"""
用两层循环 实现客户端与服务端之间的循环交互式
"""
while True:
conn,addr = server.accept() # 循环接收用户端的请求
print(addr)
while True:
try:# 解决报错抛出的异常处理(位置,类型,)
data = conn.recv(1024)
print(data)
if len(data) == 0 : break # 针对mac和 Linux 客户端异常退出之后
conn.send(data.upper()) # 返转成大写
except ConnectionRefusedError as e: # 提示的报错信息
print(e)
break
conn.close()
client:客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1',8080))
while True:
msg = input("请输入:").encode("utf-8")
if len (msg) == 0:
continue
client.send(msg)
data = client.recv(1024)
print(data)
send(发出)与recv(接收)对应关系,不能出现两边都相同的情况
recv 是跟内存要数据的,
TCP的特点:
会将数据量比较小的并且时间间隔比较短的数据
一次性打包发送给对方
利用socker模块,用代码实现了服务端与客户端的实际交互情况,IP地址和pore端口地址必须完全匹配,其中注意一些概念 如 server.listen(5) # 半连接池 指的是规定客户端访问的量,报错的异常处理。
一个作为接收端,一个作为反馈请求端,所用的参数也不一样
五、黏包
会发生黏包的两种情况
情况一 发送方的缓存机制
发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包) 情况二 接收方的缓存机制
接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
黏包:指的是当客户端同时请求所传输的内容过大,过长是,服务端反馈的结果可能只有其中的一部分,显示不全,在执行其他命令的时候又接受收到了之前执行的另外一部分的结果。
补充的subprocess子进程模块
import subprocess
cmd = input('cmd>>>:')
obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
print(obj.stdout.read().decode('gbk')) # 正确命令返回的结果
print(obj.stderr.read().decode('gbk')) # 错误的命令返回的结果 # subprocess获取到的数据 拿完就没有了 不能重复的拿
# print(obj.stdout.read().decode('gbk')) # 正确命令返回的结果
# print(obj.stderr.read().decode('gbk')) # 错误的命令返回的结果
只能单次获取请求的数据,取完就没了, 不能重复的取
该模块可以在python解释器里,实现终端的请求命令行执行并打印结果:
它的功能以及常用的操作
# subprocess模块
# 1.用户通过网络连接上了你的这台电脑
# 2.用户输入相应的命令 基于网络发送给了你这台电脑上某个程序
# 3.获取用户命令 里面subprocess执行该用户命令
# 4.将执行结果再基于网络发送给用户
# 这样就实现 用户远程操作你这台电脑的操作
# ''
struct模块
该模块可以把一个类型,如数字,转成固定长度的bytes
struct.pack:打包
struct.unpack解包
有“i” ’q‘等模式,是处理数据大小的等级
dict_size = struct.unpack('i',header_dict)[0] # 解包的时候一定要加上索引0
服务端:结合json模块 dumps序列化成一个字典,制作一个报头
客户端:用loads把字典反序列化成字符串出来,读取报头内容
当有黏包现象存在时如何解决?(即数据过大过长时)
服务端client:
1:先制作一个发送给客户端色字典
2:制作字典的报头
3:发送字典的报头
4:再发真实的数据
客户端srever:
1.先接受字典的报头
2.解析拿到字典的数据长度
3.接受字典
4.从字典中获取真实数据的长度
5.接受真实数据
用字典打包好报头,获取固定的长度后在传输
内置函数构造:
简单黏包问题的存在,接收的数据和传出去的内容不一致导致。
client 客户端
import socket client = socket.socket() # 拿电话
client.connect(('127.0.0.1',8080)) # 拨号 写的是对方的ip和port client.send(b'hello')
client.send(b'baby')
server:服务端
import socket server = socket.socket() # 买手机 不传参数默认用的就是TCP协议
server.bind(('127.0.0.1',8080)) # bind((host,port)) 插电话卡 绑定ip和端口
server.listen(5) # 开机 半连接池 conn, addr = server.accept() # 接听电话 等着别人给你打电话 阻塞
data = conn.recv(5) # 听别人说话 接收1024个字节数据 阻塞
print(data)
data = conn.recv(5) # 听别人说话 接收1024个字节数据 阻塞
print(data)
注意:只有TCP有粘包现象,UDP永远不会粘包
实现解决黏包的问题?
客户端:client
import socket
import struct
import json client = socket.socket()
client.connect(('127.0.0.1',8080)) while True:
msg = input('>>>:').encode('utf-8')
if len(msg) == 0:continue
client.send(msg)
# 1.先接受字典报头
header_dict = client.recv(4)
# 2.解析报头 获取字典的长度
dict_size = struct.unpack('i',header_dict)[0] # 解包的时候一定要加上索引0
# 3.接收字典数据
dict_bytes = client.recv(dict_size)
dict_json = json.loads(dict_bytes.decode('utf-8'))
# 4.从字典中获取信息
print(dict_json)
recv_size = 0
real_data = b''
while recv_size < dict_json.get('file_size'): # real_size = 102400
data = client.recv(1024)
real_data += data
recv_size += len(data)
print(real_data.decode('gbk')) """
1.如何将对方发送的数据收干净
解包
服务端:server
import socket
import subprocess
import struct
import json server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5) while True:
conn, addr = server.accept()
while True:
try:
cmd = conn.recv(1024)
if len(cmd) == 0:break
cmd = cmd.decode('utf-8')
obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
res = obj.stdout.read() + obj.stderr.read()
d = {'name':'jason','file_size':len(res),'info':'asdhjkshasdad'}
json_d = json.dumps(d)
# 1.先制作一个字典的报头
header = struct.pack('i',len(json_d))
# 2.发送字典报头
conn.send(header)
# 3.发送字典
conn.send(json_d.encode('utf-8'))
# 4.再发真实数据
conn.send(res)
# conn.send(obj.stdout.read())
# conn.send(obj.stderr.read())
except ConnectionResetError:
break
conn.close()
制作包
文件的上传和下载
(重点掌握,也是基于tcp协议的一个粘包问题,利用打包和解包)
import socket
import json
import os
import struct client= socket.socket()
client.connect(('127.0.0.1',8080)) while True:
MOVIE_DIR = r'E:\python脱产10期视频\day29\视频'
movie_list= os.listdir(MOVIE_DIR)
for i,movie in enumerate(movie_list,1): # 枚举列出所有数据
print(i,movie)
# 用户选择
choice=input("请选择你要下载的视频>>:")
if choice.isdigit():
choice = int(choice)-1
if choice in range(0,len(movie_list)):
path = movie_list[choice]
file_path = os.path.join(MOVIE_DIR,path)
file_size = os.path.getsize(file_path)
res_d={
'file_name':"精彩视频要你好看.MP4",
'file_size':file_size,
'msg':"做个年少有为的青年"
}
json_d = json.dumps(res_d)
json_bytes = json_d.encode('utf-8')
header = struct.pack('i',len(json_bytes)) client.send(header)
client.send(json_bytes)
with open (file_path,'rb') as f:
for line in f :
client.send(line)
else:
print("not in range") else:
print("must be a number") """
if else 的使用 先把正确逻辑的代码写好,后面再考虑搭配来写else的其他情况 """
上传文件
import json
import socket
import struct server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn,addr = server.accept()
while True:
try:
header_len = conn.recv(4)
# 解析字典报头
header_len=struct.unpack('i',header_len)[0]
# 再接收字典数据
header_dic = conn.recv(header_len)
real_dic = json.loads(header_dic.decode('utf-8'))
total_size = real_dic.get('file_size')
recv_size = 0
with open (real_dic.get('file_name'),'wb') as f:
while recv_size < total_size:
data = conn.recv(1024)
f.write(data)
recv_size+= len(data)
print("上传成功!")
except ConnectionRefusedError as e:
print(e)
break
conn.close()
文件下载
简易版本的QQ实现:
QQ服务端:
import socket
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8080)) while True:
data,adder = server.recvfrom(1024)
msg= input(">>>>:")
server.sendto(msg.encode('utf-8'),adder)
QQ客户端:
import socket client = socket.socket(type=socket.SOCK_DGRAM)
server_address = ('127.0.0.1',8080) while True:
msg = input('>>>:')
msg = '来自客户端1的消息:%s'%msg
client.sendto(msg.encode('utf-8'),server_address)
data, server_addr = client.recvfrom(1024)
print(data.decode('utf-8'))
socker通信-struct模块-粘包问题的更多相关文章
- (day27)subprocess模块+粘包问题+struct模块+ UDP协议+socketserver
目录 昨日回顾 软件开发架构 C/S架构 B/S架构 网络编程 互联网协议 socket套接字 今日内容 一.subprocess模块 二.粘包问题 三.struct模块 四.UDP 五.QQ聊天室 ...
- python笔记8 socket(TCP) subprocess模块 粘包现象 struct模块 基于UDP的套接字协议
socket 基于tcp协议socket 服务端 import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买 ...
- scoket模块 粘包问题 tcp协议特点 重启服务器会遇到地址被占用问题
scoket()模块函数用法 import socket socket.socket(socket_family,socket_type,protocal=0) 获取tcp/ip套接字 tcpsock ...
- 网络 --- 3 socket模块 粘包
一 .socket 模块参数及方法 二.缓冲区 三.粘包 1.两种粘包现象 ①连续的小包可能会被优化算法给组合到一起进行发送 ②第一次如果发送的数据大小2000B接收端一次性接受大小为1024, 这就 ...
- socket模块粘包现象理解以及解决思路
粘包现象: 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送端为了将多个发往接收端的 ...
- struct模块-黏包的解决方法
黏包的解决方案 解决方案一 问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死 ...
- 网络编程socket模块subprocess模块 粘包的解决
什么是socket? tcp 可靠地面向连接协议 udp 不可靠的,无连接的服务,传送效率高
- 网络编程 --- subprocess模块,struct模块,粘包,UDP协议,socket_server模块
目录 subprocess模块 struct模块 粘包 UDP协议 socket_server模块 subprocess模块 作用: 1.可以帮你通过代码执行操作系统的终端命令 2.并返回终端执行命令 ...
- tcp协议传输方法&粘包问题
socket实现客户端和服务端 tcp协议可以用socket模块实现服务端可客户端的交互 # 服务端 import socket #生成一个socket对象 soc = socket.socket(s ...
随机推荐
- 全面掌握Nginx配置+快速搭建高可用架构 一 开启status页面检测服务状态
输入命令Nginx -V 打开conf.d/default.conf 配置模块,配置位置在server或者location 配置完成后测试语法正确 nginx -tc /etc/nginx/nginx ...
- 二十一、CI框架之MCV
一.我们在M模型文件里面添加一个文件,代码如下: 二.在C控制器中加载模型,并调用模型函数,输出达到View,控制器代码如下: 三.在View里面输出控制器传过来的参数 四.显示效果如下: 五.我们对 ...
- 关于SOA的架构设计案例分析
SOA体系架构及相关技术,主要应用在企业应用集成领域,它能够以服务的方式共享和复用企业现有应用资产,保护用户IT投资,并能够以服务的方式构建新的业务流程,对企业流程进行灵活重构和优化,增强业务的敏捷性 ...
- Java版本和功能指南
您可以使用本指南查找和安装最新的Java,了解Java发行版(AdoptOpenJdk,OpenJDK,OracleJDK等)之间的差异,以及获得Java语言功能的概述,包括Java版本8-13. J ...
- 读取cookie、写进cookie方法
整理 读取cookie.写进cookie方法. //设置cookies中的值 function setCookie(name, value) { var Days = 30; var exp = ne ...
- Java算法练习——两数相加
题目链接 题目描述 给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字. 如果,我们将这两个数相加起来,则会返回一个新 ...
- solr8.0.0基本安装和在springboot中的基本使用(win10)
1.下载solr 下载地址:http://archive.apache.org/dist/lucene/solr/ 该地址可以也可以下载以前的版本,我这边下载的solr-8.0.0.zip版本.下载完 ...
- delphi http server
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System ...
- Class.forName(String className)解析
功能: Class.forName(xxx.xx.xx)返回的是一个类 Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类, 也就是说JVM会执行该类的静态代码段 一 ...
- java笔记——手写+键入
Frame.setDefaultCloseOpreation() Default:默认的 设置默认关闭操作 (0:什么都不干: 1:隐藏窗口: 2:关闭窗口但继续运行程序: 3:关闭窗口关闭程序:) ...