MySQL 服务器监听客户端连接源码
在下面的代码有,有些结构体还不清楚,如THD,st_vio,pollfd等。但可以看出MySQL使用Select模型来接收客户端的连接。而且也在网上查清,unix_sock是指同一机器中不同进程间的通信,如命名管道。而ip_sock指的是不同主机间的通信。
void handle_connections_sockets()
{
my_socket UNINIT_VAR(sock), UNINIT_VAR(new_sock);
uint error_count=0;
THD *thd;
struct sockaddr_storage cAddr;
int ip_flags=0,socket_flags=0,flags=0,retval;
st_vio *vio_tmp;
#ifdef HAVE_POLL
int socket_count= 0;
struct pollfd fds[2]; // for ip_sock and unix_sock
#else
fd_set readFDs,clientFDs;
uint max_used_connection= (uint) (max(ip_sock,unix_sock)+1);
#endif
DBUG_ENTER("handle_connections_sockets");
#ifndef HAVE_POLL
FD_ZERO(&clientFDs);
#endif
if (ip_sock != INVALID_SOCKET)
{
#ifdef HAVE_POLL
fds[socket_count].fd= ip_sock;
fds[socket_count].events= POLLIN;
socket_count++;
#else
FD_SET(ip_sock,&clientFDs);
#endif
#ifdef HAVE_FCNTL
ip_flags = fcntl(ip_sock, F_GETFL, 0);
#endif
}
#ifdef HAVE_SYS_UN_H
#ifdef HAVE_POLL
fds[socket_count].fd= unix_sock;
fds[socket_count].events= POLLIN;
socket_count++;
#else
FD_SET(unix_sock,&clientFDs);
#endif
#ifdef HAVE_FCNTL
socket_flags=fcntl(unix_sock, F_GETFL, 0);
#endif
#endif
DBUG_PRINT("general",("Waiting for connections."));
MAYBE_BROKEN_SYSCALL;
while (!abort_loop)
{
#ifdef HAVE_POLL
retval= poll(fds, socket_count, -1);
#else
readFDs=clientFDs;
retval= select((int) max_used_connection,&readFDs,0,0,0);//等待客户端连接,反回错误或有连接到来
#endif
if (retval < 0)
{
if (socket_errno != SOCKET_EINTR)
{
if (!select_errors++ && !abort_loop) /* purecov: inspected */
sql_print_error("mysqld: Got error %d from select",socket_errno); /* purecov: inspected */
}
MAYBE_BROKEN_SYSCALL
continue;
}
if (abort_loop)
{
MAYBE_BROKEN_SYSCALL;
break;
}
/* Is this a new connection request ? */
#ifdef HAVE_POLL
for (int i= 0; i < socket_count; ++i)
{
if (fds[i].revents & POLLIN)//读取事件类型
{
sock= fds[i].fd;
#ifdef HAVE_FCNTL
flags= fcntl(sock, F_GETFL, 0);
#else
flags= 0;
#endif // HAVE_FCNTL
break;
}
}
#else // HAVE_POLL
#ifdef HAVE_SYS_UN_H
if (FD_ISSET(unix_sock,&readFDs))
{
sock = unix_sock;
flags= socket_flags;
}
else
#endif // HAVE_SYS_UN_H
{
sock = ip_sock;
flags= ip_flags;
}
#endif // HAVE_POLL
#if !defined(NO_FCNTL_NONBLOCK)
if (!(test_flags & TEST_BLOCKING))
{
#if defined(O_NONBLOCK)
fcntl(sock, F_SETFL, flags | O_NONBLOCK);//设置为非阻塞模式
#elif defined(O_NDELAY)
fcntl(sock, F_SETFL, flags | O_NDELAY);
#endif
}
#endif /* NO_FCNTL_NONBLOCK */
for (uint retry=0; retry < MAX_ACCEPT_RETRY; retry++)
{
size_socket length= sizeof(struct sockaddr_storage);
new_sock= accept(sock, (struct sockaddr *)(&cAddr),//接受连接,连接使用阻塞方式
&length);
if (new_sock != INVALID_SOCKET ||
(socket_errno != SOCKET_EINTR && socket_errno != SOCKET_EAGAIN))
break;
MAYBE_BROKEN_SYSCALL;
#if !defined(NO_FCNTL_NONBLOCK)
if (!(test_flags & TEST_BLOCKING))
{
if (retry == MAX_ACCEPT_RETRY - 1)
fcntl(sock, F_SETFL, flags); // Try without O_NONBLOCK
}
#endif
}
#if !defined(NO_FCNTL_NONBLOCK)
if (!(test_flags & TEST_BLOCKING))
fcntl(sock, F_SETFL, flags);
#endif
if (new_sock == INVALID_SOCKET)
{
if ((error_count++ & 255) == 0) // This can happen often
sql_perror("Error in accept");
MAYBE_BROKEN_SYSCALL;
if (socket_errno == SOCKET_ENFILE || socket_errno == SOCKET_EMFILE)
sleep(1); // Give other threads some time
continue;
}
#ifdef HAVE_LIBWRAP
{
if (sock == ip_sock)
{
struct request_info req;
signal(SIGCHLD, SIG_DFL);
request_init(&req, RQ_DAEMON, libwrapName, RQ_FILE, new_sock, NULL);
my_fromhost(&req);
if (!my_hosts_access(&req))//判断主机是否具有访问权限
{//如果主机没有被赋予给予权限,则拒绝连接
/*
This may be stupid but refuse() includes an exit(0)
which we surely don't want...
clean_exit() - same stupid thing ...
*/
syslog(deny_severity, "refused connect from %s",
my_eval_client(&req));
/*
C++ sucks (the gibberish in front just translates the supplied
sink function pointer in the req structure from a void (*sink)();
to a void(*sink)(int) if you omit the cast, the C++ compiler
will cry...
*/
if (req.sink)
((void (*)(int))req.sink)(req.fd);
(void) shutdown(new_sock, SHUT_RDWR);//关闭连接
(void) closesocket(new_sock);
continue;
}
}
}
#endif /* HAVE_LIBWRAP */
{
size_socket dummyLen;
struct sockaddr_storage dummy;
dummyLen = sizeof(dummy);
if ( getsockname(new_sock,(struct sockaddr *)&dummy,
(SOCKET_SIZE_TYPE *)&dummyLen) < 0 )
{
sql_perror("Error on new connection socket");
(void) shutdown(new_sock, SHUT_RDWR);
(void) closesocket(new_sock);
continue;
}
}
/*
** Don't allow too many connections
*/
if (!(thd= new THD))//为该连接分配处理的结构,在以后用于给线程使用
{
(void) shutdown(new_sock, SHUT_RDWR);
(void) closesocket(new_sock);
continue;
}
if (!(vio_tmp=vio_new(new_sock,
sock == unix_sock ? VIO_TYPE_SOCKET :
VIO_TYPE_TCPIP,
sock == unix_sock ? VIO_LOCALHOST: 0)) ||
my_net_init(&thd->net,vio_tmp))//初始化网络
{
/*
Only delete the temporary vio if we didn't already attach it to the
NET object. The destructor in THD will delete any initialized net
structure.
*/
if (vio_tmp && thd->net.vio != vio_tmp)
vio_delete(vio_tmp);
else
{
(void) shutdown(new_sock, SHUT_RDWR);
(void) closesocket(new_sock);
}
delete thd;
continue;
}
if (sock == unix_sock)
thd->security_ctx->host=(char*) my_localhost;
create_new_thread(thd);//准备为连接分配线程
}
DBUG_VOID_RETURN;
}