8月5日 TCP与UDP协议,socket套接字编程 与TCP黏包问题及解决思路

时间:2022-08-05
本文章向大家介绍8月5日 TCP与UDP协议,socket套接字编程 与TCP黏包问题及解决思路,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
  • TCP与UDP协议

  • socket套接字编程

  • TCP黏包问题及解决思路

TCP与UDP协议

1.TCP协议的三次握手和四次挥手:

三次握手其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。

第一次握手是由客户端向服务器发送的syn=1数据包标志着客户端想要和服务器建立连接。

第二次握手是服务器在接收到客户端的syn返回ack=n+1标志着连接已经确认并且一个数据包被接收了,到底是对哪个数据包的确认,数据包的序列号信息存储在ack字段中

在确认后 服务器也会向客户端发送syn=2的数据包和客户端来建立联系

第三次握手客户端收到服务器的syn数据包后,会发送ack=n+2数据包来应答标志着连接已经确认。

2.UDP协议

UDP是基于IP的简单协议,不可靠的协议。

UDP的优点:简单,轻量化。

UDP的缺点:没有流程控制,没有应答确认机制,不能解决丢包、重发、错序问题。

socket套接字

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

服务端代码:

"""运行程序的时候  肯定是先确保服务端运行 之后才是客户端"""
import socket
# 1.创建一个socket对象
server = socket.socket()  # 括号内什么都不写 默认就是基于网络的TCP套接字
# 2.绑定一个固定的地址(ip\port)
server.bind(('127.0.0.1', 8080))  # 127.0.0.1本地回环地址(只允许自己的机器访问)
# 3.半连接池(暂且忽略)
server.listen(5)
# 4.开业 等待接客
sock, address = server.accept()
print(sock, address)  # sock是双向通道 address是客户端地址
# 5.数据交互
sock.send(b'hello big baby~')  # 朝客户端发送数据
data = sock.recv(1024)  # 接收客户端发送的数据 1024bytes
print(data)
# 6.断开连接
sock.close()  # 断链接
server.close()  # 关机

客户端代码:

import socket
# 1.产生一个socket对象
client = socket.socket()
# 2.连接服务端(拼接服务端的ip和port)
client.connect(('127.0.0.1', 8080))
# 3.数据交互
data = client.recv(1024)  # 接收服务端发送的数据
print(data)
client.send(b'hello sweet server')  # 朝服务端发送数据
# 4.关闭
client.close()
"""代码优化:
1.send与recv
    客户端与服务端不能同时执行同一个
        有一个收 另外一个就是发
       有一个发 另外一个就是收
    不能同时收或者发!!!
2.消息自定义
    input获取用户数据即可(主要编码解码)
3.循环通信
    给数据交互环节添加循环即可
4.服务端能够持续提供服务
    不会因为客户端断开连接而报错
        异常捕获 一旦客户端断开连接 服务端结束通信循环 调到连接处等待
5.消息不能为空
    判断是否为空 如果是则重新输入(主要针对客户端)"""

TCP黏包问题及解决思路

1.黏包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的

从发送端来看由于TCP协议本身的机制客户端与服务器会维持一个连接,数据在连接不断开的情况下,可以持续不断地将多个数据包发往服务器,但是如果发送的网络数据包太小并且时间间隔很多 那么就会自动组织到一起

从接受端来看服务器在接收到数据库后,放到缓冲区中,如果消息没有被及时从缓存区取走,下次在取数据的时候可能就会出现一次取出多个数据包的情况,造成黏包现象

2.如何解决

解决黏包问题我问可以用struct模块来解决

服务端代码:

info = '下午上课 以后可能是常态!'
print(len(info))  # 13  数据原本的长度
res = struct.pack('i', len(info))  # 将数据原本的长度打包
print(len(res))  # 4  打包之后的长度是4
ret = struct.unpack('i', res)  # 将打包之后固定长度为4的数据拆包
print(ret[0])  # 13  又得到了原本数据的长度

客户端代码:

 info1 = '打起精神啊 下午也需要奋斗 也需要认真听 克服困难 你困我也困!!!'
print(len(info1))  # 34
res = struct.pack('i', len(info1))  # 将数据原本的长度打包
print(len(res))  # 4  打包之后的长度是4
ret = struct.unpack('i', res)
print(ret[0])  # 34

struct模块无论数据长度是多少 都可以帮你打包成固定长度,然后基于该固定长度 还可以反向解析出真实长度

思路
1.先将真实数据的长度制作成固定长度 4
2.先发送固定长度的报头
3.再发送真实数据

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

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

原文地址:https://www.cnblogs.com/tai-yang77/p/16555682.html