客户端服务器通过Socket API通信流程 通过源码角度分析 三握手四挥手都过程的状态改变 及 例如accept()connect()具体做了什么

本文主要是介绍客户端服务器通过Socket API通信流程 通过源码角度分析 三握手四挥手都过程的状态改变 及 例如accept()connect()具体做了什么,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

首先我们先说下网络编程API:

数据在网络上通信,通信的双方一个是 客户端, 一个是 服务器

更具体来说,不是 客户端和服务器这两个机器在  经由互联网   进行通信,

                       而是 客户端上的某一进程 与 服务器端的某一进程  进行通信。

因此,客户端与服务器间的通信  是  一个进程间通信,只不过通信的两个进程不在同一机器上。

所以进程间通信方式 实际上有 ①管道(命名管道、无名管道);②消息队列;③共享内存;④信号量;⑤信号;⑥套接字Socket 这6种方式

其中的 套接字Socket 通信就是指 不在同一机器上,需要经由网络进行通信的 进程间通信方式

【具体详见: XXXXX插入一条链接】--》链接内容就是socket 作为内河中的缓冲区,是如何被fd指向来接受梁金成穿来穿去的数据的


现在,客户端进程 与 服务器进程要通信,如何使用Socket套接字呢,需要以下流程:

不加任何IO复用技术的客户端与服务器之间数据的交换调用函数如下:

 使用epoll来监听的socket通信流程调用API如下:(图中只画了三次握手和传输数据,没画关闭连接阶段,,,)

 但,无论是哪种方式(无论是程序员自己写逻辑轮询监听文件描述符还是使用select\poll\epoll),客户端与服务器间建立连接【发生三次握手】的位置都是:connect()函数 与 accept()函数交互的位置,如下图所示:


首先,让我们了解下 三次握手四次挥手过程的状态转换 :

 连接建立阶段:

第一次握手:客户端的应用进程  向 服务器端 发出连接请求报文:发送连接请求后,客户端状态变为 "SYN_SENT"

请求报文内容为:其首部中:SYN值置为1 ;seq=x


第二次握手:服务器应用进程 响应 客户端的连接请求,向客户端发回 确认报文和连接请求 : 而后,服务器端的状态变为 "SYN_RECV"

请求报文内容为:其首部中:SYN值置为1,ACK值置为1 ;ack=x+1,seq=y。


第三次握手:客户端收到确认报文之后,通知上层应用进程连接已建立,并向服务器发出确认报文:而后,客户端状态变为 "ESTABLISHED"

请求报文内容为:其首部中:ACK值置为1,ack=y+1

(服务器端 收到 第三次握手客户端发来的确认报文,状态也变为 "ESTABLISHED")


至此,TCP连接就建立了,客户端和服务器可以愉快地玩耍了。只要通信双方没有一方发出连接释放的请求,连接就将一直保持。
 

 连接释放阶段:(假设客户端主动关闭连接)

第一次挥手:客户端发送一个断开连接包(FIN包):而后,客户端状态变为 "FIN_WAIT_1"

请求报文内容为:其首部中:FIN值置为1,ACK值置为1 ;ack=... ,seq=... 。

(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据)

第二次挥手:服务器端收到FIN包后,发送一个确认包给对方:而后,服务器端的状态变为 "CLOSE_WAIT"

请求报文内容为:其首部中:ACK值置为1 ;ack=... 。


第三次挥手:服务器端发送一个FIN包,告诉客户端我的数据也发送完了,不会再给你发数据了:而后,服务器端的状态变为 "LAST_ACK"

请求报文内容为:其首部中:FIN 值置为1,ACK值置为1 ;ack=...


第四次挥手:客户端 接收到 服务器端发来的FIN包,发送一个ACK给服务器端,状态转换为:"TIME_WAIT"

服务器端接收到 客户端发来的ACK包,断开与客户端的连接

客户端处于 “TIME_WAIT”状态 2MSL 时间后,也断开连接,进入"CLOSE"状态

至此,完成四次挥手




三次握手发生在 connect()函数 和  accept() 函数中,这俩函数具体干了些什么?

服务器端接收到 connect()函数实现的对服务器端的连接请求后,accept()函数与之交互:

connect()函数调用时:

由于服务器端的ip和port都已经作为地址参数传入给connect(),

因此,第一次握手时,connect()函数去封装好一个SYN包,并且在该SYN包中也写明了 seq内容、window 大小等一系列后续数据传输的参数信息,并将自己的状态置为 SYN_SEND;

第二次握手时,服务器端接收到请求:会新建一个socket (为方便称呼我们称之为 new_socket),并将客户端的ip和端口号写入该新建的socket中,而后,返回 连接请求SYN包和ACK;

第三次握手:客户端接收到应答:判断自己socket()此时状态是“SYN_SEND”,而后接受服务器端返回的ACK包,解析出ACK应答包中的通信socket的具体内容,例如window大小等,并把服务器端的ip和port写入到自己的socket中,为以后的信息传递做准备


## 一个疑问:服务器端调用accept()生成新的new_socket与客户端通信,那么,客户端访问服务器端时,它的端口号还会是客户端用于监听的socket的80端口吗?

答案是:是的,客户端在后续的数据传输中还是在访问 80端口。因为 accept 函数新创建的socket对象其实并没有进行端口的占有,而是复制了socetfd的本地IP和端口号,并且也向其中记录了连接过来的客户端的IP和端口号

## 那,多个客户端建立了多个连接请求,都在访问80端口,服务器端怎么知道那个请求时对应哪个客户端呢?

答案是:这是因为,socket不仅是一个进程间通信缓冲区,它还包含了一个用于记录控制信息的结构体,其中记录了这块缓冲区用于承担源和目的2个进程 其进程ip和端口号

因此,客户端A访问ip和80端口,服务器端对80端口的监听程序(如epoll)就会发现有数据到来,接着就会判断,这个分析这个数据包内容:

若是一个未完成三次握手连接的新客户端在发送SYN包请求连接,则,调用accept() 来新建new_socket()与之进行三次握手操作

若是一个已完成三次握手连接的客户端发送来的数据,那么就根据该数据包 则将数据放入接收缓冲区(TCP/IP协议栈 维护一个接收发送数据的缓冲区) ,当该数据包 的接受进程KK需要读数据的时候,通过调用了recv() 或read() ,进程KK根据其socket中记录的源目的ip和端口,从缓冲区中轻易找到该数据包,(并读到自己的socket空间中【这块不确定,要不别说了】)

这就是为啥服务器端 即使 就用一个端口 也 不会弄混 不同客户端发来的请求和处理的消息。

如下图所示:

服务器监听8000端口,在未建立连接时,可以看到在监听8000

在通过一个客户端建立连接后,可以看到建立了一条连接,服务器端的端口号是8000,监听的还是8000。

在连接一个客户端,可以看到建立了两条连接,服务器端都是使用8000,监听的还是8000。

这篇关于客户端服务器通过Socket API通信流程 通过源码角度分析 三握手四挥手都过程的状态改变 及 例如accept()connect()具体做了什么的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/821057

相关文章

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

服务器集群同步时间手记

1.时间服务器配置(必须root用户) (1)检查ntp是否安装 [root@node1 桌面]# rpm -qa|grep ntpntp-4.2.6p5-10.el6.centos.x86_64fontpackages-filesystem-1.41-1.1.el6.noarchntpdate-4.2.6p5-10.el6.centos.x86_64 (2)修改ntp配置文件 [r

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

hdu1565(状态压缩)

本人第一道ac的状态压缩dp,这题的数据非常水,很容易过 题意:在n*n的矩阵中选数字使得不存在任意两个数字相邻,求最大值 解题思路: 一、因为在1<<20中有很多状态是无效的,所以第一步是选择有效状态,存到cnt[]数组中 二、dp[i][j]表示到第i行的状态cnt[j]所能得到的最大值,状态转移方程dp[i][j] = max(dp[i][j],dp[i-1][k]) ,其中k满足c

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss