深入理解Socket套接字原理

article/2025/10/20 20:48:14

Socket套接字原理

1、什么是Socket

在计算机领域,套接字Socket作为计算机之间进行通信的固定的约定方式之一存在。这种太抽象了,我举个例子,我们要是用笔记本电脑前需要先对电脑供电,那供电就有两种方式电线插座供电和电池供电,电网有电就用插座供电,电网没电就用笔记本的自带的电池供电。那么这个供电的工具(电池或者电线插座)就是套接字Socket

Socket起源于Linux系统 ,我们都知道Linux是以文件系统进行存在。在linux里万物皆文件,就连进程都成为了一种数据结构,设备也被视为一个文件可以读写。在我看来Socket对于计算机就是实现这种模式的工具,Socket的函数就是实现对通信的操作来实现网络通信。

Socket作为中间件几乎支持所有协议使用

2、Socket如何实现网络通信

学过计网的都知道,对于计算机本地通信之间的通信是由进程实现的。

2.1、本地进程的通信方式

常见就只有几种。消息队列、管道、进程管理器、信号量等

  1. 消息队列 ,按照普通队列方式完成多进程之间的数据传递(multiprocessing模块的Queue)

  2. 管道 ,文件的一种,本质是一个固定大小的缓冲区,在Linux中缓冲区大小为4kb。

    管道工作原理:假设两个进程之间需要通讯,前面的进程对缓冲区进行文件写操作,后面的进程对文件进行读操作,就这样前面写后面读,两个进程就通过管道完成通信。

    #这里提供一个演示文本
    import datetime
    from  multiprocessing import Process,Pipe
    import timefrom charset_normalizer import constantnum=0
    def sendMsg(p):now=str(datetime.datetime.now())# p.namemsg="管道传输的数据"# name=psutil.Process(os.getpid()).name()print("这个是独立子进程 "+now+"数据是 :"+msg)p.send(msg)print("数据发送成功")p.close()def receiveMsg(p):print("接受管道传输信息:",p.recv())if __name__=='__main__':print("-----------start---------")# (grandPipe,parentPipe)=Pipe()(parentPipe,sonPipe)=Pipe()task1=Process(target=sendMsg,args=[parentPipe])task1.start()task3=Process(target=sendMsg,args=[parentPipe],name="task1")task3.start()task2=Process(target=receiveMsg,args=[sonPipe],name="task2")task2.start()time.sleep(5)print("-----------end---------")
    
  3. 远程过程调用、共享内存、同步等

2.2、网络中进程的通信方式

要理解网络进程通讯,我们要先解决两个问题

  1. 如何标定一台主机,我们需要直到需要通信的进程在哪一个主机上运行?
  2. 如何标定唯一进程,本地进程通讯可以通过pid区别,在网络上怎么区别?

TCP/IP协议族会解决第一个问题,定位通讯主机;而网络七层的传输层利用三元组(ip、端口、协议)则可以解决第二个问题

2.3、Socket实现通信的过程

现在我们已经知道网络中进程如何通信的,那么如何去实现他就是Socket的作用了。

Socket就如定义一般,是作为三元组解决网络通信中的中间件工具,就目前程序来说大部分都是采用Socket套接字中间件去实现网络中进程的通讯

Socket通信的传输方式

  1. SOCK_STREAM ,面向连接数据传输方式,数据可以准确无误达到另外一台计算机。如有丢失损坏,允许重新发送,但是效率慢。Http协议使用的就是该套接字进行传输的,毕竟浏览器要解析网页,数据就不能出现错误的地方
  2. SOCK_DGRAM , 表示面向无连接的数据传输方式,不做数据校验。如果数据在传输过程中损坏,没有办法补救,错了就是错了。我们使用qq和微信的时候使用的就是该种套接字

3、加入套接字后去理解TCP/IP

TCP协议作为一种面向连接的可靠字节流通信协议,数据在传输前要建立连接,建立连接传输后还要断开连接。用TCP建立连接目的就是保证ip、端口和物理线路正确开辟传输通道,

我们建立连接三次握手通过套接字可以抽象成如下场景

1、客户端发送建立连接请求发送SYN报文,套接字A向套接字B表示A要连接传送数据

2、服务端接受SYN报文返回客户端ACk确认报文和SYN报文,套接字B表示我已经就绪

3、客户端接受ACK报文然后向服务端发送ACK确认报文,套接字A表示感谢B的支持

对于TCP四次握手断开连接

TCP断开连接是可以通过关闭套接字来实现断开连接

1、客户端发送FIN报文,套接字B向套接字A表示我要断开连接

2、服务端接受FIn后向客户端发送ACK确认报文,套接字A向套接字B表示我知道了正在处理

3、服务端完成断开连接处理后向客户端发送Fin终止报文,套接字A向套接字B表示我已处理好断开连接操作B你可以断开了

4、客户端接受FIN后再向服务端发送ACK确认报文,套接字B向套接字A表示好的我会断开连接然后等待数秒断开连接

3.1、TCP使用Socket所导致的粘包现象

什么是粘包

多个要发送的数据包被联系存储与连续的缓存中,如果接收方对发过来的数据进行读取时没能确定发送方的发送边界(连续几个数据包为一个完整数据没法确定),而是采用估测方式假定边界值(发送方认为5个数据包为一个完整数据,而接收方认为6个包为一个完整数据),最后就会导致发送方发送的多个数据包在接收方处被视为一个数据包

为什么需要考虑粘包

我们假设发送方需要像接受方发送两条信息

hello this message、you can not use this message,如果发生粘包现象发生,就会出现hello this messageyoucan not use this message,这样接收方就不知道这个信息是什么意思,处理不了。

出现粘包的原因

出现粘包的原因发送方和接收方都有责任,

  • 发送方的粘包原因

    发送方主责是TCP协议本身所造成的,TCP作为注重传输效率的协议,发送前会收集一定量的数据才能去发送。若是连续几次的发送的数据量变少,之后的传输中TCP的优化算法会把类似这种情况的数据都合成一包数据发送出去,不会考虑后果**(毕竟TCP协议只是一个打工人只考虑效益**),这就会导致接收方收到的粘连数据

  • 接收方的粘包原因

    接收方粘包主责在于使用此连接用户进程没有即时处理数据包。接收方一般接收数据后会先将数据存往一个缓冲区,使用连接的用户进程从缓冲区读取数据。 若是进程读取速度过慢,上一包和下一包数据会一起在缓冲区存储着,如果用户进程这个时候去读,就会获得多包数据造成粘连

如何解决粘包现象

  • 如果是发送发导致粘包现象,用户可以通过编程设置避免。TCP提供push强推送指令,一旦接受就立刻发出缓冲区数据,而不是等待缓冲区充满。
  • 对于接收方导致的粘包现象,用户可以通过优化程序设计、精简接受进程的工作量提高数据发送的优先级来避免粘包
  • 或者发送时机选择由接收方控制,人为的进行多次接收数据然后合并关联数据避免粘包

以上三种解决粘包现象的方法都有缺点,要么影响程序执行性能要么影响发送效率,或者在实际业务场景中不实用。

3.2、套接字如何断开TCP连接

套接字关闭是通过程序调用函数直接将套接字描述符从内存清除,然后再也不使用该套接字。套接字关闭后,通过该套接字的连接和缓存区自然失去了连接意义,TCP协议检测到套接字消失会自动执行关闭连接的操作。

默认情况下close()/closesocket()是调用关闭套接字的函数,shutdown()则是用来关闭连接的。

  • close()/closesocket(),调用即关闭套接字即向对端发送断开连接FIN包,所以会丢失缓存区的数据。
  • shutdown(),用来关闭TCP连接,会等到缓存区没有数据包内容时执行断开连接操作。

对于程序来说无论是调用close()/closesocket()还是shutdown(),计算机都会向对端发送FIN包请求断开连接。

4、导入套接字后理解OSI七层模型

在这里插入图片描述

5、Socket套接字函数与原理(基于python3.8)

Socket套接字是建立连接的中间件,而连接成立的前提就是有两台机器存在,所以Socket的使用方式也存在两种。一般这两端是服务器端和客户端。

同时两端创建socket时需要指定三种元素,ip地址协议类型、传输方式和使用的协议,其中协议指定为非必要,不指定默认是TCP

5.1、服务器端

  • 创建socket套接字的,socket()

  • 对socket绑定端口号和地址的,bind(),端口号和地址作为一组数据传入bind

  • 表示开始监听对端通讯的,listen()

    listen只是让套接字出于监听状态,要接受请求的话还得执行accept

  • 被动接受TCP客户端连接请求的,accept()

  • 从请求中接受数据指定接受量的,recv()

  • 关闭同时停止当前socket的,close()

5.2、客户端

  • 创建socket的,socket()
  • 主动连接指定地址与端口的,connect()
  • 向套接字中写入数据进行发送到套接字进行发送的,send()、sendall()
  • 关闭本端socket使用的,close()

5.3、扩展

socket本身作为一个中间件也支持udp协议,具体流程与TCP类似主要是服务器端和客户端用于数据处理的函数被替换为recvfrom()和sendto()

除此之外还有一些返回内容

s.getpeername()返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
s.getsockname()返回套接字自己的地址。通常是一个元组(ipaddr,port)
s.setsockopt(level,optname,value)设置给定套接字选项的值。
s.getsockopt(level,optname[.buflen])返回套接字选项的值。
s.settimeout(timeout)设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
s.gettimeout()返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。
s.fileno()返回套接字的文件描述符。
s.setblocking(flag)如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。
s.makefile()创建一个与该套接字相关连的文件

6、用代码实现套接字Socket的实例

python3.8,我们可以用代码模拟socket工作流程

#socketServerTest.py 服务端代码import socketdef function():s1=socket.socket(socket.AF_INET,socket.SOCK_STREAM)s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#选择使用ipv4地址方式以及流模式传递数据,如果是ipv6则对应AF_INET6# host=socket.gethostname()# port=6999# # print(host)s1.bind(('LAPTOP-SRMA7P3L',6999))s2.bind(('LAPTOP-SRMA7P3L',6998))#这是绑定要监听的接口,我通过gethost获取到本机计算机地址s1.listen(5)#开始监听,允许使用五个连接排队s2.listen(2)while True:c1,addr1=s1.accept()#服务器端等待连接c2,addr2=s2.accept()# print(c1,addr1)while True:try:data1 = c1.recv(1024)#接受客户端数据data2 = c2.recv(1024)print('reieve ',data1.decode(),data2.decode())#打印数据c1.send(data1.upper())#发送数据c2.send(data2.upper())except ConnectionError as e:print('关闭正在展现连接')breakc1.close()c2.close()
if __name__=='__main__':function()
#socketClientTest01.py 第一个客户端代码
import socketdef function():client =socket.socket(socket.AF_INET,socket.SOCK_STREAM)client.connect(('LAPTOP-SRMA7P3L',6999))while True:msg='clinet1Msg'client.send(msg.encode('utf-8'))data=client.recv(1024)print('recv: ',data.decode())client.close()
if __name__ == '__main__':function()#socketClientTest02.py 第二个客户端代码
import socketdef function():client =socket.socket(socket.AF_INET,socket.SOCK_STREAM)client.connect(('LAPTOP-SRMA7P3L',6998))while True:msg='clinet2Msg'client.send(msg.encode('utf-8'))data=client.recv(1024)print('recv: ',data.decode())client.close()
if __name__ == '__main__':function()

都运行起来后服务端控制台会打印如下内容

reieve clinet1Msg clinet2Msg

参考文章

Socket粘包问题_blingpro的博客-CSDN博客

面向连接和无连接的套接字到底有什么区别 (biancheng.net)

什么是粘包?socket 中造成粘包的原因是什么? 粘包的处理方式_Nice07的博客-CSDN博客_什么是粘包

进程间通信的几种方式_古越少年的博客-CSDN博客_进程间的通信方式三种

一、Socket技术详解 - 简书 (jianshu.com)


http://chatgpt.dhexx.cn/article/trpLeTgI.shtml

相关文章

原始套接字

一、原始套接字概述 协议栈的原始套接字从实现上可以分为“链路层原始套接字”和“网络层原始套接字”两大类。 链路层原始套接字可以直接用于接收和发送链路层的MAC帧,在发送时需要由调用者自行构造和封装MAC首部。而网络层原始套接字可以直接用于接收和发送IP层…

socket套接字

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起探讨和分享Linux C/C/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。 socket套接字 1. 什么是socket套接字2. socket编程3. 网络字节序4. IP地址转换函数5. sockaddr数据结构…

java 套接字是什么_套接字是什么,套接字通信及其原理

为通信的端点,每个套接字由一个 IP 地址和一个端口号组成。通过网络通信的每对进程需要使用一对套接字,即每个进程各有一个。 通常,套接字采用客户机-服务器架构。服务器通过监听指定端口,来等待客户请求。服务器在收到请求后,接受来自客户套接字的连接,从而完成连接。 实…

网络编程——原始套接字实现原理

目录 1. 基础知识 1.1、概述 1.2、链路层原始套接字 1.3、网络层原始套接字 2、原始套接字的实现 2.1 原始套接字报文收发流程 2.2链路层原始套接字的实现 2.2.1 套接字创建 2.2.2 报文接收 2.2.3 报文发送 2.2.4 其它 2.3 网络层原始套接字的实现 2.3.1 套接字…

UDP套接字编程

参考:《UNIX 网络编程 卷1 : 套接字联网API》 UDP 与 TCP 之间传输存在差异,也导致编写应用程序存在很多差异。UDP 客户端和服务器不建立连接,而是直接使用 sendto 函数给服务器发送数据报。但必须指定目的地的地址作为参数。服务器也不用接…

网络套接字编程

网络套接字编程 一、 认识UDP协议 UDP(User Datagram Protocol 用户数据报协议,是不可靠的数据报传输协议,不确保数据安全有序的到达对端。 特点: 传输层协议无连接不可靠传输面向数据报 应用场景:性能要求大于安全要求&…

彻底弄懂套接字

1.什么是套接字(英文名:插座) 套接字(socket)是一种通信机制,凭借这种机制,客户/服务器系统的开发工作既可以在本地单机上进行,也可以跨网络进行。Linux所提供的功能(如打…

什么是套接字?Socket基本介绍

什么是套接字?Socket基本介绍 一、什么是套接字?二、套接字特性三、套接字缓冲区 一、什么是套接字? 套接字是一种通信机制(通信的两方的一种约定),socket屏蔽了各个协议的通信细节,提供了tcp/…

微信小程序-引入地图、获得经纬度

实际这是一个获得经纬度的方法,但是有了经纬度可以做很多事情 点击按钮跳转到一个单独的页面(地图,可导航)在页面内嵌一个独立的小区域 首先可看一下腾讯地图官方文档 微信小程序JavaScript SDK | 腾讯位置服务 最基本&#xf…

微信小程序-批量地图标记

效果图 wxml <loading hidden"{{!loading}}">加载中</loading><view class"mapBox"><map id"myMap" scale"12" longitude"{{longitude}}" latitude"{{latitude}}" markers"{{mark…

实现地图功能 利用微信内置的微信地图

效果图&#xff1a; 查看微信开发文档&#xff1a;https://developers.weixin.qq.com/miniprogram/dev/api/location/wx.chooseLocation.html 其实不难&#xff0c;查看官方文档&#xff0c;一目了然 直接上代码&#xff1a; map.wxml: <button bindtap"mapViewTap&…

微信小程序应用百度地图API

微信小程序&#xff0c;定位地点&#xff0c;应用bdmap API 1 申请 百度地图开放平台页面&#xff1a;控制台&#xff0c;添加应用 应用类型选微信小程序&#xff0c;添加微信小程序名称和APP ID 2. 微信小程序后台设置 进入微信公众平台&#xff0c;开发管理 服务器域名&a…

微信接入js-sdk-获取地理位置,打开微信内置地图

1.第一步当然是已经正确接入了微信并且配置好了回调安全域名。不会的朋友可以看看《微信开发-初级接入微信公众平台MP》 2. 引用微信js-sdk, http://res.wx.qq.com/open/js/jweixin-1.0.0.js&#xff0c;然后通过config接口注入权限验证配置。先在自己的服务器上写个获取数据…

微信地图 leaflet 腾讯地图

本来在微信项目中使用的高德地图&#xff0c;发现不是想象中的好用&#xff0c;而且用了微信&#xff0c;感觉使用腾讯地图会比较方便&#xff0c;所以&#xff0c;索性使用leaflet腾讯地图的底图来实现。 其中关于正确使用腾讯地图参考了https://github.com/wuxiashuangji/TX…

微信地图组件小程序报错“permission“

报错原因 地图可以任意添加&#xff0c;但用户的位置属于个人隐私&#xff0c;因此需要添加服务引导程序&#xff0c;告知用户地理位置的获取。需要添加 Permission &#xff08;小程序权限获取设置&#xff09;。 解决方法 该片段用于获取位置权限&#xff0c;注意该片段如…

uniapp 微信对接地图的三种操作

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 1.uni.getLocation 获取当前经维度 先上代码 let that this// 获取用户是否开启 授权获取当前的地理位置、速度的权限。uni.getSetting({success (res) {console.log(res)// 如果没有授权if (!res.au…

微信地图多边形算法及判断点位是否在多边形中

最新一个小项目,需要用到地图定义自由区域,并判断选点是否落在此区域内,思路是通过map的polygons中的points来定义多边形边界,通过polygons的fillColor 、 strokeColor、strokeWidth来进行选区颜色的渲染。 然后再通过地图中心点位的移动来确定需要判断的选点(因为小程序地…

【微信小程序】打开微信内置的地图

前言 略 打开微信内置的地图 wx.openLocation({latitude: 38.043622, /*纬度&#xff0c;使用 gcj02 国测局坐标系*/longitude: 114.514746, /*经度&#xff0c;使用 gcj02 国测局坐标系*/scale: 18, /*缩放比例&#xff0c;范围5~18*/name: 石家庄市长安公园人民广场, /*这…

小程序 – 调用微信地图功能小记 + 滑动事件

学习文献官方API对 location 地图 API的具体分析日历插件 - github 2-有回调事件小程序滚动问题小程序上下滑动事件小程序map地图上显示多个marker wx.getLocation(Object object) -获取当前的地理位置、速度。并且能打印出地址消息&#xff1b; wx.openLocation() – 是使用…

微信地图wgs84坐标,gcj02坐标,bd09坐标转换

微信小程序三种常见经纬度坐标系的转化 遇见问题&#xff1a;在其它端点位显示正常&#xff0c;在小程序上点位发生偏移&#xff0c;微信小程序是gcj02坐标&#xff0c;就是xxToGcj02&#xff0c;具体情况具体分析 我们常用的地图api坐标系有wgs84坐标系&#xff0c;gcj02坐标…