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

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

1 服务端准备连接

1.1 创建套接字

要创建一个可用的套接字,需要使用下面的函数:

int socket(int domain, int type, int protocol)
  • domain
    PF_INET、PF_INET6以及PF_LOCAL等,即套接字类型
  • type
  • SOCK_STREAM
    字节流,对应TCP;
  • SOCK_DGRAM
    数据报,对应UDP;
  • SOCK_RAW
    原始套接字。
  • protocol
    原本用来指定通信协议,但现在基本废弃。因为通过前面两个参数已经指定协议。所以protocol一般写成0即可。

1.2 bind

创建的socket如果需要被使用,就需要调用bind函数把socket和socket地址绑定。

调用bind函数的方式如下:

bind(int fd, sockaddr * addr, socklen_t len)
  • sockaddr * addr
    通用地址格式,传入的参数可能是IPv4、IPv6或本地socket格式。
  • len
    传入的地址长度,bind函数会根据该字段判断传入的参数addr怎么解析。


可以把bind函数理解成这样:

bind(int fd, void * addr, socklen_t len)

不过BSD设计socket的时候大约是1982年,那时C语言还没void *语法,为解决该问题,BSD的设计者们创造性地设计了通用地址格式来作为支持bind和accept等这些函数的参数。


对于使用者,每次需将IPv4、IPv6或本地socket格式转化为通用socket格式,就像下面的IPv4 socket地址格式:


/

struct sockaddr_in name;
bind (sock, (struct sockaddr *) &name, sizeof (name)

对实现者,可根据该地址结构的前两个字节判断出是哪种地址。为处理可变长结构,需要读取函数里的len参数,即可解析和判断地址。


设置bind时,对地址和端口可以有多种处理方式。

可将地址设置成本机IP地址,等于告诉os内核,仅处理目标IP是本机IP地址的IP包。

但我们写代码时并不知道将会被部署到啥机器,通配地址解决该问题,告诉os内核只要目标地址是咱们的都可以。比如一台机器有两块网卡,IP地址分别是202.61.22.55和192.168.1.11,那么向这两个IP请求的请求包都会被我们的程序处理。

配置通配地址

  • IPv4,使用INADDR_ANY
  • IPv6,使用IN6ADDR_ANY
struct sockaddr_in name;
/* 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连接的释放。请注意,这个时候释放的只是这一个客户连接,其它被服务的客户连接可能还存在。最重要的是,监听套接字一直都处于“监听”状态,等待新的客户请求到达并服务。


目录
相关文章
|
缓存 网络协议 安全
TCP通信机制:三次握手、四次挥手、滑动窗口
TCP通信机制:三次握手、四次挥手、滑动窗口
626 1
TCP通信机制:三次握手、四次挥手、滑动窗口
|
网络协议
TCP/UDP相关-三次握手四次挥手以及为什么三次握手-如何实现可靠UDP传输
TCP/UDP相关-三次握手四次挥手以及为什么三次握手-如何实现可靠UDP传输
114 0
|
缓存 网络协议 安全
TCP三次握手四次挥手及常见问题解决方案
TCP三次握手四次挥手及常见问题解决方案
TCP三次握手四次挥手及常见问题解决方案
|
网络协议 测试技术
软件测试|TCP三次握手四次挥手
软件测试|TCP三次握手四次挥手
114 0
软件测试|TCP三次握手四次挥手
|
网络协议
TCP三次握手与四次挥手
TCP三次握手与四次挥手
131 0
|
网络协议 网络性能优化
计算机网络【UDP与TCP协议(三次握手、四次挥手)】(下)
计算机网络【UDP与TCP协议(三次握手、四次挥手)】(下)
计算机网络【UDP与TCP协议(三次握手、四次挥手)】(下)
|
缓存 网络协议 网络性能优化
计算机网络【UDP与TCP协议(三次握手、四次挥手)】(上)
计算机网络【UDP与TCP协议(三次握手、四次挥手)】(上)
计算机网络【UDP与TCP协议(三次握手、四次挥手)】(上)
|
网络协议 安全 Linux
《我要进大厂》- 计算机网络夺命连环23问,你能坚持到第几问?(TCP 三次握手、四次挥手
《我要进大厂》- 计算机网络夺命连环23问,你能坚持到第几问?(TCP 三次握手、四次挥手
《我要进大厂》- 计算机网络夺命连环23问,你能坚持到第几问?(TCP 三次握手、四次挥手
|
存储 网络协议 算法
《我要进大厂》- 计算机网络夺命连环20问,你能坚持到第几问?(应用层协议 | TCP三次握手、四次挥手 | TCP可靠传输 | Cookie&Session)(下)
《我要进大厂》- 计算机网络夺命连环20问,你能坚持到第几问?(应用层协议 | TCP三次握手、四次挥手 | TCP可靠传输 | Cookie&Session)
《我要进大厂》- 计算机网络夺命连环20问,你能坚持到第几问?(应用层协议 | TCP三次握手、四次挥手 | TCP可靠传输 | Cookie&Session)(下)
|
网络协议 安全 机器人
《我要进大厂》- 计算机网络夺命连环20问,你能坚持到第几问?(应用层协议 | TCP三次握手、四次挥手 | TCP可靠传输 | Cookie&Session)(上)
《我要进大厂》- 计算机网络夺命连环20问,你能坚持到第几问?(应用层协议 | TCP三次握手、四次挥手 | TCP可靠传输 | Cookie&Session)
《我要进大厂》- 计算机网络夺命连环20问,你能坚持到第几问?(应用层协议 | TCP三次握手、四次挥手 | TCP可靠传输 | Cookie&Session)(上)