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

news2025/2/28 0:23:16

基于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编写处如下的一段代码:

#服务端 
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()

#客户端
import socket

phone=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:continue
    phone.send(msg.encode('utf-8'))
    data=phone.recv(1024)
    print(data)

phone.close()

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

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

在这里插入图片描述

三、基于udp的套接字通信

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

#服务端
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()
#客户端
import socket

client=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()

四、粘包现象

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

import socket,subprocess

phone=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: break
            a=subprocess.Popen(data.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            res=a.stdout.read()
            conn.send(res)
        except ConnectionResetError:
            break
    conn.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.coloradmin.cn/o/2214111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习底座架构-武汉

1 学习底座架构概述 大脑学习中心-边缘系统 一、当下教育现状 二、什么是学习底座 三、学习底座价值 七、学习底座解决问题的流程 案例:以4R注意力为例 一 注意力问题解决流程 二 注意力问题的危害 三 衡量注意力水平高低的标准 四 注意力问题4大根源 & 2大诱因…

FPGA驱动HDMI 初级篇

简介 本章节主要讲述如何通过FPGA驱动HDMI显示。 本章节框图如下: bd框图中使用了两个IP核,分别是Video Timing controller核AXI4-Stream to video out两个模块,下面先对两个模块做介绍。 Video Timing controller 配置如下: 这里由于没有使用ZYNQ PS端,…

BKD树介绍、区别与联系

简介 BKD树(全称 bushy kd-trees)是一种用于高维数据搜索的数据结构。它结合了K-D树和B树的特点,旨在提高多维空间数据的索引和查询效率。 基本概念 BKD树是一种二叉树结构,类似于K-D树,但其设计更加注重空间利用率…

10.11Python数学基础-多维随机变量及其分布

多维随机变量及其分布 1.二维随机变量及其分布 假设E是随机试验,Ω是样本空间,X、Y是Ω的两个变量;(X,Y)就叫做二维随机变量或二维随机向量。X、Y来自同一个样本空间。 联合分布函数 F ( x , y ) P ( X ≤ x , Y ≤ y ) F(x,y)P(X≤x,Y≤…

SpringBoot+XXL-JOB:高效定时任务管理

前言 在现代应用程序中,定时任务是不可或缺的一部分。Spring Boot 和 XXL-Job 为你提供了一个强大的工具组合,以简化任务调度和管理。 本文将带领你探索如何将这两者集成在一起,实现高效的定时任务管理。无论你是初学者还是有经验的开发者&…

【进阶】面向对象之接口

文章目录 为什么要有接口如何定义和使用一个接口练习 接口里面的成员特点接口和类之间的关系 为什么要有接口 接口:就是一种规则 如何定义和使用一个接口 接口用关键字interface来定义 public interface 接口名{}接口不能实例化接口和类之间是实现关系,通过imple…

集合的基础操作

1.集合中元素没有顺序,且不会重复,输入的数据若有重复则会去重 2.集合的格式为:变量名{##,##,##} 3.集合中元素的添加格式为:变量名.add(添加的元素) 4.集合中元素的取出格式(取出元素后,原集合中该元素没有了&#…

软件测试学习笔记丨Linux三剑客-grep

本文转自测试人社区,原文链接:https://ceshiren.com/t/topic/32506 一、简介 1.1 grep命令 grep是一个全局查找正则表达式,并且打印结果行的命令。grep的输入是一个文件或者一个标准输入(stdin),或者是一…

JAVA多线程实现

一、方法总结 Thread类的常用方法 void setName(String name)//将此线程的名称更改为name String getName()//如果不设置名称,则线程默认名称为Thread-0或Thread-1或…就是Thread -? String Thread.currentThread().getName()//获得当前线程名称 …

C++常见的内存错误和解决策略

目录 1.未初始化指针 (Uninitialized Pointer) 2.内存分配未成功却使用了它 3.野指针 (Dangling Pointer) 4.内存泄漏 (Memory Leak) 5.重复释放内存 (Double Free) 6.内存越界访问 (Buffer Overflow) 7.错误的数组删除方式 (Mismatched Delete) 8.栈内存溢出 (Stack O…

24/10/14 算法笔记 循环神经网络RNN

RNN: 一种专门用于处理序列数据的神经网络,它能够捕捉时间序列中的动态特征。RNN的核心特点是其循环连接,这允许网络在不同时间步之间传递信息,从而实现对序列数据的记忆和处理能力。 应用的场景: 自然语言处理(NLP&…

这款懂人情世故的大模型强得可怕!

这款孵化于首期书生大模型实战营,基于 InternLM2 开发的项目——天机,更懂人情世故的大模型,这 2 天在社区可谓有点火!相关内容在小红书上至少收获了六千多点赞与收藏 ! 你是否还在苦苦挣扎于各种应酬,四处寻找“高情商…

MySQL-04.DDL-数据库操作

一.数据库的操作 DDL(data definition language):数据定义语言,用来定义数据库对象(数据库、表) DDL分为两类:1.数据库的DDL语句,主要是针对数据库的定义,增加,删除,使用 2.表结构的DDL语句&…

大数据-160 Apache Kylin 构建Cube 按照日期构建Cube 详细记录

点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…

TuyaOS开发学习笔记(4)——BLE开发搭建环境、编译烧写(NRF52832)

一、搭建环境 1.1 官方资料 TuyaOS 1.2 安装Visual Studio Code 官网下载:https://code.visualstudio.com 百度网盘:https://pan.baidu.com/s/1R62HT0PVmVzMwOXtCmIQwA 提取码:g9fb 1.3 安装Tuya Wind IDE 启动 Visual Studio Code 后&am…

windows10 输入法突然变成繁体字。

1.在任务栏的输入法上切换到设置页面 2. 进入语言选项 3.浮动在桌面上? 4. 点击繁体或简体切换。

IWO-Kmeans聚类 | MATLAB实现IWO-Kmeans侵入性杂草优化K均值聚类算法

智能优化 | MATLAB实现IWO-Kmeans侵入性杂草优化K均值聚类算法 目录 智能优化 | MATLAB实现IWO-Kmeans侵入性杂草优化K均值聚类算法效果一览基本介绍模型描述程序设计参考资料效果一览 基本介绍 侵入性杂草优化 (IWO) 聚类, 与 K-means 和 GMM 高斯混合模型的比较。 入侵杂草优…

jupyter notebook显示左侧内容方法(版本7也可以用)

1 安装 Nbextensions pip install jupyter_contrib_nbextensions如果安装不成功, jupyter安装Nbextensions后不出现Nbextensions选项 就使用以下语句 conda install -c conda-forge jupyter_contrib_nbextensions然后设置下 jupyter contrib nbextension insta…

kali在git外网的代理

如果发现用git无法直接连接到某些外网项目。可以配置一下代理。 vi /etc/proxychains4.conf 主机可以开一下机场代理&#xff0c;查一下主机的地址和代理所开的端口&#xff0c;我这里是7890 写上代码&#xff1a; socks5 <your ip> <your port> 写上之后wq保…

java内存管理

Java内存管理主要涉及Java虚拟机&#xff08;JVM&#xff09;对内存的分配与回收过程。这一过程确保了Java程序在运行时能够有效地使用内存资源&#xff0c;同时避免了手动管理内存所带来的复杂性和潜在错误。 内存分配&#xff1a; 堆&#xff08;Heap&#xff09;&#xff1a…