目录

1.TCP协议和UDP协议

2.什么是socket?

3.socket正文

  1.TCP基本语法

  2.TCP循环发消息

  3.UDP基本语法

  4.UDP循环发消息

4.黏包

5.解决黏包问题

  1.解决黏包方式一:先发送接下来要发送数据的大小

  2.解决黏包方式二:conn.send("00000100".encode())

  3.前戏:struct模块

  4.解决黏包方式三:使用struct模块

1.TCP协议和UDP协议

TCP(Transmission Control Protocol)一种面向连接的、可靠的、传输层通信协议(比如:打电话)

优点:可靠,稳定,传输完整稳定,不限制数据大小

缺点:慢,效率低,占用系统资源高,一发一收都需要对方确认

应用:Web浏览器,电子邮件,文件传输,大量数据传输的场景

UDP(User Datagram Protocol)一种无连接的,不可靠的传输层通信协议(比如:发短信)

优点:速度快,可以多人同时聊天,耗费资源少,不需要建立连接

缺点:不稳定,不能保证每次数据都能接收到

应用:IP电话,实时视频会议,聊天软件,少量数据传输的场景

2.什么是socket?

socket的意义:通络通信过程中,信息拼接的工具(中文:套接字)

3.socket正文

1.TCP基本语法

服务端

#  ### 服务端
import socket
# 1.创建一个socket对象
sk = socket.socket() # 2.绑定对应的ip和端口号(让其他主机在网络中可以找得到)
"""127.0.0.1代表本地ip"""
sk.bind( ("127.0.0.1",9001) ) # 3.开启监听
sk.listen() # 4.建立三次握手
conn,addr = sk.accept() # 5.处理收发数据的逻辑
"""recv 接受 send 发送"""
res = conn.recv(1024)# 最多一次接受 1024 字节
print(res.decode("utf-8")) # 6.四次挥手
conn.close() # 7.退还端口
sk.close()

客户端

# ### 客户端
import socket
# 1.创建一个socket对象
sk = socket.socket() # 2.与服务器建立连接
sk.connect( ("127.0.0.1",9001) ) # 3.发送数据(只能发送二进制的字节流)
sk.send("北京昨天迎来了暴雨,如今有车是不行的,还得有船".encode("utf-8")) # 4.关闭连接
sk.close()

2.TCP循环发消息

服务端

# ### 服务端
import socket
# 1.创建socket对象
sk = socket.socket()
# # 在bind方法之前加上这句话,可以让一个端口重复使用
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 2.绑定ip和端口号(在网络中注册该主机)
sk.bind( ("127.0.0.1" , 9000) )
# 3.开启监听
sk.listen()
"""
print(conn)
print(addr)
conn:<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9002), raddr=('127.0.0.1', 53620)>
addr:('127.0.0.1', 53620)
""" # 5.处理收发数据的逻辑
"""send(字节流)"""
"""
conn.send("我去北京先买船".encode("utf-8"))
"""
while True:
# 4.三次握手
conn,addr = sk.accept()
while True:
res = conn.recv(1024)
print(res.decode())
strvar = input("请输入服务端要给客户端发送的内容")
conn.send(strvar.encode())
if strvar.upper() == "Q":
break # 6.四次挥手
conn.close() # 7.退还端口
sk.close()

客户端

# ### 客户端
import socket
# 1.创建socket对象
sk = socket.socket()
# 2.连接服务端
sk.connect( ("127.0.0.1" , 9000) )
# 3.收发数据
"""
res = sk.recv(1024) # 一次最多接受1024个字节
print(res.decode())
""" while True:
strvar = input("请输入您要发送的内容:")
sk.send(strvar.encode())
res = sk.recv(1024)
if res == b"q" or res == b"Q":
break
print(res.decode()) # 4.关闭连接
sk.close()

3.UDP基本语法

服务端

# ### 服务端
import socket
# 1.创建udp对象
sk = socket.socket(type=socket.SOCK_DGRAM)
# 2.绑定地址端口号
sk.bind( ("127.0.0.1",9000) )
# 3.udp服务器,在一开始只能够接受数据
msg,cli_addr = sk.recvfrom(1024) print(msg.decode())
print(cli_addr) # 服务端给客户端发送数据
msg = "我是你老娘,赶紧给我回家吃饭"
sk.sendto(msg.encode(),cli_addr) # 4.关闭连接
sk.close()

客户端

# ### 客户端
import socket
# 1.创建udp对象
sk = socket.socket(type=socket.SOCK_DGRAM) # 2.收发数据的逻辑 # 发送数据
msg = "你好,你是mm还是gg"
# sendto( 消息,(ip,端口号) )
sk.sendto( msg.encode() , ("127.0.0.1",9000) ) # 接受数据
msg,server_addr = sk.recvfrom(1024)
print(msg.decode())
print(server_addr) # 3.关闭连接
sk.close()

4.UDP循环发消息

服务端

# ### 服务端
import socket
# 1.创建udp对象
sk = socket.socket(type=socket.SOCK_DGRAM)
# 2.绑定地址端口号
sk.bind( ("127.0.0.1",9000) )
# 3.udp服务器,在一开始只能够接受数据
while True:
# 接受消息
msg,cli_addr = sk.recvfrom(1024)
print(msg.decode())
message = input("服务端给客户端发送的消息是?:")
# 发送数据
sk.sendto(message.encode() , cli_addr) # 4.关闭连接
sk.close()

客户端

# ### 客户端
import socket
# 1.创建udp对象
sk = socket.socket(type=socket.SOCK_DGRAM) # 2.收发数据的逻辑
while True:
# 发送数据
message = input("客户端给服务端发送的消息是?:")
sk.sendto(message.encode(), ("127.0.0.1",9000) ) # 接受数据
msg,addr = sk.recvfrom(1024)
print(msg.decode("utf-8")) # 3.关闭连接
sk.close()

4.黏包

1.出现黏包的原因

tcp协议在发送数据时,会出现黏包现象.

1.数据粘包是因为在客户端/服务器端都会有一个数据缓冲区,

缓冲区用来临时保存数据,为了保证能够完整的接收到数据,因此缓冲区都会设置的比较大。

2.在收发数据频繁时,由于tcp传输消息的无边界,不清楚应该截取多少长度

导致客户端/服务器端,都有可能把多条数据当成是一条数据进行截取,造成黏包

2.黏包出现的两种情况

黏包现象一:

在发送端,由于两个数据短,发送的时间隔较短,所以在发送端形成黏包

黏包现象二:

在接收端,由于两个数据几乎同时被发送到对方的缓存中,所有在接收端形成了黏包

总结:

发送端,包之间时间间隔短 或者 接收端,接受不及时, 就会黏包

核心是因为tcp对数据无边界截取,不会按照发送的顺序判断

3.黏包的应用场景

解决黏包场景:

应用场景在实时通讯时,需要阅读此次发的消息是什么

不需要解决黏包场景:

下载或者上传文件的时候,最后要把包都结合在一起,黏包无所谓.

5.解决黏包问题

1.解决黏包方式一:先发送接下来要发送数据的大小

服务端

# ### 服务端
import time
import socket
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind( ("127.0.0.1",9000) )
sk.listen() conn,addr = sk.accept() # 处理收发数据的逻辑 # 先发送接下来要发送数据的大小
conn.send("5".encode()) # 发送5个字节
# 发完长度之后,再发数据
conn.send("hello".encode())
conn.send(",world".encode()) conn.close()
sk.close()

客户端

# ### 客户端
"""
黏包出现的两种情况:
(1) 发送端发送数据太快
(2) 接收端接收数据太慢
"""
import socket
import time
sk = socket.socket()
sk.connect( ("127.0.0.1",9000) ) time.sleep(2) # 睡2s,让其接受速度慢一些,制造黏包效果
# 处理收发数据的逻辑
# 先接受接下来要发送数据的大小
res = sk.recv(1) # res = "5"
num = int(res.decode()) # num = 5
# 接受num这么多个字节数
res1 = sk.recv(num) # 一次最多只能接收5个字节
res2 = sk.recv(1024)
print(res1)
print(res2) sk.close()

2.解决黏包方式二:conn.send("00000100".encode())

服务端:

# ### 服务端
import time
import socket
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind( ("127.0.0.1",9000) )
sk.listen() conn,addr = sk.accept() # 处理收发数据的逻辑
# 先发送接下来要发送数据的大小
conn.send("00000100".encode())
# 发完长度之后,再发数据
msg = "hello" * 20
conn.send(msg.encode())
conn.send(",world".encode()) conn.close()
sk.close()

客户端:

# ### 客户端
"""
黏包出现的两种情况:
(1) 发送端发送数据太快
(2) 接收端接收数据太慢
"""
import socket
import time
sk = socket.socket()
sk.connect( ("127.0.0.1",9000) ) time.sleep(2)
# 处理收发数据的逻辑
# 先接受接下来要发送数据的大小
res = sk.recv(8)
num = int(res.decode())
# 接受num这么多个字节数
res1 = sk.recv(num)
res2 = sk.recv(1024)
print(res1)
print(res2) sk.close()

其实,这两种写法都存在一定的限制,并非最完美的解决方案

下面介绍一个模块,用来完美的解决黏包现象

3.前戏:struct模块

struct模块里有两个方法:

pack :把任意长度数字转化成具有固定4个字节长度的字节流

unpack :把4个字节值恢复成原来的数字,返回最终的是元组

import struct

# pack
# i => int 要转化的当前数据是整型
res1 = struct.pack("i",999999999)
print(res1,len(res1)) # b'\xff\xc9\x9a;' 4
res2 = struct.pack("i",1)
print(res2,len(res2)) # b'\x01\x00\x00\x00' 4
res3 = struct.pack("i",4399999)
print(res3,len(res3)) # b'\x7f#C\x00' 4
# pack 的范围 -2147483648 ~ 2147483647 21个亿左右
res4 = struct.pack("i",2100000000)
print(res4,len(res4)) # b'\x00u+}' 4 # unpack
# i => 把对应的数据转换成int整型
tup = struct.unpack("i",res)
print(tup) # (2100000000,)
print(tup[0]) # 2100000000

4.解决黏包方式三:使用struct模块

服务端

# ### 服务端
import time
import socket
import struct
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind( ("127.0.0.1",9000) )
sk.listen() conn,addr = sk.accept() # 处理收发数据的逻辑
strvar = input("请输入你要发送的数据")
msg = strvar.encode()
length = len(msg) # 你输入字符串的长度
res = struct.pack("i",length) # 无论长度是多少,res都是固定4个字节长度的字节流
print("---",res) # 第一次发送的是字节长度
conn.send(res) # 第二次发送真实的数据
conn.send(msg) # 第三次发送真实的数据
conn.send("世界真美好123".encode()) conn.close()
sk.close()

客户端

# ### 客户端
"""
黏包出现的两种情况:
(1) 发送端发送数据太快
(2) 接收端接收数据太慢
"""
import socket
import time
import struct
sk = socket.socket()
sk.connect( ("127.0.0.1",9000) ) time.sleep(2)
# 处理收发数据的逻辑 # 第一次接受的是字节长度
n = sk.recv(4) # 接收到4个字节长度的字节流
tup = struct.unpack("i",n) # 将4个字节长度的字节流转化成数字
n = tup[0] # n就是长度 # 第二次接受真实的数据
res = sk.recv(n)
print(res.decode()) # 第三次接受真实的数据
res = sk.recv(1024)
print(res.decode())
sk.close()

struct如何做到控制接受字节数的呢?

day30:TCP&UDP:socket的更多相关文章

  1. TCP&UDP&Socket讲解(上)

    这两天我将整理TCP&UDP&Socket,大约花大家10-15分钟之间,希望本篇文章让大家对TCP使用的理解提高一个层次. 建议大家拿出纸和笔,画一下!!! 一.TCP 1. TCP ...

  2. TCP/UDP,SOCKET,HTTP,FTP 简析

    (一)TCP/UDP,SOCKET,HTTP,FTP简析 TCP/IP是个协议组,可分为三个层次:网络层.传输层和应用层: 网络层:IP协议.ICMP协议.ARP协议.RARP协议和BOOTP协议 传 ...

  3. Bash Shell 下打开一个TCP / UDP SOCKET

    Bash Shell 下打开一个TCP / UDP SOCKET http://jingyan.baidu.com/article/636f38bb6166c3d6b84610d1.html

  4. TCP UDP socket http webSocket 之间的关系

    ---恢复内容开始--- OSI&TCP/IP模型 要弄清tcp udp socket http websocket之间的关系,首先要知道经典的OSI七层模型,与之对应的是TCP/IP的四层模 ...

  5. HTTP TCP UDP Socket 关系的几个经典图

      从上图可以看到,TCP/IP是个协议组,可分为三个层次:网络层.传输层和应用层. 在网络层有IP协议.ICMP协议.ARP协议.RARP协议和BOOTP协议. 在传输层中有TCP协议与UDP协议. ...

  6. TCP/UDP socket

    TCP socket:有链接,绑定端口,接着去侦听,若有请求,那么accept(),获得新的socket,并且去接收/发送数据报. UDP socket:无连接,不需要侦听,也不用一个新的socket ...

  7. How To: Perl TCP / UDP Socket Programming using IO::Socket::INET

    http://www.thegeekstuff.com/2010/07/perl-tcp-udp-socket-programming/ In this article, let us discuss ...

  8. TCP/UDP Socket调试工具提供了TCP Server,TCP Client,UDP Server,UDP Client,UDP Group 五种Socket调试方案。

    一.TCP通信测试: 1)   创建TCP Server: 选中左方的TCP Server, 然后点击”创建”按钮,软件弹出监听端口输入框 输入监听端口后,即创建了一个在指定端口上进行监听的TCP S ...

  9. tcp/udp socket编程异同

    一.TCP与UDP的区别 基于连接与无连接 对系统资源的要求(TCP较多,UDP少) UDP程序结构较简单 流模式与数据报模式 TCP保证数据正确性,UDP可能丢包 TCP保证数据顺序,UDP不保证 ...

  10. 网络协议中HTTP,TCP,UDP,Socket,WebSocket的优缺点/区别

    先说一下网络的层级:由下往上分为 物理层.数据链路层.网络层.传输层.会话层.表示层和应用层 1.TCP和UDP TCP:是面向连接的一种传输控制协议.属于传输层协议.TCP连接之后,客户端和服务器可 ...

随机推荐

  1. 工控小工具 snmp 、opc ua 、modbus 、tcp、bacnet 开发环境Net6.0

    下载地址 https://files.cnblogs.com/files/blogs/745639/net6.0-windows.rar?t=1674114312

  2. sqlserver substring 函数截取text格式文本格式乱码导致的定位错误的问题

    描述:使用 charindex 函数对 text 字段所要截取的内容下标读取例如:str(表字段名称-类型text)= <p>●123456</p> 截取 123 , inde ...

  3. 使用idea从零编写SpringCloud项目-Hystrix

    ps:Hystrix和Fegin里面使用的Hystrix,有些许区别.我理解的是Fegin.Hystrix主要是用于消费方在调用服务方接口时的异常处理,返回兜底数据等,而Hystrix则是消费方自己本 ...

  4. Vue+SSM+Element-Ui实现前后端分离(3)

    前言:经过博文(1)vue搭建,(2)ssm搭建,到这里就该真真切切的实现小功能了<-_-> 规划:实现登录,用户列表查询,访问日志aop; 开始先解决跨域:在目录config/index ...

  5. Unity学习笔记——坐标转换(3)

    通过Transform.Translate移动物体         6个重载:         public void Translate(float x, float y, float z, [De ...

  6. 【Unity】阅读LuaFramework_UGUI的一种方法

    写在前面 我第一次接触到LuaFramework_UGUI是在一个工作项目中,当时也是第一次知道toLua.但我刚开始了解LuaFramework_UGUI时十分混乱,甚至将LuaFramework_ ...

  7. vs2019 配置 qt6

    1.下载qt6 我的目录C:\Qt\6.3.1\msvc2019_64\bin C:\Qt\6.3.1\msvc2019_64\include C:\Qt\6.3.1\msvc2019_64\lib ...

  8. ffmpeg gcc is unable to create an executable file C compiler test failed

    ffmpeg 编译出现 #sudo ./configure --enable-shared --prefix=/usr/local/ffmpeg gcc is unable to create an ...

  9. Conda简单教程 - 搬运

    Conda简单教程 转自:https://www.cnblogs.com/nuccch/p/15046969.html 目录 什么是Conda 安装Conda 虚拟环境管理 模块管理 何时使用Cond ...

  10. ffmpeg的常用参数

    -encoders 查看支持的编码器 Intel处理器的核心显卡支持的编码器带有qsv后缀(Intel quick sync video acceleration)        NVIDIA独立显卡 ...