一文讲透TCP三次握手到底怎么实现的

本文详细介绍了IPv4TCP套接字的创建、绑定、监听和连接过程,重点阐述了服务器端如何通过bind和listen等待客户端连接,以及客户端如何通过connect发起连接,特别强调了TCP三次握手的工作原理。
摘要由CSDN通过智能技术生成

/* IPV4通配地址 */

name.sin_addr.s_addr = htonl (INADDR_ANY);

端口

=================================================================

如果把端口设置成0,就相当于把端口的选择权交给内核,内核会根据一定的算法选择一个空闲的端口,完成套接字的绑定。这在服务器端不常使用。

一般来说,服务器端的程序一定要绑定到一个众所周知的端口上。服务器端的IP地址和端口数据,相当于打电话拨号时需要知道的对方号码,如果没有电话号码,就没有办法和对方建立连接。

我们来看一个初始化IPv4 TCP 套接字的例子:

#include <stdio.h>

#include <stdlib.h>

#include <sys/socket.h>

#include <netinet/in.h>

int make_socket (uint16_t port)

{

int sock;

struct sockaddr_in name;

/* 创建字节流类型的IPV4 socket. */

sock = socket (PF_INET, SOCK_STREAM, 0);

if (sock < 0)

{

perror (“socket”);

exit (EXIT_FAILURE);

}

/* 绑定到port和ip. */

name.sin_family = AF_INET; /* IPV4 */

name.sin_port = htons (port); /* 指定端口 */

name.sin_addr.s_addr = htonl (INADDR_ANY); /* 通配地址 */

/* 把IPV4地址转换成通用地址格式,同时传递长度 */

if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)

{

perror (“bind”);

exit (EXIT_FAILURE);

}

return sock

}

listen:接上电话线,一切准备就绪。

bind函数只是让socket和地址关联。如果要让别人打通电话,还需要我们把电话设备接入电话线,让服务器真正处于可接听状态,这就需要listen函数。

初始化创建的socket,之后会主动发起请求(通过调用connect函数)。通过listen函数,可以将原来的"主动"socket转换为"被动"socket,告诉操作系统内核:“我这个socket是用来等待用户请求的。”当然,操作系统内核会为此做好接收用户请求的一切准备,比如完成连接队列。

listen函数原型:

int listen (int socketfd, int backlog)

  • socketfd

socket描述符

  • backlog

未完成连接队列的大小,即可以接收的并发数目。越大,并发数理论上越大。但参数过大也会占用过多系统资源,所以Linux并不允许对这个参数进行改变。

accept

=====================================================================

当客户端的连接请求到达时,服务器端应答成功,连接建立,这时内核需把该事件通知到应用程序,让应用程序感知到这个连接。

accept这个函数的作用就是连接建立之后,内核和应用程序之间的桥梁。它的原型是:

int accept(int listensockfd, struct sockaddr *cliaddr, socklen_t *addrlen)

  • listensockfd

套接字,通过bind,listen一系列操作而得到的套接字。返回值有两部分,cliadd是通过指针方式获取的客户端的地址,addrlen地址的大小

函数的返回值,代表与客户端的连接。

两个socket描述字:

  • 输入参数,监听socket描述字listensockfd

  • 返回的已连接socket描述字

为什么要把两个套接字分开呢?

网络程序需要并发处理,不可能一个应用程序运行后只能服务一个客户。

所以监听socket一直都存在,服务成千上万的客户,直到这个监听socket关闭。一旦一个客户和服务器连接成功,完成了TCP三次握手,操作系统内核就为这个客户生成一个已连接套接字,让应用服务器使用这个已连接套接字和客户进行通信处理。如果应用服务器完成了对这个客户的服务,比如一次网购下单,一次付款成功,那么关闭的就是已连接套接字,这样就完成了TCP连接的释放。请注意,这个时候释放的只是这一个客户连接,其它被服务的客户连接可能还存在。最重要的是,监听套接字一直都处于“监听”状态,等待新的客户请求到达并服务。

客户端发起连接的过程

=========================================================================

第一步建立一个套接字,不一样的是客户端需要调用connect发起请求。

connect

======================================================================

客户端和服务器端的连接建立,是通过connect函数完成的。这是connect的构建函数:

int connect(int sockfd,

const struct sockaddr *servaddr,

socklen_t addrlen)

  • sockfd

连接套接字,通过前面讲述的socket函数创建

  • servaddr、addrlen

指向套接字地址结构的指针和该结构的大小。

套接字地址结构必须含有服务器的IP地址和端口号。

客户在调用函数connect前不必非得调用bind函数,如果需要,内核会确定源IP地址,并选择一个临时端口作为源端口。

如果是TCP套接字,那么调用connect函数将激发TCP的三次握手过程,而且仅在连接建立成功或出错时才返回。其中出错返回可能有以下几种情况:

三次握手无法建立,客户端发出的SYN包没有任何响应,于是返回TIMEOUT错误。这种情况比较常见的原因是对应的服务端IP写错。

客户端收到了RST(复位)回答,这时候客户端会立即返回CONNECTION REFUSED错误。这种情况比较常见于客户端发送连接请求时的请求端口写错,因为RST是TCP在发生错误时发送的一种TCP分节。产生RST的三个条件是:目的地为某端口的SYN到达,然而该端口上没有正在监听的服务器(如前所述);TCP想取消一个已有连接;TCP接收到一个根本不存在的连接上的分节。

客户发出的SYN包在网络上引起了"destination unreachable",即目的不可达的错误。这种情况比较常见的原因是客户端和服务器端路由不通。

根据不同的返回值,我们可以做进一步的排查。

著名的TCP三次握手: 这一次不用背记

你在各个场合都会了解到著名的TCP三次握手,可能还会被要求背下三次握手整个过程,但背后的原理和过程可能未必真正理解。我们刚刚学习了服务端和客户端连接的主要函数,下面结合这些函数讲解一下TCP三次握手的过程。这样我相信你不用背,也能根据理解轻松掌握这部分的知识。

这里我们使用的网络编程模型都是阻塞式的。所谓阻塞式,就是调用发起后不会直接返回,由操作系统内核处理之后才会返回。 相对的,还有一种叫做非阻塞式的,我们在后面的章节里会讲到。

TCP三次握手

======================================================================

服务器端通过socket,bind和listen完成了被动套接字的准备工作,被动的意思就是等着别人来连接,然后调用accept,就会阻塞在这里,等待客户端的连接来临;客户端通过调用socket和connect函数之后,也会阻塞。接下来的事情是由操作系统内核完成的,更具体一点的说,是操作系统内核网络协议栈在工作。

客户端的协议栈向服务器端发送了SYN包,并告诉服务器端当前发送序列号j,客户端进入SYNC_SENT状态;

服务器端的协议栈收到这个包之后,和客户端进行ACK应答,应答的值为j+1,表示对SYN包j的确认,同时服务器也发送一个SYN包,告诉客户端当前我的发送序列号为k,服务器端进入SYNC_RCVD状态;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

分享

首先分享一份学习大纲,内容较多,涵盖了互联网行业所有的流行以及核心技术,以截图形式分享:

(亿级流量性能调优实战+一线大厂分布式实战+架构师筑基必备技能+设计思想开源框架解读+性能直线提升架构技术+高效存储让项目性能起飞+分布式扩展到微服务架构…实在是太多了)

其次分享一些技术知识,以截图形式分享一部分:

Tomcat架构解析:

算法训练+高分宝典:

Spring Cloud+Docker微服务实战:

最后分享一波面试资料:

切莫死记硬背,小心面试官直接让你出门右拐

1000道互联网Java面试题:

Java高级架构面试知识整理:

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
er微服务实战:

[外链图片转存中…(img-xXccVqb3-1712266106441)]

最后分享一波面试资料:

切莫死记硬背,小心面试官直接让你出门右拐

1000道互联网Java面试题:

[外链图片转存中…(img-O87JrMcE-1712266106441)]

Java高级架构面试知识整理:

[外链图片转存中…(img-MvAwKyF3-1712266106442)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值