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

server/2024/10/19 7:29:26/

基于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每次收发消息都是以一个数据报为单位的(套接字会给每次的消息加上消息头),每次接受消息都会一次取完。如果服务端接收的字节限制比接收内容小时,多出来的内容会丢失(windows中会报错),而不会发送粘包的问题。由于udp的消息都含有消息头,所以即便是短时间内发送多次消息,也不会发生上面说到的第二种粘包问题。
tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头。


http://www.ppmy.cn/server/132986.html

相关文章

重塑企业数字化未来:物联网与微服务架构的战略性深度融合

从物联网到微服务架构的战略价值解读 随着全球数字化转型的不断加速,企业需要重新审视其技术基础架构,以适应日益复杂的业务需求和市场变化。物联网(IoT)作为核心技术,已广泛应用于制造、农业、交通、医疗等各个行业&…

人工智能与生活:探索科技未来的无限可能性

随着科技的不断进步,人工智能已经成为了我们日常生活中不可或缺的一部分。从智能手机到智能家居,从自动驾驶汽车到医疗诊断,人工智能已经渗透到了我们的方方面面。在这篇文章中,我们将从不同的角度探讨人工智能与生活的联系&#…

uniapp 微信小程序分包操作

1. 在项目根目录创建一个新的目录,名称为分包名称 2. 打开manifest.json,选择源码视图,加入以下代码 "optimization" : {"subPackages" : true } 3. 在pages.json中,pages后面添加分包代码 "subPackag…

Java - 使用AOP+SpEL基于DB中的用户ID自动补全用户姓名

Java - 使用AOPSpEL基于DB中的用户ID自动补全用户姓名 文章目录 Java - 使用AOPSpEL基于DB中的用户ID自动补全用户姓名一、引言二、环境三、基本思路四、实现过程1. 确定切入点;2. 基于自定义注解,注册切入点;3. 在实体类上标记依赖关系&…

计算机毕业设计 | SpringBoot大型旅游网站 旅行后台管理系统(附源码)

1, 概述 1.1 项目背景 随着互联网技术的快速发展和普及,旅游行业逐渐转向线上,越来越多的游客选择在线预订旅游产品。传统的线下旅行社模式已不能满足市场需求,因此,开发一个高效、便捷的旅游网站成为行业的迫切需求…

查缺补漏----三次握手与四次挥手

注意事项: ① 如果是和FTP服务器建立连接,那么要建立两个TCP连接。一个是控制连接一个是数据连接。 ② SYN报文段不能携带数据。三次握手的最后一个报文段可以捎带数据,但是如果不携带数据,那么就不消耗序号。 ③ 在断开连接过程中…

jmeter在beanshell中使用props.put()方法的注意事项

在jmeter中,通常使用beanshell去处理一些属性的设置和获取的操作,而这些操作也是有一定的规则的。 1. 设置属性时,在属性名上要加双引号,这代表它不是一个需要用var去声明的变量 这种设置属性的方式才是有效可行的,在…

Parameter-Efficient Fine-Tuning for Large Models: A Comprehensive Survey阅读笔记

Parameter-Efficient Fine-Tuning for Large Models: A Comprehensive Survey 综述阅读笔记 仅记录个人比较感兴趣的部分 基本知识 PEFT的三种分类:additive, selective, reparameterized, and hybrid fine-tuning selective fine-tuning 不需要任何额外的参数&am…