• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
菩提叶子
博客园    首页    新随笔    联系   管理    订阅  订阅
tcp和udp

一、概念

TCP(Transmission Control Protocol,传输控制协议)与UDP(User Data Protocol,用户数据协议)是互联网传输数据较为常用的协议,我们熟知的HTTP就是基于TCP的。

二、区别

 

1. 连接类型:TCP是面向连接的协议,要传输数据必须先进行连接,就是常说的“三次握手”,握手成功建立连接之后才能进行数据的传输交互,如同微信视频聊天需要对方接受才能彼此看到。UDP是非面向连接的协议,发送数据时不管对方状态直接发送,无需建立连接,如同微信发送一个消息或者语音信息,对面在不在线无所谓。

 

2. 传输开销: 由于二者特性的差异,TCP在传输数据过程中,接收方接收数据包时也会向发送方反馈数据包,因为会造成额外通信开销。UDP接收方则不会进行反馈,因此不会有这方面的额外开销。

 

3. 速度:TCP相较于UDP较慢,这也主要是因为TCP有一个连接的过程而UDP没有。

 

常见基于TCP的应用:HTTP、WebSocker、重要数据文件传输等

 

常见基于UDP的应用:QQ等部分实时通信软件、视频/音频下载传输等

 

三、tcp三次握手、四次挥手

三次握手:客户端请求服务器连接。

 客户端发送一个带有 SYN =1 标志的请求,同时随机生成一个 seq 序列号

服务端收到后,发送一个确认标志 ACK =1 和确认序列号 ack = seq+1,同时发送一个 SYN =1 标志以及序列号 seq 给客户端。这时,对客户端来说,收发消息都没有问题。但是,对服务器来说,仅仅是收到了客户端的连接请求,不能确认客户端是否收到了确认回应

第三次握手,客户端发送 ACK 确认标志以及序列号 ack

四次挥手:客户端请求服务器断开连接。

 客户端发送一个 FIN =1 的标志以及序列号 seq 给服务端

这个时候服务端可能还有数据没有传输结束,所以只是给客户端发送一个确认标志 ACK 和序列号

等传输完所有数据之后,服务器会发送一个 FIN =1 标志以及 seq 序列号给客户端,表示可以断开连接

客户端再发送一个确认标志 ACK = 1 以及序列号给服务端,断开连接

四、tcp的基本语法

 

#客户端
import socket
#1、创建socket对象
sk = socket.socket()
#2、与服务端建立连接
sk.connect((IP,端口号))
#3、发送数据
sk.send("发送内容")
#4、关闭
sk.close


#服务端
import socket
#1、创建socket对象
sk = socket.socket()
#2、绑定对应的ip和端口号
sk.bind((ip,端口号))
#3、开启监听
sk.listen()
#4、建立连接(三次握手)
conn,addr = sk.accept()
#5、处理收发数据的逻辑
res = conn.recv(1024)#接收数据的大小
#6、四次挥手
conn.close()
#7、退还端口
sk.close

 

五、udp的基本语法

#客户端
import socket
#1、创建udp对象
sk = socket.socket(type=socket.SOCK_DGRAM)
#2、收发数据
#发
msg = "发送数据"
sk.sendto(msg.encode(),(ip,端口号))

#收
msg,server_addr = sk.recvfrom(1024)#接收文件大小

#3、关闭连接
sk.close()






#服务端
import socket
#1、创建udp对象
sk = socket.socket(type=socket.SOCK_DGRAM)
#2、绑定地址端口号
sk.bind((ip,端口号))
#3、udp服务,在一开始只能接收数据
#收
msg,cli_addr = sk.recvfrom(1024)
#发
msg = "发送数据"
sk.sendto(msg.encode(),cli_addr )
#4、关闭数据
sk.close()

六、注意

#socket使用后必须退还端口,以便下次重复使用
#如果不退还端口,下次使用会报出端口占用的错误
#端口重复使用可以在bind绑定端口之前,加上一句话,保证端口重复使用
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

七、黏包

1、产生黏包的原因

因为 tcp协议发送数据是以流的形式发送,网络传输数据的速度可能会快过接收方处理数据的速度,udp是以报文形式发送

2、解决黏包

解决黏包的方式有多种,比如

1)、客户端或服务端添加sleep阻塞

2)、客户端或服务端添加边界符

3)、struct模块

3、struct模块

pack()方法将任意长度的 数字 打包成新的数据,这个新数据的长度是固定

pack()方法第一个参数是格式,第二个参数是整数(数据的长度)

unpack()方法将固定长度的 数字 解包成打包前数据真实的长度

第一个参数是格式,第二个参数是pack()方法打包后生成的新数据,返回值是一个元组,元组中放着打包前数据真实长度

如果打包的数据量非常的巨大,就会导致无法打包,可以不直接打包原始数据,打包成一个数据字典,利用json.dump序列化

4、struct基本使用

#客户端
import socket
import struct
#1、建立socket对象
sk = socket.socket()
#2、建立连接
sk.connect((ip,端口号))
#3、处理收发数据
#第一次接收字节长度
n = sk.recv(4)
tup = struct.unpack("i",n)
n = tup[0]

#第二次接收数据
res = sk.recv(n)
#第三次接收真实数据
res = sk.recv(1024)
print(res.decode())
#4、关闭
sk.close()
#服务端
import socket
import struct
#1、创建socket对象
sk = socket.socket()
#2、绑定ip和端口号
sk = bind((ip,端口号))
#3、开启监听
sk.listen()
#4、三次握手
conn,addr = sk.accept()
#处理收发数据
strvar =  "发送数据"
msg = strvar.encode()
length = len(msg)
res = struct.pack("i",length)
#第一次发送字节长度
conn.send(res)
#第二次发送数据
conn.send(msg)
#第三次发送数据
conn.send("发送大量数据".encode())
#4、四次挥手
conn.close()
#5、关闭连接
sk.close()

八、更多方法

#tcp相关函数
#服务端套接字函数
bind() #绑定ip和端口号
listen() #开启tcp监听
accept() #被动接受tcp客户连接,(阻塞式)等待连接
#客户端套接字函数
connect() #主动初始化tcp服务器连接
connect_ex() #connect函数扩展版本,出错时返回错码,而不是抛出异常

#公用套接字函数
recv() #接收tcp数据
send() #发送数据,返回值是发送[字节数量],可能小于要发送的字节数
sendall() #发送数据,返回值是None,发送所有数据






#udp相关函数
recvfrom() #接收udp数据
sendto() #发送udp数据
getpeername() #连接到当前套接字的远端地址
getsockname() #当前套接字的地址
getsockopt() # 返回指定套接字的参数
setsockopt() #设置指定套接字的参数
close() #关闭套接字

 

posted on 2022-11-01 17:59  菩提叶子  阅读(208)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3