网络通信与并发编程(二)基于tcp的套接字、基于udp的套接字、粘包现象

news/2024/10/18 16:55:10/

基于tcp的套接字

文章目录

  • 基于tcp的套接字
  • 一、套接字的工作流程
  • 二、基于tcp的套接字通信
  • 三、基于udp的套接字通信
  • 四、粘包现象

一、套接字的工作流程

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。

在这里插入图片描述

服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

二、基于tcp的套接字通信

基于上面的套接字工作原理,我们可以用python编写处如下的一段代码:

python">#服务端 
import socket#socket.AF_INET表示套接字,socket.SOCK_STREAM表示tcp,tcp也称为流式协议
#创建套接字对象
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #绑定服务端ip和端口
phone.bind(('127.0.0.1',8081))#开始监听,listen表示半连接池,限制的是请求数
phone.listen(5)# 连接循环,服务端需要一直开启等待客户端的连接(连接循环)
while True: #收到客户端的请求,通过三次握手与四次挥手建立通信通道#conn是建立的通信通道,client_addr是客户端的信息#当没有建立链接请求时,服务端会一直停在phone.accept()处conn,client_addr=phone.accept()#通信通道建立完成,与客户端持续通信(通信循环)while True: try:print('服务端正在收数据...')#为了降低内存的压力,需要限制每次接收的字节数#当没有接收到客户端的消息时,服务端会一直停在conn.recv(1024)处data=conn.recv(1024) #linux中客户端中断后服务端会接收空字符,此时需要跳出通信循环if len(data) == 0:break print('来自客户端的数据',data)#回复客户端的信息conn.send(data.upper())#windows中客户端连接中断会报错,需要用try推出通信循环except ConnectionResetError:break#关闭通信通道,服务端准备与下一个客户端建立通信链接conn.close()#关闭套接字对象
phone.close()
python">#客户端
import socketphone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#客户端不需要绑定ip和端口,只需向服务端的ip和端口发送请求
phone.connect(('127.0.0.1',8080)) # 指定服务端ip和端口#通信循环
while True: msg=input('>>: ').strip()#套接字中无法发送空字符if len(msg) == 0:continuephone.send(msg.encode('utf-8'))data=phone.recv(1024)print(data)phone.close()

如果在重启服务端的过程中出现如下的情况表示服务端仍在四次挥手的time_wait状态(服务端进程依然在后台运行),此时可以采取两种方法。

  • 修改绑定给服务端的端口号
  • 在绑定服务端的ip和端口前加上phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)

在这里插入图片描述

udp_87">三、基于udp的套接字通信

基于udp协议编写的套接字如下:

python">#服务端
import socket#socket.SOCK_DGRAM表示udp协议,udp是数据报协议
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 
server.bind(('127.0.0.1',8080))#udp协议不需要建立通信通道,因此它是不可靠的通信协议
#简单来说tcp是一对一的收发消息,一个客户端结束才会回应其他客户端
#udp是一对多的收发消息,由客户端发送消息时服务端就会回应
while True:#接收客户端的消息data,client_addr=server.recvfrom(1024)print('===>',data,client_addr)#发送消息给客户端,由于没有链接通道,发送信息需要带上客户端的ip和端口信息server.sendto(data.upper(),client_addr)server.close()
python">#客户端
import socketclient=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) while True:msg=input('>>: ').strip()#向服务端的ip和端口发送信息client.sendto(msg.encode('utf-8'),('127.0.0.1',8080))data,server_addr=client.recvfrom(1024)print(data)client.close()

四、粘包现象

将服务端的代码作如下的修改:

python">import socket,subprocessphone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',8080))phone.listen(5)while True:conn,client_addr=phone.accept()while True:try:data=conn.recv(1024)if len(data) == 0: breaka=subprocess.Popen(data.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)res=a.stdout.read()conn.send(res)except ConnectionResetError:breakconn.close()
phone.close()

我们尝试在客户端通过指令tasklist查看服务端的进程列表,第一次客户端向服务端发送tasklist命令返回如下的结果:
映像名称 PID 会话名 会话# 内存使用
========================= ======== ================ =========== ============
System Idle Process 0 Services 0 8 K
System 4 Services 0 12 K
Registry 296 Services 0 26,600 K
smss.exe 892 Services 0 528 K
csrss.exe 1124 Services 0 2,840 K
wininit.exe 1236 Services 0 3,400 K
services.exe 1308 Services 0 8,912 K
lsass.exe 1332 Services 0 20,172 K
svchost.exe 1460 Services 0 29,432 K
fontdrvhost.exe 1484 Services 0 104 K
WUDFHost.exe 1536 Services 0 2,952 K
svchost.

第二次当客户端向服务端发送ping www.baidu.com时会发现返回的结果依然是客户端的进程列表:
exe 1596 Services 0 15,092 K
svchost.exe 1640 Services 0 6,416 K
WUDFHost.exe 1764 Services 0 21,224 K
svchost.exe 1876 Services 0 3,868 K
svchost.exe 1884 Services 0 7,436 K
svchost.exe 1904 Services 0 4,420 K
svchost.exe 1940 Services 0 9,876 K
svchost.exe 1948 Services 0 7,880 K
svchost.exe 2036 Services 0 7,128 K
svchost.exe 1304 Services 0 15,372 K
svchost.exe 2128 Services 0 4,932 K
svchost.exe 2140 Services 0 6,348 K
svchost.exe 2148 Services 0 7,032 K
svchost.exe

这是怎么回事呢?我们知道tcp协议是流式协议,也就是说基于tcp协议发送消息时,服务端套接字会把需要发送的消息给自己的操作系统,而自己的操作系统将这些消息一段一段发送给客户端的操作系统,由于是一段一段的发送,客户端无法判断一条消息的始末,所以客户端套接字每次只从操作系统中取字节数限制字节的消息,当发送的消息量过大时,只有一部分消息会被接收并打印到终端上,剩余的消息依然在客户端的操作系统中。当我们再次向服务端发送消息接收消息以后,套接字会先接收上次没有接受完的消息,再接受新的消息,这就产生了粘包现象。
另外如果tcp多次短间隔的发送消息,发送端的套接字会将这些消息并再一起发送,这样会发送接受方的另一种粘包问题。
这时候肯定有人要说如果我们不限制套接字每次接受的字节数是不是就能解决这个问题呢?问题是如果我们接受的是一个很大的内容,比如50g,套接字会将接受的消息全部读入内存,这就会引发内存爆满的情况,显然这种解决方式是不可取的。
udp协议是数据报式的协议,也就是说udp每次收发消息都是以一个数据报为单位的(套接字会给每次的消息加上消息头),每次接受消息都会取干净,所以如果服务端接收的字节限制比接收内容小时,多出来的内容会丢失,而不会发送粘包的问题。由于udp的消息都是由消息头的,所以即便是短时间内发送多次消息,也不会发送上面说到的第二种粘包问题。
tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头。


http://www.ppmy.cn/news/1540040.html

相关文章

网络原理 - HTTP/HTTPS

文章目录 HTTP抓包工具浏览器内置了抓包工具WiresharkFiddler使用 HTTP 协议格式请求响应 HTTP 请求 (Request)URL 基本格式URL encode认识 "方法" (method)GET 方法POST 方法经典面试题: 谈谈 GET 和 POST 的区别其他方法 认识请求 "报头" (header) HTTP …

Go Wails 学习笔记:创建第一个项目

文章目录 1. 安装 Wails2. 创建 Wails 项目3. 项目结构4. 运行项目5. 构建项目6. 部署和发布总结 Wails 是一个用于构建跨平台桌面应用程序的框架,允许开发者使用前端技术(如 HTML、CSS、JavaScript)以及 Go 语言来开发桌面应用。本文基于官方…

Maven运行时分析多余依赖问题

下载 loosejar 库 https://github.com/kyrill007/loosejar/releases 官方文档 以 IntelliJ IDEA 开发工具为例 1. 使用IDE 打开项目工程 2. Edit Configurations ... 如图: 增加 javaagent 配置 如图: -javaagent:${保存路径}/loosejar-1.1.0.jar …

vue3使用indexDB缓存静态资源

indexDB IndexedDB 是一个浏览器内建的数据库,它可以存放对象格式的数据,默认情况下,浏览器会将自身所在的硬盘位置剩余容量全部作为indexedDB的存储容量 indexDB的使用 1.初始化数据库 注:数据库的相关操作都是异步的 const request i…

淘宝商品 API 接口怎样去使用?

​​​​​​​ 在当今数字化商业时代,淘宝作为全球最大的电子商务平台之一,拥有海量的商品资源和庞大的用户群体。对于开发者和企业来说,淘宝商品 API 接口提供了一种强大的工具,可以实现对淘宝商品数据的高效获取和利用…

基于深度学习的对抗攻击的防御

基于深度学习的对抗攻击防御是一项重要的研究方向,旨在提高模型在面对对抗样本时的鲁棒性和安全性。对抗攻击通常通过向输入数据中添加微小扰动,使得深度学习模型做出错误的预测。为了应对这些攻击,研究人员提出了多种防御策略。 1. 对抗训练…

cv2.imshow和plt.imshow的区别

同样一张灰度图,plt.imshow显示的图片和cv2.imshow显示的图片不一致,这是因为cv2.imshow和plt.imshow在处理图像显示时的默认行为有所不同。 # matplotlib绘制 plt.imshow(image, "gray") plt.show()# cv2绘制 cv2.imshow("img", i…

学习笔记之ifconfig看不到ens33的解决方法和普通用户sudo命令的配置

1.遇到的问题:使用finalshell与centos进行连接时,发现连接不上,在centos命令行敲下ifconfig发现ens33消失了。(ens33里面包含了我们远程连接所需的ip地址) 解决方法:在centos命令窗口依次输入 systemctl s…