TCP与UDP协议,socket套接字编程,通信相关操作
文章目录
- TCP与UDP协议
- TCP协议
- ==三次握手==
- ==四次挥手==
- UDP协议
- TCP与UDP的区别
- 应用层
- socket套接字
- 代码优化
- 循环通信
- 半连接池
- 粘包问题
TCP与UDP协议
本质
规定了数据传输所遵循的规则 ps:数据传输能够遵循的协议有很多,TCP和UDP是较为常见的两个
TCP协议
TCP是一种面向广域网的通信协议,目的是在跨越多个网络通信时,为两个通信端点之间提供一条通信方式
三次握手
建立双向通道,中间的 FIN 和 ACK 能合并在一起
ps:洪水攻击(同时让大量的客户端朝服务端建立TCP连接的请求,容易出现状况)
第一次握手:客户端将标志位SYN设置为1,并且随机产生一个值seq=x,并且将该数据包发送给服务器,此刻客户端进入等待状态,等待服务器的确认
第二次握手:服务器接收到数据包之后,标志位是SYN=1,可以知道客户端请求连接,服务端如果同意创建连接,此刻SYN ACK都设置为1,并且ack=x+1,随机产生一个值seq=y,传递给客户端
第三次握手:客户端接收到确认之后,检查ack是否是x+1,ACK是否是1,如果都是正确的,会将标志位ACK设置为1,确认号ack设置为y+1,发送给服务端
四次挥手
断开双向通道
双方各自向对方发起建立连接的请求,再各自给对方回应,只不过,中间的 FIN 和 ACK 不一定能合并在一起
第一次挥手:客户端发送标识位FIN,用来关闭客户端到服务端的连接
第二次挥手:服务器接收到客户端发送的FIN之后,先发送ACK,ack,seq给客户端
第三次挥手:服务器发送FIN给客户端,用来关闭服务器和客户端的数据传送
第四次挥手:客户端接收到FIN之后,发送ACK,ack给服务端
注意:TCP链接是全双工的 ,全双工同时可以在2个方向上传输数据
类似于打电话的一个过程:
UDP协议
本质
基于UDP协议发送数据,没有任何的通道也没有任何的限制,提供面向交易的简单和不可靠的信息传输服务
作用
主要用于支持需要在计算机之间传输数据的网络应用
TCP与UDP的区别
1.UDP和TCP协议的主要区别是两者在如何实现信息的可靠传递方面不同
2.TCP 是面向连接的传输控制协议,而UDP 提供了无连接的数据报服务;
3.TCP 具有高可靠性,确保传输数据的正确性,不出现丢失或乱序;UDP 在传输数据前不建立连接,不对数据报进行检查与修改,无须等待对方的应答,所以会出现分组丢失、重复、乱序;
4.UDP 具有较好的实时性,工作效率较 TCP 协议高;UDP 段结构比 TCP 的段结构简单
应用层
1.应用层的目的:
应用层对应用程序的通信提供服务。
2.应用层重要协议:
FTP、SMTP和POP3、HTTP、DNS。
3.应用层的功能:
① 文件传输。访问和管理。
② 电子邮件。
③ 虚拟终端。
④ 查询服务和远程作业登录。
socket套接字
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
基于文件类型的套接字家族套接字家族的名字: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()
服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始TCP监听
s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来客户端套接字函数
s.connect() 主动初始化TCP服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常公共用途的套接字函数
s.recv() 接收TCP数据
s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
s.recvfrom() 接收UDP数据
s.sendto() 发送UDP数据
s.getpeername() 连接到当前套接字的远端的地址
s.getsockname() 当前套接字的地址
s.getsockopt() 返回指定套接字的参数
s.setsockopt() 设置指定套接字的参数
s.close() 关闭套接字面向锁的套接字方法
s.setblocking() 设置套接字的阻塞与非阻塞模式
s.settimeout() 设置阻塞套接字操作的超时时间
s.gettimeout() 得到阻塞套接字操作的超时时间面向文件的套接字的函数
s.fileno() 套接字的文件描述符
s.makefile() 创建一个与该套接字相关的文件
代码优化
循环通信
引言
主要用来打破信息只能传输一次的问题
方法
1.先解决消息固定的问题
解决办法:利用input获取用户输入
2.再解决通信循环的问题
解决方法:将双方用于数据交互的代码循环起来代码实现
while True:data = sock.recv(1024) # 听别人说话print(data.decode('utf8'))msg = input('请回复消息>>>:').strip()sock.send(msg.encode('utf8')) # 回复别人说的话while True:msg = input('请输入你需要发送的消息>>>:').strip()client.send(msg.encode('utf8')) # 给服务端发送消息data = client.recv(1024) # 接收服务端回复的消息print(data.decode('utf8'))
输入的消息不能为空(主要是针对客户端)
解决方法:判断是否为空,为空则重新输入
服务端能够持续提供服务
需求:不会因为客户端断开连接而报错 解决方法:异常捕获 一旦客户端断开连接 服务端结束通信循环 调到连接处等待
半连接池
1.什么是半连接池:
当服务器在响应了客户端的第一次请求后会进入等待状态,会等客户端发送的ack信息,这时候这个连接就称之为半连接
2.本质
半连接池其实就是一个容器,系统会自动将半连接放入这个容器中,可以避免半连接过多而保证资源耗光
3.产生半连接的两种情况:
客户端无法返回ACK信息
服务器来不及处理客户端的连接请求
粘包问题
问题:
我们知道tcp容易产生黏包的问题,而udp不会产生黏包的问题,但是会产生丢包的问题,tcp应用的场景很多所以黏包问题必须要解决。(传输信息字节大小和允许接收信息字节大小的问题)
1.TCP特性
流式协议:所有的数据类似于水流 连接在一起的
ps:数据量很小 并且时间间隔很多 那么就会自动组织到一起
2.recv
我们不知道即将要接收的数据量多大 如果知道的话不会产生也不会产生黏包
解决方法一
1.解决黏包问题第一种方法,我们知道黏包问题是由于tcp的优化算法将两个不太大的数据包合并了一起发送的,这种情况一般出现在连续使用几个send()出现的,所以我们如果知道要发送的数据有多大我们就可以设置接收的大小,这样就可以刚好能把所有的数据接收完。下面是具体的步骤细节见代码
这是远程执行cmd命令并返回结果的程序
server端代码
import struct
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()
conn,addr = sk.accept()
while True:cmd = input('>>>')conn.send(bytes(cmd,encoding='utf-8'))num = conn.recv(1024).decode('utf-8') #接收client端计算好的数据长度conn.send(bytes('ok',encoding='utf-8'))#发送一个确认防止发送num的时候跟后面的send内容合并了ret = conn.recv(num)print(ret.decode('gbk'))
conn.close()
sk.close()
client端代码
import socket
import struct
import subprocess
sk = socket.socket()
sk.connect(('127.0.0.1',8080))
while True:cmd = sk.recv(1024).decode('utf-8')ret = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)std_out = ret.stdout.read()std_err = ret.stderr.read()sk.send(bytes(str(len(std_err)+len(std_out)),encoding='utf-8'))#上面计算字符串的长度发送给server端在接收的时候刚好接收那么长的数据sk.recv(1024) #ok 这一步主要的目的是为了将num的发送跟后面的send分割开防止黏包现象sk.send(std_out)sk.send(std_err)
sk.close()
总结:但是有一个问题就是多了一次连接延时要接收一个没有用的数据 ok,如何不需要接收ok就能解决黏包现象呢?这就需要下面这种解决方案.
解决方法二
2.用struct模块解决黏包现象
server端代码#tcp黏包现象的解决 struct
import struct
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8080))
sk.listen()
conn,addr = sk.accept()
while True:cmd = input('>>>')conn.send(bytes(cmd,encoding='utf-8'))num = conn.recv(1024) #接收数据num = struct.unpack('i',num)[0]#进行解包,解包的结果是一个元组类型取第一个数据ret = conn.recv(num)print(ret.decode('gbk'))
conn.close()
sk.close()
client端代码import socket
import struct
import subprocess
sk = socket.socket()
sk.connect(('127.0.0.1',8080))
while True:cmd = sk.recv(1024).decode('utf-8')ret = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)std_out = ret.stdout.read()std_err = ret.stderr.read()num = len(std_err) + len(std_out)num = struct.pack('i',num) #利用struct模块将一个数据转换成bytes类型 i代表int型sk.send(num)sk.send(std_out)sk.send(std_err)
sk.close()
总结:
struct模块无论数据长度是多少 都可以帮你打包成固定长度
然后基于该固定长度 还可以反向解析出真实长度
struct模块针对数据量特别大的数字没有办法打包!!!
思路:
服务端
1.先将真实数据的长度制作成固定长度 4
2.先发送固定长度的报头
3.再发送真实数据
客户端
1.先接收固定长度的报头 4
2.再根据报头解压出真实长度
3.根据真实长度接收即可
TCP与UDP协议,socket套接字编程,通信相关操作相关推荐
- udp协议没有粘包问题、基于socketserver实现并发的socket(基于tcp、udp协议)、基于udp协议的套接字、操作系统原理以及进程知识
基于udp协议的套接字通信服务端 from socket import *server=socket(AF_INET,SOCK_DGRAM) #数据报协议->udp server.bind((' ...
- 基于UDP协议的socket套接字编程 基于socketserver实现并发的socket编程
基于UDP协议 的socket套接字编程 1.UDP套接字简单示例 1.1服务端 import socketserver = socket.socket(socket.AF_INET,socket.S ...
- 基于UDP协议的套接字+socketserver模块
基于UDP协议的套接字 user datagram protocal 数据报协议 无双向通道.自带报头.类似发短信.不会粘包 不可靠:数据发送时,丢就丢了 UDP socket: 服务端: impor ...
- 20181225 基于TCP/IP和基于UDP/IP的套接字编程
一.TCP/IP的套接字编程 服务器端代码: import socketserver = socket.socket() # 默认是基于TCP# 基于TCP的对象serve=socket.socke ...
- day26-2 基于TCP协议的套接字编程
目录 基于TCP协议的套接字编程 套接字 套接字工作流程 基于TCP协议的套接字编程(简单) 服务端 客户端 基于TCP协议的套接字编程(循环) 服务端 客户端1 客户端2 基于TCP协议的套接字编程 ...
- 自学Python 58 Socket套接字编程 (一)
Python Socket套接字编程 (一) 文章目录 Python Socket套接字编程 (一) 一.库 Socket 内置函数和属性 二.Python中的socket通信逻辑 Socket又 ...
- TCP与UDP协议、socket套接字编程、通信相关操作(cs架构软件)、TCP黏包问题及解决思路
OSI七层协议 传输层 1.PORT协议:前面讲过 2.TCP协议与UDP协议:规定了数据传输所遵循的规则(数据传输能够遵循的协议有很多,TCP和UDP是较为常见的两个) TCP协议 基于TCP传输数 ...
- 网络七层协议 五层模型 TCP连接 HTTP连接 socket套接字
socket(套接字)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程 ...
- TCP Socket套接字编程 附源码
TCP 最主要的特点 TCP 是面向连接的运输层协议.应用程序在使用 TCP 协议之前,必须先建立 TCP 连接.在传送数据完毕后,必须释放已经建立的 TCP 连接 每一条 TCP 连接只能有两个端点 ...
最新文章
- [译] RabbitMQ tutorials (3) ---- 'Pub/Sub' (Javascript)
- 删除有序vector中的重复值c++
- mvc php session,PHP Session入门教程
- 专访 Swin Transformer 作者胡瀚:面向计算机视觉中的「开放问题」 原创
- ttl继承逻辑门的逻辑功能与参数测试 实验总结_LMS电声测试仪,LMS-V测试系统,精声电声...
- 为什么有那么多人选择“人工智能”,真的有那么好吗?
- 基础算法 —— 高精度计算 —— 高精度乘法
- Windows Firewall/Internet Connection Sharing (ICS) 无法启动解决办法
- 东大OJ-5到100000000之间的回文质数
- python实现将给定列表划分为(等长的/不等长)的元素和大致相等的两个子列表
- notepad++中文版下载
- python解一元三次方程_利用Python的sympy包求解一元三次方程示例
- 一种特殊的魔方阵解法
- 2021年饶州中学高考成绩查询,鄱阳饶州中学2019高考成绩喜报、一本二本上线人数情况...
- 怎么在contenteditable可编辑的div插入图片
- 娑罗双树,半枯半荣,娑罗花开,盛者必衰
- 冬天别忘晒太阳[整理]
- 微信中禁止网页下拉出现网页由XXX提供
- mysql 的一个错误 Error Code: 2013. Lost connection to MySQL server during...
- Robcup2D足球学习记录【2020.01.14】
热门文章
- pygame的基础知识详解(主窗口创建、图像绘制、时钟对象和事件响应等知识点),请惠存
- 英文记忆之拆分联想法
- 字符串中包含unicode编码内容转中文打印或输出到txt文件
- 嵌入式培训如何学?什么人适合转行嵌入式开发?
- Psoc Creator入门——EZI2C 通信
- html如何创建邮件链接,如何制作电子邮件链接html
- Qt-qmake install相关
- 连接到mysql提示错误:The server time zone value is unrecognized or represents more than one time zone
- 解决Error creating bean with name ‘redisConnectionFactory‘ defined in class path resource...问题
- vue 视频播放(使用vue-video-player)