从TCP到Socket,彻底理解网络编程是怎么回事

进行程序开发的同学,无论Web前端开发、Web后端开发,还是搜索引擎和大数据,几乎所有的开发领域都会涉及到网络编程。比如我们进行Web服务端开发,除了Web协议本身依赖网络外,通常还需要连接数据库,而数据库连接通常是通过网络连接数据库服务器,或者数据库集群,如果负载太高还要搞个缓存集群。

我们在上学的时候基本学了网络编程和网络协议。但两者之间的具体关系可能有些摸不到头脑。这里我们首先重点介绍2个概念,一个概念是网络编程,另外一个是协议。

我们知道网络协议是一个分层的协议族,也就是是有一组协议构成,从下往上各自负责各自的功能。那什么是协议呢?协议的字面意思是共同计议,商议。简单的理解其实就是多方进行沟通的规定。而网络协议其实就是在网络中多个计算节点进行交互、沟通的规定。如果根我们日常生活对比的话,协议可以理解为语言,比如汉语普通话。两个人交流如果都用不通话,那么彼此都能理解对方表达的意图。例如,一个人用四川话,而另外一个用浙江话,那沟通起来估计几乎不太可能。网络协议也是一样的,通过对数据格式的规范化,从而使计算机之间能够彼此明确对方的意图。

下面本文介绍一下网络编程,网络编程也称为socket编程,socket通常译作“套接字”,但原意其实意译应该为”接口“。也就是操作系统提供给开发人员进行网络开发的API接口。这套接口通常可以参数的调整支持多种协议,包括TCP、UDP和IP等等。下面本文从套接字编程和协议两方面分别详细的进行介绍。

网络编程

为了便于理解,本文先从具体的内容开始,也就是通过一个实例介绍一下网络编程是怎么回事。

本文将以TCP协议为例介绍网络编程和协议之前的关系。为了简单,便于理解,本文以Python为例进行介绍,如果不了解Python编程语言关系也不大,下面代码很容易理解。我们知道在网络通信中无论是BS架构还是CS架构,通常分为服务端和客户端,只不过BS架构中的浏览器就是客户端。因此,本文的示例也包含服务端和客户端2部分的代码。代码功能很简单,就是实现客户端和服务端发送字符串。0db49deb20cf4f3b8d718bd7fd0bc425.png

 这个代码清单是服务端的代码,这段代码的作用就是在服务端的某个端口建立监听,并等待客户端建立连接。完成连接建立后,等待客户端发送数据,并将数据回传给客户端。

#!/usr/bin/env python3
#-*- coding:utf-8 -*-
from socket import *
from time import ctime
host = ''
port = 12345
buffsize = 2048
ADDR = (host,port)
# 创建一个基于TCP协议的套接字
tctime = socket(AF_INET,SOCK_STREAM)
tctime.bind(ADDR)
# 在指定的地址和端口监听
tctime.listen(3)
while True:print('Wait for connection ...')tctimeClient,addr = tctime.accept()print("connection from :",addr)while True:data = tctimeClient.recv(buffsize).decode()if not data:breaktctimeClient.send(('[%s] %s' % (ctime(),data)).encode())tctimeClient.close()
tctimeClient.close()

阅读服务端的代码可以看出主要包括,socket、bind、listen、accept、recv和send几个。其中值得关注的是listen和accept,两者分别用于监听端口和接受客户端的连接请求。

下面代码清单是客户端的实现,这里特别的地方是有一个connect函数,该函数实现与服务端建立连接。

#!/usr/bin/env python3
#-*- coding:utf-8 -*-
from socket import *
HOST ='localhost'
PORT = 12345
BUFFSIZE=2048
ADDR = (HOST,PORT)
tctimeClient = socket(AF_INET,SOCK_STREAM)
tctimeClient.connect(ADDR)
while True:data = input(">")if not data:breaktctimeClient.send(data.encode())data = tctimeClient.recv(BUFFSIZE).decode()if not data:breakprint(data)
tctimeClient.close()

通过上述示例代码可以看出服务端通常是被动的,而客户端则要主动一些。服务端程序建立对某个端口的监听,等待客户端的连接请求。客户端向服务端发送连接请求,不出意外的情况下连接建立成功,这时客户端和服务端之前就可以互发数据了。当然,在实际生产环境中意外是经常的,因此从协议和接口层面,需要处理各种意外,本文在协议部分将详细介绍。

另外,本文实现了一个基本的客户端和服务端通信的程序,这个模式的通信在实际生产中几乎不再使用。在实际生产中为了提高数据传输和处理的效率,通常采用异步模式,这些内容超出了本文的介绍范围,后续文章会逐渐介绍。

TCP协议详解

前文说了网络协议是网络中不同计算机信息通信的语言,为了实现交互,这个语言就需要有一定的格式。本文以TCP协议为例进行介绍。

TCP协议是一个可靠的传输协议,其可靠性表现在2方面,一方面是保证数据包可以按照发送的顺序到达,另外一方面是保证数据包一定程度的正确性(后文详解为什么是一定程度上的正确性)。其可靠性的实现则基于2点技术,一点是具有一个CRC校验,这样如果数据包中的某些数据出现错误可以通过该校验和发现;另外一点是每个数据包都有一个序号,这样就能保证数据包的顺序性,如果出现错位的数据包可以请求重发。

既然说到了格式,那我们先看一下TCP数据包的数据格式。如下图是TCP数据包的格式,包括原端口、目的端口、序列号和标识位等等内容,内容有些多,看着可能有点眼花。但从大的方面理解,这个数据包其实只包含2部分内容,一个是包头,另外一个则是具体需要传输的数据。在TCP协议的控制逻辑中,包头起着最为关键的作用,它是TCP协议中诸如建立连接、断开连接、重传和错误校验等各种特性的基础。632e5735aa674455b3de4b84f3031c9b.png

 

包头的其它信息的含义都比较明了,本文仅仅介绍几个标志位(URG、ACK、PSH、RST、SYN和FIN)的含义。具体含义如下:

  • ACK: 确认序号有效。
  • RST:重置连接
  • SYN:发起一个新连接
  • FIN:释放一个连接

连接的建立 

TCP在具体传输数据之前需要建立连接。这里的连接并不是物理连接,物理连接基于底层的协议已经建立完成,而且TCP建立连接也是要假设底层连接已经成功,TCP的连接其实是一个虚拟的,逻辑的连接。简单粗暴的理解,就是客户端和服务端分别记录了各自接受到的数据包的序号,并且将自身设置为某种状态。在TCP协议中,连接的建立通常成为3次握手,从字面的概念可以看出,连接的建立需要经过3次确认的过程。5d553d4e723b4b7f99c7d1036301e511.png

 

TCP协议3次握手的过程如图所示,初始状态客户端和服务端都处于关闭状态。主要过程分为3步:

  1. 客户端发送预连接数据包: TCP的连接是由客户端主动发起建立,客户端会发送一个数据包(报文)给服务端,需要注意的是数据包中的SYN标识位为1。我们前文已经介绍,如果SYN为1,则说明为建立连接的数据包。同时,在该数据包中包含一个请求序列号,该序列号也是建立连接的依据。
  2. 服务端回复连接确认: 服务端确认可以建立连接(服务端不一定可以建立连接,因为系统中套接字的数量是有限的)的情况下会向客户端发送一个应答数据包。在应答数据包中会将ACK标志位设置为1,表示为服务端应答数据包。同时,在应答数据包中会设置请求序列号和应答序列号的值,具体参考图3.
  3. 客户端回复连接确认: 最后,客户端再次发送一个连接确认数据包,告诉服务端连接建立成功。

从上面流程可以看出,连接的建立需要经过多次交互,这就是我们日常中所说的建立连接是高成本的操作。在实际生产环境中,为了应对这个问题,会减少连接建立的频度,通常的做法是建立连接池,传输数据时直接从连接池中获取连接,而不是新建连接。

有人可能觉得可以对建立连接的过程进行优化,比如将客户端最后一次的确认取消掉,觉得这个没有卵用。对于正常情况确实没有多大的作用,这里主要是应对异常情况。因为网络拓扑是非常复杂的,特别是在广域网中,有着数不清的网络节点,因此会出现各种异常情况。因此,TCP协议在设计的时候必须要保证异常情况下的可靠性。

我们这里举一个例子,就是连接请求超时的情况。假设客户端向服务端发送一个连接请求,由于各种原因,请求一直没有到达服务端,因此服务端也就没有回复连接确认消息。客户端连接超时,因此客户端重新发送一个连接请求到服务端,这次比较顺利,很快到达了,并且顺利建立了连接。之后,前一个数据包经过长途跋涉最终还是到了服务端,服务端也向客户端发送了回复数据包,服务端认为连接是建立成功的,并且会维持连接。但客户端层面认为连接是超时的,因此将永远不会关闭该连接。这样就会造成服务端有残留的资源,从而造成服务端资源浪费,久而久之可能会导致服务端无新连接资源可用。

另外一个需要说明的是客户端和服务端的套接字都有相应的状态,而且状态会随着连接的不同阶段变化。初始状态都是CLOSE,最终连接建立成功后都是ESTABLISHED,具体变化过程如图3所示。后面本文会详细介绍状态变化情况。

传输数据 

完成连接建立之后,客户端和服务端就可以进行数据传输了。我们知道TCP是可靠的传输,那么传输的可靠性是通过什么来保证的呢?主要就是通过包头中的校验和、请求序列号和应答序列号(参考图2)。

TCP数据内容的可靠性是通过校验和保证的。TCP在发送数据时都会计算整个数据包的校验和,并存储在包头的校验和字段中。接收方会按照规则进行计算,从而确认接收到的数据是否是正确的。发送发计算校验和的流程大概如下:

  1. 把伪首部、TCP包头和TCP数据分为16为的字,并把TCP包头中的校验和字段置0
  2. 用反码加法累加所有16位数字
  3. 对计算结果去反,将其填充到TCP包头的校验和字段

接收方将所有原码相加,高位叠加,如果全为1则表示数据正确,否则说明数据有错误。

TCP数据包顺序的可靠性是通过请求序列号和应答序列号保证的。在数据传输中的每个请求都会有一个请求序列号,而在接收方接收到数据后会发送一个应答序列号,这样发送方就能知道数据是否被正确接收,而接收方也能知道数据是否出现乱序,从而保证数据包的顺序性。

断开连接 

TCP关闭连接分为4步,称为4次挥手。连接的关闭不一定是在客户端发起,服务端也可以发起关闭连接。关闭连接的过程如下:

  1. 发起方发送一个FIN置位的数据包,用来请求关闭发送方到接收方的连接
  2. 接收方发送一个应答,ACK标志位为1,确认关闭。此时完成了发起方到接收方的连接,也即发送方无法再向接收方发送数据,但接收方还可以向发送方发送数据。
  3. 接收方数据传输完成后向发起方发送一个FIN为1的包,表示请求断开连接
  4. 发起方回复一个ACK包,确认关闭成功


d8bde4bf38db403ea3dfbcd46c9eaf52.png

 

TCP是全双工通信,因此关闭连接时需要双向关闭连接。首先是关闭发起方关闭本端的连接,然后是关闭接收方在收到发起方的关闭请求后,除了回复关闭应答外,还要确保数据传输完成后发起一个关闭连接的请求,保证双向同时关闭。

截止到这里,本文介绍了基于TCP协议进行网络编程的主要内容。当然这个只是入门级的,如果需要真正理解TCP协议和网络编程还需要学习很多内容。后续本号将陆续介绍给大家。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/185139.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

易点易动固定资产管理系统:定制流程与用量控制的高效管理利器

固定资产管理对于企业来说至关重要,而如何提高固定资产管理的效率和精确度一直是企业管理者关注的焦点。易点易动固定资产管理系统以其自定义固定资产流程和用量控制功能,成为了提升固定资产管理效率的利器。本文将详细介绍易点易动固定资产管理系统的自…

功能: 在web应用程序中、读取文件

通过使用文件 API&#xff0c;web 内容可以要求用户选择本地文件&#xff0c;然后读取这些文件的内容。这种选择可以通过使用 HTML <input type"file"> 元素或通过拖放来完成。 1.通过 click() 方法使用隐藏的文件 input 元素 你可以隐藏公认难看的文件 <…

Android MotionLayout

MotionLayout exends ConstraintLayout(动画框架 过渡) View动画 API1 属性动画API11 过渡动画API18 root.width RootViewWidth TransitionManager.beginDelayedTransition(view) 过渡动画 可以改变其大小和流畅性 Fade 可以改变透明度 通过TrasitinManager管理 Go:动态替…

Android Gldie复用只取之前decode过的缓存resource,Kotlin

Android Gldie复用只取之前decode过的缓存resource&#xff0c;Kotlin import android.graphics.Bitmap import android.os.Bundle import android.util.Log import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.life…

小程序开发平台源码系统 各种类型小程序任由选择 带完整的搭建教程

近年来随着微信、支付宝等平台对小程序的大力推广和支持下应运而生的。小程序作为一种轻量级的应用程序&#xff0c;为用户提供了无需下载安装、即用即走的便捷体验&#xff0c;因此得到了广大用户的喜爱和青睐。所以今天罗峰来给大家介绍一款小程序开发平台源码系统&#xff0…

【NeurIPS 2020】基于蒙特卡罗树搜索的黑箱优化学习搜索空间划分

Learning Search Space Partition for Black-box Optimization using Monte Carlo Tree Search 目标&#xff1a;从采样&#xff08;Dt ∩ ΩA&#xff09;中学习一个边界&#xff0c;从而最大化两方的差异 先使用Kmeans在特征向量上&#xff08; [x, f(x)] &#xff09;聚类…

智能网联汽车有哪些信息安全场景

目录 1.车内安全通信 2.车云安全通信 3.安全启动 4.车载应用程序保护 5.入侵检测防御与日志管理系统 在聊完车载信息安全需求之后&#xff0c;势必要去看看​应用场景有哪些。根据之前的开发经验简单聊一下我知道的&#xff0c;还有很多没有讲&#xff0c;比如说车云之间具…

单月突破20万台大关!哪些供应商正在领跑W/AR HUD前装市场

传统汽车升级&#xff0c;从电动化到智能化&#xff0c;驱动更多的增量部件进入前装市场。这些产品通常都会经历几个关键的时间节点。 其中&#xff0c;前装搭载率10%&#xff0c;被视为细分赛道处在快速成长期的关键指标之一。并且&#xff0c;这意味着&#xff0c;产品已经得…

面试题:说一下线程、线程锁与线程池

文章目录 前言一、线程1.线程概念2.线程与进程的关系3.定义4.wait()和sleep()5.线程的状态及其他API 二、线程锁1. 普通锁机制2. Lock 三、线程同步工具类1. CountDowmLatch闭锁&#xff1a;2. CyclicBarrier栅栏&#xff1a;3. Exchanger交换机&#xff1a;4. 信号量 四、线程…

FreeRTOS源码阅读笔记2--list.c

list.c中主要完成列表数据结构的操作&#xff0c;有列表和列表项的初始化、列表的插入和移除。 2.1列表初始化vListInitialise() 2.1.1函数原型 void vListInitialise( List_t * const pxList ) pxList&#xff1a;列表指针&#xff0c;指向要初始化的列表。 2.1.2函数框架…

ARMday03(寄存器读写、栈、程序状态寄存器、软中断和异常、混合编程)

单寄存器内存读写指令 将一个寄存器中的数值写入到内存&#xff0c;或者从内存中读取数据放在某一个指定寄存器中 指令码和功能 1.向内存中写&#xff1a; str{条件码} 目标寄存器,[目标地址]&#xff1a;将目标寄存器的4字节数值写入到目标地址为首地址的空间中 strh{条件码…

跟着森老师学React Hooks(1)——使用Vite构建React项目

Vite是一款构建工具&#xff0c;对ts有很好的支持&#xff0c;最近也是在前端越来越流行。 以往的React项目的初始化方式大多是通过脚手架create-react-app(本质是webpack)&#xff0c;其实比起Vite来构建&#xff0c;启动会慢一些。 所以这次跟着B站的一个教程&#xff0c;使用…

JavaScript脚本操作CSS

脚本化CSS就是使用JavaScript脚本操作CSS&#xff0c;配合HTML5、Ajax、jQuery等技术&#xff0c;可以设计出细腻、逼真的页面特效和交互行为&#xff0c;提升用户体验&#xff0c;如网页对象的显示/隐藏、定位、变形、运动等动态样式。 1、CSS脚本化基础 CSS样式有两种形式&…

【Ruoyi管理后台】用户登录强制修改密码

近期有个需求&#xff0c;就是需要调整Ruoyi管理后台&#xff1a;用户如果三个月(长时间)未修改过密码&#xff0c;需要在登录时强制修改密码&#xff0c;否则不能登录系统。 一、后端项目调整 从需求来看&#xff0c;我们需要在用户表增加一个字段&#xff0c;用于标记用户最…

Ansible优化大全

文章目录 一、关闭系统信息收集二、开启加速 Ansible 执行速度修改配置文件/etc/ansible/ansible.cfg由于该功能与sudo冲突&#xff0c;必须关闭 requiretty 选项方法一方法二 参考文章&#xff1a; https://blog.csdn.net/o0o0o0D/article/details/110998873 一、关闭系统信息…

【教3妹学编程-算法题】逃离火灾

3妹&#xff1a;2哥&#xff0c;今日都立冬了&#xff0c; 可是天气一点都不冷。 2哥 : 立冬了&#xff0c;晚上要不要一起出去吃饺子&#xff1f;&#x1f95f; 3妹&#xff1a;好呀好呀&#xff0c;2哥请吃饺子喽 2哥 : 歪歪&#xff0c;我说的是一起出去吃&#xff0c;没说我…

Python|OpenCV-图像的添加和混合操作(8)

前言 本文是该专栏的第8篇,后面将持续分享OpenCV计算机视觉的干货知识,记得关注。 在使用OpenCV库对图像操作的时候,有时需要对图像进行运算操作,类似于加法,减法,位操作等处理。而本文,笔者将针对OpenCV对图像的添加,混合以及位操作进行详细的介绍说明和使用。 下面,…

【慢SQL性能优化】 一条SQL的生命周期 | 京东物流技术团队

一、 一条简单SQL在MySQL执行过程 一张简单的图说明下&#xff0c;MySQL架构有哪些组件和组建间关系&#xff0c;接下来给大家用SQL语句分析 例如如下SQL语句 SELECT department_id FROM employee WHERE name Lucy AND age > 18 GROUP BY department_id其中name为索引&a…

Python算法例9 罗马数字转换为整数

1. 问题描述 给定一个罗马数字&#xff0c;将其转换为整数&#xff0c;要求返回结果的取值为1~3999。 2. 问题示例 Ⅳ→4&#xff0c;Ⅻ→12&#xff0c;ⅩⅪ→21&#xff0c;XCVI→99。 3. 代码实现 def roman_to_int(s):roman_map {I: 1, V: 5, X: 10, L: 50, C: 100, …

Spring Boot中使用Spring Data JPA访问MySQL

Spring Data JPA是Spring框架提供的用于简化JPA&#xff08;Java Persistence API&#xff09;开发的数据访问层框架。它通过提供一组便捷的API和工具&#xff0c;简化了对JPA数据访问的操作&#xff0c;同时也提供了一些额外的功能&#xff0c;比如动态查询、分页、排序等。 …