libevent源码深度剖析10

libevent源码深度剖析10 支持I/O多路复用技术 libevent的核心是事件驱动、同步非阻塞,为了达到这一目标,必须采用系统提供的I/O多路复用技术,而这些在Windows、Linux、Unix等不同平台上却各有不同,如何能提供优雅而统一的支持方式,是首要关键的问题,这其实不难,本节就来分析一下。 1. 统一的关键 libevent支持多种I/O多路复用技术的关键就在于结构体eventop,这个结构体前面也曾提到过,它的成员是一系列的函数指针, 定义在event-internal.h文件中: struct eventop { const char *name; void *(*init)(struct event_base *); // 初始化 int (*add)(void *, struct event *); // 注册事件 int (*del)(void *, struct event *); // 删除事件 int (*dispatch)(struct event_base *, void *, struct timeval *); // 事件分发 void (*dealloc)(struct event_base *, void *); // 注销,释放资源 /* set if we need to reinitialize the event base */ int need_reinit; }; 在libevent中,每种I/O demultiplex机制的实现都必须提供这五个函数接口,来完成自身的初始化、销毁释放;对事件的注册、注销和分发。 比如对于epoll,libevent实现了5个对应的接口函数,并在初始化时并将eventop的5个函数指针指向这5个函数,那么程序就可以使用epoll作为I/O demultiplex机制了。 2. 设置I/O demultiplex机制 libevent把所有支持的I/O demultiplex机制存储在一个全局静态数组eventops中,并在初始化时选择使用何种机制,数组内容根据优先级顺序声明如下: /* In order of preference */ static const struct eventop *eventops[] = { #ifdef HAVE_EVENT_PORTS &evportops, #endif #ifdef HAVE_WORKING_KQUEUE &kqops, #endif #ifdef HAVE_EPOLL &epollops, #endif #ifdef HAVE_DEVPOLL &devpollops, #endif #ifdef HAVE_POLL &pollops, #endif #ifdef HAVE_SELECT &selectops, #endif #ifdef WIN32 &win32ops, #endif NULL }; 然后libevent根据系统配置和编译选项决定使用哪一种I/O demultiplex机制,这段代码在函数**event_base_new()**中: ...

2021-01-11

libevent源码深度剖析11

libevent源码深度剖析11 时间管理 为了支持定时器,libevent必须和系统时间打交道,这一部分的内容也比较简单,主要涉及到时间的加减辅助函数、时间缓存、时间校正和定时器堆的时间值调整等。下面就结合源代码来分析一下。 1. 初始化检测 libevent在初始化时会检测系统时间的类型,通过调用函数**d****etect_monotonic()完成,它通过调用clock_gettime()**来检测系统是否支持monotonic时钟类型: static void detect_monotonic(void){ #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) use_monotonic = 1; // 系统支持monotonic时间 #endif } Monotonic时间指示的是系统从boot后到现在所经过的时间,如果系统支持Monotonic时间就将全局变量use_monotonic设置为1,设置use_monotonic到底有什么用,这个在后面说到时间校正时就能看出来了。 2. 时间缓存 结构体event_base中的tv_cache,用来记录时间缓存。这个还要从函数**gettime()**说起,先来看看该函数的代码: static int gettime(struct event_base *base, struct timeval *tp){ // 如果tv_cache时间缓存已设置,就直接使用 if (base->tv_cache.tv_sec) { *tp = base->tv_cache; return (0); } // 如果支持monotonic,就用clock_gettime获取monotonic时间 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) if (use_monotonic) { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) return (-1); tp->tv_sec = ts.tv_sec; tp->tv_usec = ts.tv_nsec / 1000; return (0); } #endif // 否则只能取得系统当前时间 return (evutil_gettimeofday(tp, NULL)); } 如果tv_cache已经设置,那么就直接使用缓存的时间;否则需要再次执行系统调用获取系统时间。 函数**evutil_gettimeofday()用来获取当前系统时间,在Linux下其实就是系统调用gettimeofday();Windows没有提供函数gettimeofday,而是通过调用_ftime()**来完成的。 在每次系统事件循环中,时间缓存tv_cache将会被相应的清空和设置,再次来看看下面event_base_loop的主要代码逻辑: int event_base_loop(struct event_base *base, int flags){ // 清空时间缓存 base->tv_cache.tv_sec = 0; while(!done){ timeout_correct(base, &tv); // 时间校正 // 更新event_tv到tv_cache指示的时间或者当前时间(第一次) // event_tv <--- tv_cache gettime(base, &base->event_tv); // 清空时间缓存-- 时间点1 base->tv_cache.tv_sec = 0; // 等待I/O事件就绪 res = evsel->dispatch(base, evbase, tv_p); // 缓存tv_cache存储了当前时间的值-- 时间点2 // tv_cache <--- now gettime(base, &base->tv_cache); // .. 处理就绪事件 } // 退出时也要清空时间缓存 base->tv_cache.tv_sec = 0; return (0); } 时间event_tv指示了dispatch()上次返回,也就是I/O事件就绪时的时间,第一次进入循环时,由于tv_cache被清空,因此gettime()执行系统调用获取当前系统时间;而后将会更新为tv_cache指示的时间。 时间tv_cache在dispatch()返回后被设置为当前系统时间,因此它缓存了本次I/O事件就绪时的时间(event_tv)。 从代码逻辑里可以看出event_tv取得的是tv_cache上一次的值,因此event_tv应该小于tv_cache的值。 设置时间缓存的优点是不必每次获取时间都执行系统调用,这是个相对费时的操作;在上面标注的时间点2到时间点1的这段时间(处理就绪事件时),调用gettime()取得的都是tv_cache缓存的时间。 ...

2021-01-11

libevent源码深度剖析12

libevent源码深度剖析12 让libevent支持多线程 libevent本身不是多线程安全的,在多核的时代,如何能充分利用CPU的能力呢,这一节来说说如何在多线程环境中使用libevent,跟源代码并没有太大的关系,纯粹是使用上的技巧。 1. 错误使用示例 在多核的CPU上只使用一个线程始终是对不起CPU的处理能力啊,那好吧,那就多创建几个线程,比如下面的简单服务器场景。 1 主线程创建工作线程1; 2 接着主线程监听在端口上,等待新的连接; 3 在线程1中执行event事件循环,等待事件到来; 4 新连接到来,主线程调用libevent接口event_add将新连接注册到libevent上; … … 上面的逻辑看起来没什么错误,在很多服务器设计中都可能用到主线程和工作线程的模式…. 可是就在线程1注册事件时,主线程很可能也在操作事件,比如删除,修改,通过libevent的源代码也能看到,没有同步保护机制,问题麻烦了,看起来不能这样做啊,难道只能使用单线程不成!? 2. 支持多线程的几种模式 libevent并不是线程安全的,但这不代表libevent不支持多线程模式,其实方法在前面已经将signal事件处理时就接触到了,那就是消息通知机制。 一句话,“你发消息通知我,然后再由我在合适的时间来处理”; 说到这就再多说几句,再打个比方,把你自己比作一个工作线程,而你的头是主线程,你有一个消息信箱来接收别人发给你的消息,当时头有个新任务要指派给你。 2.1 暴力抢占 那么第一节中使用的多线程方法相当下面的流程: 1 当时你正在做事,比如在写文档; 2 你的头找到了一个任务,要指派给你,比如帮他搞个PPT,哈; 3 头命令你马上搞PPT,你这是不得不停止手头的工作,把PPT搞定了再接着写文档; … 2.2 纯粹的消息通知机制 那么基于纯粹的消息通知机制的多线程方式就像下面这样: 1 当时你正在写文档; 2 你的头找到了一个任务,要指派给你,帮他搞个PPT; 3 头发个消息到你信箱,有个PPT要帮他搞定,这时你并不鸟他; 4 你写好文档,接着检查消息发现头有个PPT要你搞定,你开始搞PPT; … 第一种的好处是消息可以立即得到处理,但是很方法很粗暴,你必须立即处理这个消息,所以你必须处理好切换问题,省得把文档上的内容不小心写到PPT里。在操作系统的进程通信中,消息队列(消息信箱)都是操作系统维护的,你不必关心。 第二种的优点是通过消息通知,切换问题省心了,不过消息是不能立即处理的(基于消息通知机制,这个总是难免的),而且所有的内容都通过消息发送,比如PPT的格式、内容等等信息,这无疑增加了通信开销。 2.3 消息通知+同步层 有个折中机制可以减少消息通信的开销,就是提取一个同步层,还拿上面的例子来说,你把工作安排都存放在一个工作队列中,而且你能够保证“任何人把新任务扔到这个队列”,“自己取出当前第一个任务”等这些操作都能够保证不会把队列搞乱(其实就是个加锁的队列容器)。 再来看看处理过程和上面有什么不同: 1 当时你正在写文档; 2 你的头找到了一个任务,要指派给你,帮他搞个PPT; 2 头有个PPT要你搞定,他把任务push到你的工作队列中,包括了PPT的格式、内容等信息; 3 头发个消息(一个字节)到你信箱,有个PPT要帮他搞定,这时你并不鸟他; 4 你写好文档,发现有新消息(这预示着有新任务来了),检查工作队列知道头有个PPT要你搞定,你开始搞PPT; … 工作队列其实就是一个加锁的容器(队列、链表等等),这个很容易实现实现;而消息通知仅需要一个字节,具体的任务都push到了在工作队列中,因此想比2.2减少了不少通信开销。 多线程编程有很多陷阱,线程间资源的同步互斥不是一两句能说得清的,而且出现bug很难跟踪调试;这也有很多的经验和教训,因此如果让我选择,在绝大多数情况下都会选择机制3作为实现多线程的方法。 3. 例子——memcached Memcached中的网络部分就是基于libevent完成的,其中的多线程模型就是典型的消息通知+同步层机制。下面的图足够说明其多线程模型了,其中有详细的文字说明。 注:该图的具体出处忘记了,感谢原作者。 4. 小节 本节更是libevent的使用方面的技巧,讨论了一下如何让libevent支持多线程,以及几种支持多线程的机制,和memcached使用libevent的多线程模型。

2021-01-11

libevent源码深度剖析13

libevent源码深度剖析13 libevent信号处理注意点 前面讲到了 libevent 实现多线程的方法,然而在多线程的环境中注册信号事件,还是有一些情况需要小心处理,那就是不能在多个 libevent 实例上注册信号事件。依然冠名追加到 libevent 系列。 以 2 个线程为例,做简单的场景分析。 1 首先是创建并初始化线程 1 的 libevent 实例 base1 ,线程 1 的 libevent 实例 base2 ; 2 在 base1 上注册 SIGALRM 信号;在 base2 上注册 SIGINT 信号; 3 假设当前 base1 和 base2 上都没有注册其他的事件; 4 线程 1 和 2 都进入 event_base_loop 事件循环: 5 假设线程 1 先进入 event_base_loop ,并设置 evsignal_base = base1 ;并等待; 6 接着线程 2 也进入 event_base_loop ,并设置 evsignal_base = base2 ;并等待; 于是 evsignal_base 就指向了 base2 ; 7 信号 ALARM 触发,调用服务例程: static void evsignal_handler(int sig){ ... evsignal_base->sig.evsigcaught[sig]++; evsignal_base->sig.evsignal_caught = 1; /* Wake up our notification mechanism */ send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0); ... } 于是 base2 得到通知 ALARM 信号发生了,而实际上 ALARM 是注册在 base1 上的, base2 上的 ALARM 注册 event 是空的,于是处理函数将不能得到调用;因此在 libevent 中,如果需要处理信号,只能将信号注册到一个 libevent 实例上。 ...

2021-01-11

libevent源码深度剖析一

libevent源码深度剖析一 1. 前言 libevent是一个轻量级的开源高性能网络库,使用者众多,研究者更甚,相关文章也不少。写这一系列文章的用意在于,一则分享心得;二则对libevent代码和设计思想做系统的、更深层次的分析,写出来,也可供后来者参考。 附带一句:libevent是用c语言编写的(大牛们都偏爱c语言哪),而且几乎是无处不函数指针,学习其源代码也需要相当的c语言基础。 2. Libevent简介 上来当然要先夸奖啦,libevent 有几个显著的亮点: 事件驱动(event-driven),高性能; 轻量级,专注于网络,不如ACE那么臃肿庞大; 源代码相当精炼、易读; 跨平台,支持Windows、Linux、BSD和Mac Os; 支持多种I/O多路复用技术, epoll、poll、dev/poll、select和kqueue等; 支持I/O,定时器和信号等事件; 注册事件优先级; libevent已经被广泛的应用,作为底层的网络库;比如memcached、Vomit、Nylon、Netchat等等。 libevent当前的最新稳定版是1.4.13;这也是本文参照的版本。 3. 学习的好处 学习libevent有助于提升程序设计功力,除了网络程序设计方面外,libevent的代码里有很多有用的设计技巧和基础数据结构,比如信息隐藏、函数指针、c语言的多态支持、链表和堆等等,都有助于提升自身的程序功力。 程序设计不止要了解框架,很多细节之处恰恰也是事关整个系统成败的关键。只对libevent本身的框架大概了解,那或许仅仅是一知半解,不深入代码分析,就难以了解其设计的精巧之处,也就难以为自己所用。 事实上libevent本身就是一个典型的Reactor模型,理解Reactor模式是理解libevent的基石;因此下一节将介绍典型的事件驱动设计模式——Reactor模式。 参考资料: libevent官方地址: http://monkey.org/~provos/libevent/

2021-01-11

Linux C/C++后端开发面试问哪些问题

Linux C/C++后端开发面试问哪些问题 今天我的技术群(想加技术群的可以加我微信 easy_coder)里面一名叫“成都-go-戒炸鸡”的群友提出了他最近面试的一些面试题,面试题内容个人觉得非常典型、也非常有代表性和针对性,故拿出来与大家分享一下,也感谢他的分享。成都-go-戒炸鸡说: “今天面试,我没答出来的有redis持久化机制,redis销毁方式机制,mq实现原理,c++虚函数,hash冲突的解决,memcached一致性哈希,socket函数select的缺陷,epoll模型,同步互斥,异步非阻塞,回调的概念,innodb索引原理,单向图最短路径,动态规划算法。” 为了避免问题有歧义,面试题略有修改。 思路分析 从面试题的内容可以看出,这是一个后台开发的职位。 除了关于 c++ 虚函数这个问题以外,其他的大多数问题都与哪种编程语言关系不大,大多数是原理性和基础性的问题,少数是工作经验问题,笔者试着给大家分析。 语言基础 C++ 虚函数这是面试初、中级 C ++ 职位一个概率95%以上的面试题。一般有以下几种问法: 在有继承关系的父子类中,构建和析构一个子类对象时,父子构造函数和析构函数的执行顺序分别是怎样的? 在有继承关系的类体系中,父类的构造函数和析构函数一定要申明为 virtual 吗?如果不申明为 virtual 会怎样? 什么是 C++ 多态?C++ 多态的实现原理是什么? 什么是虚函数?虚函数的实现原理是什么? 什么是虚表?虚表的内存结构布局如何?虚表的第一项(或第二项)是什么? 菱形继承(类D同时继承B和C,B和C又继承自A)体系下,虚表在各个类中的布局如何?如果类B和类C同时有一个成员变了m,m如何在D对象的内存地址上分布的?是否会相互覆盖? 算法与数据结构基础 说到算法和数据结构,对于社招人士和对于应届生一般是不一样的,对于大的互联网公司和一般的小的企业也是不一样的。下面根据我当面试官面试别人和找工作被别人面试经验来谈一谈。 先说考察的内容,除了一些特殊的岗位,常见的算法和数据结构面试问题有如下: 排序(常考的排序按频率考排序为:快速排序 > 冒泡排序 > 归并排序 > 桶排序) 一般对于对算法基础有要求的公司,如果你是应届生或者工作经验在一至三年内,以上算法如果写不出来,给面试官的影响会非常不好,甚至直接被 pass 掉。对于工作三年以上的社会人士,如果写不出来,但是能分析出其算法复杂度、最好和最坏的情况下的复杂度,说出算法大致原理,在多数面试官面前也可以过的。注意,如果你是学生,写不出来或者写的不对,基本上面试过不了。 二分查找 二分查找的算法尽量要求写出来。当然,大多数面试官并不会直接问你二分查找,而是结合具体的场景,例如如何求一个数的平方根,这个时候你要能想到是二分查找。我在2017年年底,面试agora时,面试官问了一个问题:如何从所有很多的ip地址中快速找个某个ip地址。 链表 无论是应届生还是工作年限不长的社会人士,琏表常见的操作一定要熟练写出来,如链表的查找、定位、反转、连接等等。还有一些经典的问题也经常被问到,如两个链表如何判断有环(我在2017年面试饿了么二面、上海黄金交易所一面被问过)。链表的问题一般不难,但是链表的问题存在非常多的“坑”,如很多人不注意边界检查、空链表、返回一个链表的函数应该返回链表的头指针等等。 队列与栈 对于应届生来说一般这一类问的比较少,但是对于社会人士尤其是中高级岗位开发,会结合相关的问题问的比较多,例如让面试者利用队列写一个多线程下的生产者和消费者程序,全面考察的多线程的资源同步与竞态问题(下文介绍多线程面试题时详细地介绍)。 栈一般对于基础要求高的面试,会结合函数调用实现来问。即函数如何实现的,包括函数的调用的几种常见调用方式、参数的入栈顺序、内存栈在地址从高向低扩展、栈帧指针和栈顶指针的位置、函数内局部变量在栈中的内存分布、函数调用结束后,调用者和被调用者谁和如何清理栈等等。某年面试京东一基础部门,面试官让写从0加到100这样一个求和算法,然后写其汇编代码。 哈希表 哈希表是考察最多的数据结构之一。常见的问题有哈希冲突的检测、让面试者写一个哈希插入函数等等。基本上一场面试下来不考察红黑树基本上就会问哈希表,而且问题可浅可深。我印象比较深刻的是,当年面试百度广告推荐部门时,二面问的一些关于哈希表的问题。当时面试官时先问的链表,接着问的哈希冲突的解决方案,后来让写一个哈希插入算法,这里需要注意的是,你的算法中插入的元素一定要是通用元素,所以对于 C++ 或者 Java 语言,一定要使用模板这一类参数作为哈希插入算法的对象。然后,就是哈希表中多个元素冲突时,某个位置的元素使用链表往后穿成一串的方案。最终考察 linux 下 malloc(下面的ptmalloc) 函数在频繁调用造成的内存碎片问题,以及开源方案解决方案 tcmalloc 和 jemalloc。总体下来,面试官是一步步引导你深入。(有兴趣的读者可以自行搜索,网上有很多相关资料) 树 面试高频的树是红黑树,也有一部分是B树(B+树)。 红黑树一般的问的深浅不一,大多数面试官只要能说出红黑树的概念、左旋右旋的方式、分析出查找和插入的平均算法复杂度和最好最坏时的算法复杂度,并不要写面试者写出具体代码实现。一般 C++ 面试问 stl 的map,java 面试问 TreeMap 基本上就等于开始问你红黑树了,要有心里准备。笔者曾经面试爱奇艺被问过红黑树。 B树一般不会直接问,问的最多的形式是通过问 MySQL 索引实现原理(数据库知识点将在下文中讨论)。笔者面试腾讯看点部门二面被问到过。 图 图的问题就我个人面试从来没遇到过,不过据我某位哥哥所说,他在进三星电子之前有一道面试题就是深度优先和广度优先问题。 其他的一些算法 如A*寻路、霍夫曼编码也偶尔会在某一个领域的公司的面试中被问到,如宝开(《植物大战僵尸》的母公司, 在上海人民广场附近有分公司)。 编码基本功 还有一类面试题不好分类,笔者姑且将其当作是考察编码基本功,这类问题既可以考察算法也可以考察你写代码基本素养,这些素养不仅包括编码风格、计算机英语水平、调试能力等,还包括你对细节的掌握和易错点理解,如有意识地对边界条件的检查和非法值的过滤。请读者看以下的代码执行结果是什么?(笔者2011年去北京中关村的鼎普面试的问题) for(char i = 0; i < 256; ++i) { printf("%d\n", i); } 下面再列举几个常见的编码题: 实现一个 memmov 函数 这个题目考查点在于 memmov 函数与 memcpy 函数的区别,这两者对于源地址与目标地址内存有重叠的这一情况的处理方式是不一样的。 实现strcpy或strcpy函数 这个函数写出来没啥难度,但是除了边界条件需要检查以外,还有一个容易被忽视的地方即其返回值一定要是目标内存地址,以支持所谓的链式拷贝。即: strcpy(dest3, strcpy(dest2, strcpy(dest1, src1))); 实现atoi函数 这个函数的签名如下: int atoi(const char* p); 容易疏忽的地方有如下几点: ...

2021-01-11

Linux epoll 模型(含LT 模式和 ET 模式详解)

Linux epoll 模型(含LT 模式和 ET 模式详解) 综合 select 和 poll 的一些优缺点,Linux 从内核 2.6 版本开始引入了更高效的 epoll 模型,本节我们来详细介绍 epoll 模型。 要想使用 epoll 模型,必须先需要创建一个 epollfd,这需要使用 epoll_create 函数去创建: #include <sys/epoll.h> int epoll_create(int size); 参数 size 从 Linux 2.6.8 以后就不再使用,但是必须设置一个大于 0 的值。epoll_create 函数调用成功返回一个非负值的 epollfd,调用失败返回 -1。 有了 epollfd 之后,我们需要将我们需要检测事件的其他 fd 绑定到这个 epollfd 上,或者修改一个已经绑定上去的 fd 的事件类型,或者在不需要时将 fd 从 epollfd 上解绑,这都可以使用 epoll_ctl 函数: int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event); 参数说明: 参数 epfd 即上文提到的 epollfd; 参数 op,操作类型,取值有 EPOLL_CTL_ADD、EPOLL_CTL_MOD 和 EPOLL_CTL_DEL,分别表示向 epollfd 上添加、修改和移除一个其他 fd,当取值是 EPOLL_CTL_DEL,第四个参数 event 忽略不计,可以设置为 NULL; 参数 fd,即需要被操作的 fd; 参数 event,这是一个 epoll_event 结构体的地址,epoll_event 结构体定义如下: struct epoll_event { uint32_t events; /* 需要检测的 fd 事件,取值与 poll 函数一样 */ epoll_data_t data; /* 用户自定义数据 */ }; epoll_event 结构体的 data 字段的类型是 epoll_data_t,我们可以利用这个字段设置一个自己的自定义数据,它本质上是一个 Union 对象,在 64 位操作系统中其大小是 8 字节,其定义如下: typedef union epoll_data { void* ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t; 函数返回值:epoll_ctl 调用成功返回 0,调用失败返回 -1,你可以通过 errno 错误码获取具体的错误原因。 ...

2021-01-11

Linux tcpdump 使用介绍

Linux tcpdump 使用介绍 tcpdump 是 Linux 系统提供一个非常强大的抓包工具,熟练使用它,对我们排查网络问题非常有用。如果你的机器上还没有安装,可以使用如下命令安装: yum install tcpdump 如果要使用 tcpdump 命令必须具有 sudo 权限。 tcpdump 常用的选项有: -i 指定要捕获的目标网卡名,网卡名可以使用前面章节中介绍的 ifconfig 命令获得;如果要抓所有网卡的上的包,可以使用 any 关键字。 ## 抓取网卡ens33上的包 tcpdump -i ens33 ## 抓取所有网卡上的包 tcpdump -i any -X 以 ASCII 和十六进制的形式输出捕获的数据包内容,减去链路层的包头信息;-XX 以 ASCII 和十六进制的形式输出捕获的数据包内容,包括链路层的包头信息。 -n 不要将 ip 地址显示成别名的形式;-nn 不要将 ip 地址和端口以别名的形式显示。 -S 以绝对值显示包的 ISN 号(包序列号),默认以上一包的偏移量显示。 -vv 抓包的信息详细地显示;-vvv 抓包的信息更详细地显示。 -w 将抓取的包的原始信息(不解析,也不输出)写入文件中,后跟文件名: tcpdump -i any -w filename -r 从利用 -w 选项保存的包文件中读取数据包信息。 除了可以使用选项以外,tcpdump 还支持各种数据包过滤的表达式,常见的形式如下: ## 仅显示经过端口 8888 上的数据包(包括tcp:8888和udp:8888) tcpdump -i any 'port 8888' ## 仅显示经过端口是 tcp:8888 上的数据包 tcpdump -i any 'tcp port 8888' ## 仅显示从源端口是 tcp:8888 的数据包 tcpdump -i any 'tcp src port 8888' ## 仅显示源端口是 tcp:8888 或目标端口是 udp:9999 的包 tcpdump -i any 'tcp src port 8888 or udp dst port 9999' ## 仅显示地址是127.0.0.1 且源端口是 tcp:9999 的包 ,以 ASCII 和十六进制显示详细输出, ## 不显示 ip 地址和端口号的别名 tcpdump -i any 'src host 127.0.0.1 and tcp src port 9999' -XX -nn -vv 下面我们通过三个具体的操作实例来演示一下使用 tcpdump 的抓包过程。 ...

2021-01-11

Linux 网络故障排查的瑞士军刀

Linux 网络故障排查的瑞士军刀 nc 即 netcat 命令,这个工具在排查网络故障时非常有用,功能非常强大,因而被业绩称为网络界的“瑞士军刀”,请读者务必掌握。默认系统是没有这个命令的,你需要安装一下,安装方法: yum install nc nc 命令常见的用法是模拟一个服务器程序被其他客户端连接,或者模拟一个客户端连接其他服务器,连接之后就可以进行数据收发。我们来逐一介绍一下: 模拟一个服务器程序 使用 -l 选项(单词 listen 的第一个字母)在某个 ip 地址和端口号上开启一个侦听服务,以便让其他客户端连接。通常为了显示更详细的信息,会带上 -v 选项。 示例如下: [root@iZ238vnojlyZ ~]# nc -v -l 127.0.0.1 6000 Ncat: Version 6.40 ( http://nmap.org/ncat ) Ncat: Listening on 127.0.0.1:6000 这样就在 6000 端口开启了一个侦听服务器,我们可以通过 127.0.0.1:6000 去连接上去;如果你的机器可以被外网访问,你可以使用 0.0.0.0 这样的侦听地址,示例: [root@iZ238vnojlyZ ~]# nc -v -l 0.0.0.0 6000 Ncat: Version 6.40 ( http://nmap.org/ncat ) Ncat: Listening on 0.0.0.0:6000 模拟一个客户端程序 用 nc 命令模拟一个客户端程序时,我们不需要使用 -l 选项,直接写上 ip 地址(或域名,nc 命令可以自动解析域名)和端口号即可,示例如下: ## 连接百度 web 服务器 [root@iZ238vnojlyZ ~]# nc -v www.baidu.com 80 Ncat: Version 6.40 ( http://nmap.org/ncat ) Ncat: Connected to 115.239.211.112:80. 输出提示我们成功连接上百度 Web 服务器。 我们知道客户端连接服务器一般都是操作系统随机分配一个可用的端口号连接到服务器上去,使用 nc 命令作为客户端时可以使用 -p 选项指定使用哪个端口号连接服务器,例如,我们希望通过本地 5555 端口连接百度的 Web 服务器,可以这么输入: ...

2021-01-11

Memcached源码分析

Memcached源码分析 00 服务器资源调整 01 初始化参数解析 02 网络监听的建立 03 网络连接建立 04 内存初始化 05 资源初始化 06 get过程 07 cas属性 08 内存池 09 连接队列 10 Hash表操作 12 set操作 13 do_item_alloc操作 14 item结构 15 Hash表扩容 16 线程交互 17 状态机 ​

2021-01-11