socket套接字,粘包问题

2023-10-26

目录

scoket套接字

 socket工作流程

TCP服务端

TCP客户端

基于TCP 的SOCKET服务端与客户端

基础版本

客户端

加入连接循环

加入通信循环

支持并发的TCP服务端

常见问题:

半连接池

粘包问题

TCP协议的特点

解决粘包问题

UDP协议

服务端不需要考虑客户端是否异常退出



scoket套接字

基于文件类型的套接字家族名字:AF_UNIX

基于网络类型的套接字家族名字:AF_IN

socket(简称 套接字) 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的
例如我们每天浏览网页、QQ 聊天、收发 email 等等

1.socket介绍
1.什么是socket?
socket套接字,是进程通信的一种实现方式
2.主要特点
主要针对不同主机之间的通信。即网络进程的通信,
几乎所有的网络都是通et流程,实现网络通信

在这里插入图片描述

 socket工作流程

在这里插入图片描述


TCP服务端

首先拿到一个socket对象,socket对象与IP+PORT绑定才能进行端口监听.

然后socket对象监听端口是否有请求,同时调用accept方法,一直阻塞等待客户端的链接;

如果有客户端的请求发来,那么服务端接收到请求之后调用read方法读取数据;

之后再处理请求,写回数据使用write方法(python中是send方法)

当需要断开连接时服务端发送请求,客户端读取之后在调用close方法断开请求,

服务端read收到断开请求,服务端再调用close方法即可.

TCP客户端

首先拿到一个socket对象,调用connect方法,传入IP与PORT,发送建立链接的请求, 

之后就可以进行数据交互, 如果需要结束链接,直接调用close方法即可.

基于TCP 的SOCKET服务端与客户端

基础版本

TCP是基于链接的, 必须先启动服务端,然后再启动客户端去链接服务器

import socket
import time

server = socket.socket()

# 注意: 传入的参数必须是一个元组或者列表
# ip '127.0.0.1'是本地回环地址,只能自己玩
# 如果是别的IP地址,可以在同一个局域网里一起使用
server.bind(('127.0.0.1',8080))


# 监听 半连接池为5,相当于队列中最多有5个, 超出5个就会报错
# 同时智能服务一个人
server.listen(5)


# 等待客户端的链接
# socket 是连接对象, address是客户端地址
# 以后这个服务端和客户端使用sock这个连接对象
sock,address = server.accept()



# 接收客户端发了的数据
# 传入的参数为一次接受的字节
data = sock.recv(1024)

print(f'收到了来自{address}的数据{data.decode("utf8")}')


# 服务端给客户端发送消息
data_send = f'收到了你发送的{data}'.encode('utf8')
# 注意必须传送二进制格式数据
sock.send(data_send)

time.sleep(2)

# 关闭连接对象,此时服务器没有关闭
sock.close()

# 关闭服务端
server.close()


客户端

import socket


client = socket.socket()


# 链接服务端的地址加端口
client.connect(('127.0.0.1,8080))


# 连上以后就可以发送消息了
client.send(b'good evening')


# 收到服务端返回的消息
data = client.recv(1024)
print(data.decode('utf8'))


client.close()

加入连接循环

只加入连接循环, 此时服务端会一直接收消息看相当于死循环, 服务器不会自动断开

服务端




import socket

server = socket.socket()

server.bind(('127.0.0.1', 8008))

server.listen(5)
print('等待客户端的链接')

# 等待客户端的链接
# 链接循环
while True:
    sock, addr = server.accept()


    # 接受客户端发了的数据
    data = sock.recv(1024)

    print(f'接收到来自{addr}的数据--{data.decode("utf-8")}')


    # 服务端给客户端发送消息
    data_send = f'收到了你发送的 {data}'.encode('utf-8')
    # 注意:必须传送二进制的格式
    sock.send(data_send)


    # 关闭连接对象,此时服务端并没有关闭!!!
    sock.close()

    # 关闭服务端
    server.close()

客户端


import socket

client = socket.socket()

# 连接服务端的地址加端口
client.connect(('127.0.0.1', 8008))

# 连上以后就可以发送消息了
client.send(b'good evening')

# 收到服务端返回的消息
data = client.recv(1024)
print(data.decode('utf-8'))

client.close()

加入通信循环

加入通信循环,此时客户端可以多次发送数据

服务端



import socket

server = socket.socket()
server.bind(('127.0.0.1', 8008))
server.listen(5)
print('等待客户端的链接')

while True:
    sock, addr = server.accept()
    print(f'客户端{addr}已连接')
    # 等待接受客户端发的数据
    # 如果客户端没法,就会一直等待下去
    while True:
        try:
            # 同一个客户端,不停地发送
            data = sock.recv(1024)
            # 当客户端主动断开后,客户端会发送一个空的数据过来
            # 此时服务端必须对这种情况进行处理,否则会出现死循环
            if len(data) == 0:
                print(f'客户端{addr}已断开', '\n')
                break

            print(f'接收到来自{addr}的数据--{data.decode("utf-8")}')
            data_send = f'收到了你发送的 {data}'.encode('utf-8')
            sock.send(data_send)
        except Exception as e:
            print(e)
            break

    sock.close()

    # 关闭服务端
    server.close()

客户端


import socket

client = socket.socket()
client.connect(('127.0.0.1', 8008))
# 连上以后就可以发送消息了

while True:
    data = input('输入发送给服务端的消息(q to quit):')
    if data == 'q': break
    client.send(data.encode("utf-8"))

    data = client.recv(1024)
    print(data.decode('utf-8'))

    client.close()


支持并发的TCP服务端

在这里是开启多进程完成的并发,可以换成多线程, 只要将Process类换成Thread类

import socket
from multiprocessing import Process

def talk(sock,addr):
    print('客户端连接成功', addr)
    while True:
        try:
            data = sock.recv(1024)
            if len(data) == 0:
                print(f'客户端{addr}已断开', '\n')
                break

            print(f'接收到来自{addr}的数据--{data.decode("utf-8")}')
            data_send = f'收到了你发送的 {data}'.encode('utf-8')
            sock.send(data_send)
        except Exception as e:
            print(e)
            break
    sock.close()


if __name__ == '__main__':
    server = socket.socket()
    server.bind(('127.0.0.1', 81))
    server.listen(5)
    while True:
        sock,addr = server.accept()
        print(f'客户端{addr}已连接')
        # 连接上一个客户端之后开启一个进程,对该客户端进行服务
        p = Process(target=talk, args=(sock, addr))
        p.start()

        server.close()

常见问题:

1.发送消息不能为空 >>>> 统计长度饼判断即可

2. 反复重启服务端可能会报错 >>> address in use

        这个错在苹果本上会比较频繁出现,windows频率较少

from socket import SOL_SOCKET,SO_REUSEADDR
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 在bind前加

3. 连接循环

如果是Windows电脑 客户端异常推出之后服务端会直接报错

处理方式 >>> 异常处理

如果是mac或者linux服务端会接收到一个空消息

处理方式 >>> len判断

客户端如果异常断开,服务端代码应该重新回到accept等待新的客人

目前的服务端只能实现一次服务一个人 不能做到同时服务多人, [并发状态下可以实现]

4. 加入通信循环之后,想要同一个py文件启动多个客户端, pycharm会出现以下提示,不允许启动多个

在这里插入图片描述

 解决办法

 

半连接池

listen(5)

# py文件默认同一时间只能运行一次 

# 半连接池
# 设置最大等待人数 >>> 节省资源 提高CPU利用率

粘包问题

data1 = conn.recv(1024)
print(data1)
data2 = conn.recv(1024)
print(data2)
data3 = conn.recv(1024)
print(data3)

client.send(b'hello')
client.send(b'jason')
client.send(b'kevin')
"""
三次打印的结果
  b'hellojasonkevin'
  b''
  b''
"""

TCP协议的特点

会将数据量比较小 并且时间间隔比较短的数据整合到一起发送

并且还会受制于recv()内数字的大小,此现象我们称之为''六十协议''

>>> 问题产生的原因是因为recv括号内我们不知道即将要接收的数据到底有多大, 如果每次接受的收据我们都能够精准的知道他的大小, 那么肯定不会有粘包问题出现<<<<

如果我们能知道即将要接收的数据量大小那么粘包问题就解决了~

解决粘包问题


  
# struct模块
	import struct

  data1 = 'hello world!'
  print(len(data1))  # 12
  res1 = struct.pack('i', len(data1))  # 第一个参数是格式 写i就可以了
  print(len(res1))  # 4
  ret1 = struct.unpack('i', res1)
  print(ret1)  # (12,)


  data2 = 'hello baby baby baby baby baby baby baby baby'
  print(len(data2))  # 45
  res2 = struct.pack('i', len(data2))
  print(len(res2))  # 4
  ret2 = struct.unpack('i', res2)
  print(ret2)  # (45,)



pack可以讲任意长度的数字打包成固定长度

unpack可以将固定长度的数字解包成打包之前的数据真实长度

>>> 将真实数据打包成固定长度的包

>>> 将固定长度的包发送给对方

>>> 对方接收到包之后再解包获取真实数据长度

>>> 接受真是长度的数据

<<<<<<< 1.先接收固定长度的报头 
        2.再根据报头解压出真实长度 
        3.根据真实长度接收即可<<<<<<<<<<<<<<

struct模块针对数据量特别大的数字没有办法打包

UDP协议

服务端

# 服务端
import socket
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1', 8080))
msg, address = server.recvfrom(1024)
print('msg>>>:%s' % msg.decode('utf8'))
print('address>>>:',address)
server.sendto('我是服务端 你好啊'.encode('utf8'), address)

客户端

# 客户端
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_address = ('127.0.0.1', 8080)
client.sendto('我是客户端 想我了没'.encode('utf8'), server_address)
msg, address = client.recvfrom(1024)
print('msg>>>:%s' % msg.decode('utf8'))
print('address>>>:',address)

服务端不需要考虑客户端是否异常退出

UDP不存在粘包问题, UDP多用于短消息交互,如QQ

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

socket套接字,粘包问题 的相关文章

随机推荐

  • 求二元函数最大值matlab,利用matlab, 二元函数求最大值

    求二元函数 z 0 2323 x 2 0 2866 2 2 0 5406 a0 2 1 0203 a0 2 x 2 x 2 y 2 0 5 tanh 2 x 2 y 2 0 5 x 2 0 5733 u0 2 的最大值 变量x和y都是在0
  • React -css in js框架style-components

    原文 https www jianshu com p 27788be90605 前言 前端飞一般的发展中 衍生出各式各样的框架 框架的目的是减轻开发人员的开发难度 提高效率 以前网页开发的原则是关注点分离 意思是各种技术只负责自己的领域 不
  • 【偷偷卷死小伙伴Pytorch20天-day16-损失函数】

    最近这几天忙着开学返校的事情 终于几番周折回到了学校 继续pytorch的学习打卡 一般来说 监督学习的目标函数由损失函数和正则化项组成 Objective Loss Regularization Pytorch中的损失函数一般在训练模型时
  • 服务器 文件类型,linux服务器支持的文件类型

    linux服务器支持的文件类型 内容精选 换一换 弹性云服务器卸载磁盘 弹性云服务器状态为stopped时支持系统盘 也就是 dev sda挂载点 和用户盘的卸载 没有操作系统限制 也不需要在弹性云服务器内部安装vmtools 弹性云服务器
  • 【深度解析→博文总结】李宏毅机器学习2023作业01Regression(COVID-19 Cases Prediction)

    文章目录 系列文章 简要说明 视频分享 作业详情 调参记录 Simple Baseline 1 96993 Medium Baseline 1 15678 Strong Baseline 0 92619 Boss Baseline 0 81
  • Seata1.2.0配置及分布式事务失效解决

    配置 版本说明 springCloud Alibaba组件版本关系 我用的是 cloud Alibaba 2 2 1 RELEASE springboot 2 2 5 RELEASE nacos 1 2 1 seata1 2 0 1 配置数
  • booth算法

    booth算法 1 booth算法定义 2 二进制乘法过程 3 二进制乘法转换成 booth乘法运算 4 Radix 2 Booth乘法器 5 Radix 4 Booth乘法器 6 Booth乘法器计算实例 1 booth算法定义 将乘数看
  • 【CV】图像分类中的细粒度/粗粒度怎么理解

    粗粒度图像分类 类别之间差异大 比如人 汽车 树 细粒度图像分类 类别之间差异小 比如200种鸟的分类 100种花的分类 由于细粒度类别属于同一个大类 所以各类别之间的差距很小 这些细微的差距容易被光照 颜色 背景 形状和位置等变化因素覆盖
  • Python作图——numpy库和matplotlib库

    一 numpy库 1 1概述 numpy是一个存储和处理多维数组 矩阵等的库 提供多种关于数组运算的数学函数 可供直接调用 1 1 1数据类型 numpy的数据类型包括整型 浮点型 复数型 布尔型等 在IDLE查询numpy支持的数据类型
  • MATLAB .dat读、存及简单处理

    文章目录 0 前言 1 思路 2 MATLAB 3 结语 0 前言 近期接触到二进制文件 dat 写一个简单的教程 假设文件内容为连续的通信数据 含有不符合的数据 对其进行简单剔除 1 思路 假设输入 dat文件共有3个整帧数据 每帧长度5
  • 机器学习(归一化、去中心化、标准化)

    为什么要进行数据的预处理 这需要分两种情况说明 1 数据数值很大 2 数据数值很小 1 首先 对于一个数值非常之大的特征 T 若其数值非常之大 区间也非常之大 例如区间范围为 10 10 10 20 以线性拟合函数举例 显然我们在进行机器学
  • Android:JNI调用C++自定义类的详细方法

    一般情况下 我们都是用 JNI 调用 C 的某个方法的代码 包括直接使用 android studio 生成的代码也是如此 但有时我们需要新建 或者得到的是 C 的一个自定义类 在调用时就不能像调用 C 方法那样了 查阅了一部分其他人的博客
  • 学习笔记五:电路设计需要注意的那些事

    注意One part 电路中电源VBAT须要先经过滤波电容再到芯片的VBAT引脚 芯片时钟端走线尽量不要靠近电源走线 避免对电源走线产生纹波的干扰 需要隔离地处理 天线走50欧姆的阻抗设计 走线两侧距离30mil处添加过孔 最好用第三层GN
  • Adobe MAX 2020:最新版Photoshop提供AI智能神经滤镜及天空替换等功能

    数据猿年度重磅活动预告 2020年度金猿策划活动 金猿榜单发布 金猿奖杯颁发 即将推出 尽情咨询期待 大数据产业创新服务媒体 聚焦数据 改变商业 数据报道 受新冠疫情影响 今年的Adobe MAX 2020 于北京时间10月21日改为了线上
  • 一个优秀的程序员真的能顶10个普通的程序员吗?

    一个优秀的程序员真的能顶10个普通的程序员吗 一 前言 优秀的程序员 比糟糕的程序员的工作产出高100倍 二 糟糕程序员会有哪些表现 1 无法对代码进行推理 2 没有补救措施 3 代码难以改动 三 优秀程序员是怎么做的呢 1 先进行实验是他
  • Java+SSM+Vue 毕业设计 游戏攻略资讯补丁售卖商城(含源码+论文)

    文章目录 1 项目简介 2 实现效果 2 1 界面展示 3 设计方案 3 1 概述 3 2 系统流程 3 3 系统结构设计 4 项目获取 1 项目简介 Hi 各位同学好呀 这里是M学姐 今天向大家分享一个今年 2022 最新完成的毕业设计项
  • Win32学习笔记(16)消息类型

    1 消息的产生与处理流程 如上图 我们在创建窗口程序中详细介绍了这张图 比如 我们点击某个窗口的时候就会产生消息 操作系统会先判断这个消息是点在了哪个窗口 找到窗口后 会根据窗口对象中的一个成员找到窗口是属于哪个线程的 找到后就会把他封装好
  • 从零开始搭建一个 React 项目 -- 配置篇(一)

    从零开始搭建一个 React 项目 配置篇 一 参考资源 从零搭建完整的React项目模板 Webpack React hooks Mobx Antd 1 项目初始化及常用以来安装配置 1 创建名为 react admin demo 的目录
  • python虚拟环境配置、Python代码打包成exe可执行文件

    背景 因工作需要 要打包一些脚本使其成为exe文件 方便未安装python环境的电脑运行脚本 但是直接使用默认环境的话 会有很多的包 但是其实这个脚本根本用不到 导致生成的exe文件很大或者直接打包失败 所以创建一个虚拟环境 只安装该代码需
  • socket套接字,粘包问题

    目录 scoket套接字 socket工作流程 TCP服务端 TCP客户端 基于TCP 的SOCKET服务端与客户端 基础版本 客户端 加入连接循环 加入通信循环 支持并发的TCP服务端 常见问题 半连接池 粘包问题 TCP协议的特点 解决