暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL创建连接的原理之三次握手

万照 2021-07-19
1586

该图是一个TCP连接发送的数据包

一、先搞明白几个问题

  • 为什么需要有三次握手?

客户端和服务端传送数据之前需要建立一个TCP连接,建立连接时两个端点之间一共需要交换3个数据段,通过三次握手来处理来避免连接错误。


  • 为什么会用到套接字?

mysql服务端通过系统调用socket来创建一个套接字,它是系统单独分配给mysql进程称为文件描述符的资源。凭借这种机制,客户端和服务端程序可以通过它跨网通信


  • 如何实现一个服务端套接字连接?

mysql服务使用socket函数创建一个未命名的套接字。接下来使用bind函数命名套接字,这样将特定端口号(3306)的连接转到对应的mysql服务。然后使用listen函数,创建一个队列来存放客户端连接。再使用accept函数接受处理客户端连接。最后使用read/write函数进行读写操作。


  • poll函数是做什么用的?

它是IO多路复用的一种方法。调用poll()能够判断一堆TCP连接是否来了数据,有数据的话再调用recfrom()将数据从内核复制到用户空间。


二、模拟MySQL 客户端连接server

    #include <stdlib.h>
    #include <stdio.h>
    #include "mysql.h"
    int main(int argc, char *argv[]){
    MYSQL *conn;
    unsigned int timeout = 10;


    conn = mysql_init(NULL);
    mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&timeout);
    mysql_options(conn, MYSQL_INIT_COMMAND, "SET autocommit=1");


    if (!mysql_real_connect(conn, "localhost", "wanzhao", "wanzha123", "wanzhao", 0, NULL, 0)) {
    printf("Create connection failed\n");
    printf("Connection error:%s", mysql_error(conn));
    }
    mysql_close(conn);
    return 1;
    }


    进行编译
    gcc -I/work/soft/mysql/mysqlbug/include c_connct.c -L/work/soft/mysql/mysqlbug/lib -lmysqlclient -o c_connct
    复制


    三、理解客户端和MySQL server三次握手的流程

    • 客户端和MySQL server三次握手的流程

    1、客户端调用connect()发送SYN包,与MySQL server服务器的监控套接字建立连接,客户端进入SYN_SENT状态,
    2、服务器把连接放到半连接队列中,进入SYN_RCVD状态,并发送SYN ACK包,需要注意半连接队列的长度是有限制的,操作系统参数net.ipv4.tcp_max_syn_backlog控制
    3、客户端收到SYN ACK包,进入ESTABLISHED状态,connect()处理完成,并发送ACK包
    4、服务器收到ACK包,并从半连接队列中取出数据放到全连接队列,进入ESTABLISHED状态,需要注意全连接队列是会满的,由mysql参数backlog内核参数net.core.somaxconn的最小值来确定
    5、accept()从ESTABLISHED状态全连接队列的队首处取出第一个未处理的连接返回给MySQL进程来处理

    四、MySQL server三次握手的函数栈
      // jy 初始化连接接受器
      mysqld_socket_acceptor->init_connection_acceptor()
          // jy 初始化listener,调用socket|bind函数|listen函数
         Mysqld_socket_listener::setup_listener
      // jy 接受连接开始处理
      Connection_acceptor<Mysqld_socket_listener>::connection_event_loop
          // jy 监听套接字从客户端接收连接事件,调用poll|accept函数来实现
      Mysqld_socket_listener::listen_for_connection_event
              inline_mysql_socket_accept
      复制
      复制
      4.1、mysqld_socket_acceptor连接接收器
      连接接收器是一个通用接口,先初始化和运行套接字监听器,然后使用while循环来调用listen_for_connection_event()处理客户端的连接事件。
      4.2、Mysqld_socket_listener套接字监听器
      (1)等待连接事件
      套接字监听器使用poll()阻塞,检测多个数据报套接字已经就绪(这里是ESTABLISHED状态的连接),获取连接。
      (2)接受连接
      mysql_socket_accept函数调用accpet()接受连接,用于从已完成的连接队列队头返回一个已连接的套接字描述符给MySQL服务器来处理。
      (1)和(2)的功能代码如下:
        Channel_info* Mysqld_socket_listener::listen_for_connection_event()
        {
        #ifdef HAVE_POLL
          //jy 检测多个数据报套接字已经就绪
        int retval= poll(&m_poll_info.m_fds[0], m_socket_map.size(), -1);
        #endif
        ......
        for (uint retry= 0; retry < MAX_ACCEPT_RETRY; retry++)
        {
        socket_len_t length= sizeof(struct sockaddr_storage);
            //jy inline_mysql_socket_accept调用accept函数,接受连接
        connect_sock= mysql_socket_accept(key_socket_client_connection, listen_sock,
        (struct sockaddr *)(&cAddr), &length);
        if (mysql_socket_getfd(connect_sock) != INVALID_SOCKET ||
        (socket_errno != SOCKET_EINTR && socket_errno != SOCKET_EAGAIN))
        break;
        }
        复制


        (3)MySQL实际处理新连接
        Connection_handler_manager::process_new_connection根据已连接的套接字描述符,在MySQL实例中建立一个新的线程来处理新连接。

        五、最后划重点
        1、套接字是网络通信的一种重要的方式。socket|bind|listen|accpet函数是服务端实现连接的基本操作。
        2、客户端和MySQL server三次握手的流程中,会创建2个连接队列,1个是半连接队列,1个是全连接队列,队列的长度是有限制的,如果队列满了,MySQL服务器不会发送RST
        3、客户端和MySQL服务器初始化连接的过程,包括建立套接字监听器,以及它检测连接,获取连接,接受连接的环节。

        文章转载自万照,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

        评论