热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【原创】MySQLProxy中socketpair的使用

2019独角兽企业重金招聘Python工程师标准学习MySQLProxy0.8.3的源码后可知,其全部事件处理线程均对全局socketpair的读端进行了监听&

2019独角兽企业重金招聘Python工程师标准>>> hot3.png


      学习 MySQL Proxy 0.8.3 的源码后可知,其全部事件处理线程均对全局 socketpair 的读端进行了监听,以实现通知管道的功能:threads->event_notify_fds[0] 。

int chassis_event_threads_init_thread(chassis_event_threads_t *threads, chassis_event_thread_t *event_thread, chassis *chas) {event_thread->event_base = event_base_new();...// 设置当前线程监听 fd 为 socketpair 的读端 fdevent_thread->notify_fd = dup(threads->event_notify_fds[0]);...event_set(&(event_thread->notify_fd_event), event_thread->notify_fd, EV_READ | EV_PERSIST, chassis_event_handle, event_thread);event_base_set(event_thread->event_base, &(event_thread->notify_fd_event));event_add(&(event_thread->notify_fd_event), NULL);return 0;
}
该 socketpair 是在主线程初始化过程中创建的:

chassis_event_threads_t *chassis_event_threads_new() {...threads = g_new0(chassis_event_threads_t, 1);/* create the ping-fds** the event-thread write a byte to the ping-pipe to trigger a fd-event when* something is available in the event-async-queues*/// 创建 socketpairif (0 != evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, threads->event_notify_fds)) {...}.../* make both ends non-blocking */evutil_make_socket_nonblocking(threads->event_notify_fds[0]);evutil_make_socket_nonblocking(threads->event_notify_fds[1]);return threads;
}
其中 evutil_socketpair 实现如下(取自 libevent 1.4.13): 

int
evutil_socketpair(int family, int type, int protocol, int fd[2])
{
#ifndef WIN32return socketpair(family, type, protocol, fd);
#else/* This code is originally from Tor. Used with permission. *//* This socketpair does not work when localhost is down. So* it's really not the same thing at all. But it's close enough* for now, and really, when localhost is down sometimes, we* have other problems too.*/int listener = -1;int connector = -1;int acceptor = -1;struct sockaddr_in listen_addr;struct sockaddr_in connect_addr;int size;int saved_errno = -1;if (protocol
#ifdef AF_UNIX|| family != AF_UNIX
#endif) {EVUTIL_SET_SOCKET_ERROR(WSAEAFNOSUPPORT);return -1;}if (!fd) {EVUTIL_SET_SOCKET_ERROR(WSAEINVAL);return -1;}// 创建作为listener 的socketlistener &#61; socket(AF_INET, type, 0);if (listener <0)return -1;memset(&listen_addr, 0, sizeof(listen_addr));listen_addr.sin_family &#61; AF_INET;listen_addr.sin_addr.s_addr &#61; htonl(INADDR_LOOPBACK);listen_addr.sin_port &#61; 0; /* kernel chooses port. */// 进行绑定&#xff0c;内核会分配portif (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr)) &#61;&#61; -1)goto tidy_up_and_fail;// 宣告开始监听连接请求if (listen(listener, 1) &#61;&#61; -1)goto tidy_up_and_fail;// 创建作为connector 的socketconnector &#61; socket(AF_INET, type, 0);if (connector <0)goto tidy_up_and_fail;/* We want to find out the port number to connect to. */size &#61; sizeof(connect_addr);// 获取bind 后内核为listener 分配的port ( ip 为INADDR_LOOPBACK )if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) &#61;&#61; -1)goto tidy_up_and_fail;if (size !&#61; sizeof (connect_addr))goto abort_tidy_up_and_fail;// 从connector 向listener 发起连接&#xff0c;connect_addr 为连接目的地址if (connect(connector, (struct sockaddr *) &connect_addr, sizeof(connect_addr)) &#61;&#61; -1)goto tidy_up_and_fail;size &#61; sizeof(listen_addr);// 在套接字listener 上accept &#xff0c;函数返回后listen_addr 中为对端地址acceptor &#61; accept(listener, (struct sockaddr *) &listen_addr, &size);if (acceptor <0)goto tidy_up_and_fail;if (size !&#61; sizeof(listen_addr))goto abort_tidy_up_and_fail;// 关闭listenerEVUTIL_CLOSESOCKET(listener);/* Now check we are talking to ourself by matching port and host on thetwo sockets. */// 获取connect 后内核为connector 分配的地址信息-- 自动绑定功能if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) &#61;&#61; -1)goto tidy_up_and_fail;// 将从两侧分别获得的地址地址进行比较if (size !&#61; sizeof (connect_addr)|| listen_addr.sin_family !&#61; connect_addr.sin_family|| listen_addr.sin_addr.s_addr !&#61; connect_addr.sin_addr.s_addr|| listen_addr.sin_port !&#61; connect_addr.sin_port)goto abort_tidy_up_and_fail;fd[0] &#61; connector;fd[1] &#61; acceptor;return 0;abort_tidy_up_and_fail:saved_errno &#61; WSAECONNABORTED;tidy_up_and_fail:if (saved_errno <0)saved_errno &#61; WSAGetLastError();if (listener !&#61; -1)EVUTIL_CLOSESOCKET(listener);if (connector !&#61; -1)EVUTIL_CLOSESOCKET(connector);if (acceptor !&#61; -1)EVUTIL_CLOSESOCKET(acceptor);EVUTIL_SET_SOCKET_ERROR(saved_errno);return -1;
#endif
}
      从上述实现中可以看出&#xff0c;在非 WIN32 平台&#xff0c;直接就可以使用现成的 API 函数创建 socketpair &#xff1b;在 WIN32 平台上&#xff0c;是通过创建两个本地 socket 相互连接建立的 socketpair 。

      实现上述功能的另外一种方式是&#xff0c;使用 pipe 。用法很简单&#xff0c;摘抄代码如下&#xff08;摘自 memcached-1.4.14&#xff09;&#xff1a;

void thread_init(int nthreads, struct event_base *main_base) {
...// nthreads 为创建的工作线程数for (i &#61; 0; i ...
}
       至于用哪种更好&#xff0c;大家自己思考~~

&#61;&#61;&#61;&#61;&#61;&#61; 更新 2013-11-11 &#61;&#61;&#61;&#61;&#61;&#61;

      最近写 Modb 代码时&#xff0c;想要利用上面的线程间通信机制&#xff0c;所以使用了相对简单的 pipe 实现方案&#xff0c;但在 windows 下调试时总会遇到  “Unknown error 10038” 错误。查阅相关文档后发现&#xff0c;结论是  windows 下不能将 pipe 和 select 一起使用&#xff0c;因为会认为 pipe 不是一个合法的 socket 句柄&#xff0c;然后 linux 下是没有这个问题的。
解决方案&#xff1a;

  • 通过 socket 模拟 pipe 的实现&#xff1b;
  • 使用上面的 socketpair 实现&#xff1b;
      网上找到一份“为了 windows 上能够对 pipe 句柄进行 select” 而采用 socket 模拟 pipe 的 实现 。代码留存如下&#xff1a;

int pipe(int fildes[2])
{int tcp1, tcp2;sockaddr_in name;memset(&name, 0, sizeof(name));name.sin_family &#61; AF_INET;name.sin_addr.s_addr &#61; htonl(INADDR_LOOPBACK);int namelen &#61; sizeof(name);tcp1 &#61; tcp2 &#61; -1;int tcp &#61; socket(AF_INET, SOCK_STREAM, 0);if (tcp &#61;&#61; -1){goto clean;}if (bind(tcp, (sockaddr*)&name, namelen) &#61;&#61; -1){goto clean;}if (listen(tcp, 5) &#61;&#61; -1){goto clean;}if (getsockname(tcp, (sockaddr*)&name, &namelen) &#61;&#61; -1){goto clean;}tcp1 &#61; socket(AF_INET, SOCK_STREAM, 0);if (tcp1 &#61;&#61; -1){goto clean;}if (-1 &#61;&#61; connect(tcp1, (sockaddr*)&name, namelen)){goto clean;}tcp2 &#61; accept(tcp, (sockaddr*)&name, &namelen);if (tcp2 &#61;&#61; -1){goto clean;}if (closesocket(tcp) &#61;&#61; -1){goto clean;}fildes[0] &#61; tcp1;fildes[1] &#61; tcp2;return 0;
clean:if (tcp !&#61; -1){closesocket(tcp);}if (tcp2 !&#61; -1){closesocket(tcp2);}if (tcp1 !&#61; -1){closesocket(tcp1);}return -1;
}
原文作者指出有如下缺点&#xff1a; 

  • 效率低下&#xff08;是否所有其他实现方式都比基于 socket 的方式高效&#xff1f;&#xff09;
  • 占用了两个 TCP 端口&#xff08;pipe 不会占用端口&#xff09;
  • accept 的返回值未必就是 tcp1 连接过来的&#xff08;多线程或者别的进程在干预&#xff09;&#xff0c; 所以最好通过发送数据进行确认&#xff08;这个比较严重&#xff0c;在有多个连接同时进入的时候确实无法保证当前连接时正确的&#xff09;
  • 由于不是匿名的&#xff0c; 所以可以在 netstat 里面看到&#xff08;看到又怎样&#xff1f;&#xff09;
优点只有一个, 可以使用 select 调用。

      将该 pipe 实现和上面的 socketpair 的实现进行对比&#xff0c;发现两者根本就是同一个东东&#xff0c;并且 pipe 的实现没有 libevent 中 socketpair 实现写的好。所以 pipe 实现的作者指出的那些缺点&#xff0c;本人持保留意见。看客自己斟酌。

补充&#xff1a;由于上面的 socketpair 是基于  INADDR_LOOPBACK 的&#xff0c;所以如果 lo 必须处于 up 状态才行。


转:https://my.oschina.net/moooofly/blog/121588



推荐阅读
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • LeetCode笔记:剑指Offer 41. 数据流中的中位数(Java、堆、优先队列、知识点)
    本文介绍了LeetCode剑指Offer 41题的解题思路和代码实现,主要涉及了Java中的优先队列和堆排序的知识点。优先队列是Queue接口的实现,可以对其中的元素进行排序,采用小顶堆的方式进行排序。本文还介绍了Java中queue的offer、poll、add、remove、element、peek等方法的区别和用法。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
author-avatar
浪子得意_357
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有