把昨天的 第二天的内容说一下,复习一下,第二天 讲的东西不算多,但是有两个作业题来写一写,

大致浏览一下,三次握手 四次挥手的过程,大家有没有画一下? 能画出来吗?同学们,大家注意 这个写代码的时候其实我们用不着它,就是让大家面试的时候能够顺利一些,这个你需要了解清除,让大家画图,每一个图里面 它的编号  序号怎么来的,

这个数字怎么来的  你要搞清楚,这个你们都动手画了画是吗?其中 三次握手是干什么的? 是不是建立连接的 三次握手的过程啊? 还有四次挥手 是不是断开连接的? 那么这两个过程是你做的吗?内核帮我完成的,那么我再提一个问题,那么为什么我们这个tcp 它是面向连接 安全可靠的啊? 你从哪几个方面去说这个问题呀? 首先 第一个  那么为什么 说它是面向连接的,为什么这么说?因为 它是不是有一个 三次握手 建立连接的过程啊?这肯定是面向连接的? 第二个 为什么它是安全可靠的呢?其实主要是 这两个方面,

是不是序号 和确认序号啊? 是不是主要体现在这啊? 那么如果说这个 丢了包呢?是不是会重传啊?那么对方收到之后,它是不是会给你来一个确认序号啊?那么你收到确认序号,它是不是会给你往下发呀?是不是?所以说 它是面向连接 安全可靠的数据流传输, 或者叫字节流传输也行,

滑动窗口的主要作用是什么呀?进行流量控制,举个例子,如果发送的过快,接收的过慢 是不是造成数据的阻塞啊?有可能会造成数据的丢失,那么 接收方的接收缓冲区 ,满以后那么再发的话 那么有可能会丢失,是不是这样的? 其实也就是这个滑动窗口双方是相当于决定了规则啊?我发多少你收多少? 你那边有多少空间你告诉我,

这个图里面每一个标识的含义你要搞清楚,mss 是什么意思?谁告诉谁?箭头左边的 告诉 箭头右边的,那么它里面 也就是说箭头左边,告诉箭头右边的,那么它这边一次性 最多接多少个数据吧?大家注意,这个是字节, 按字节来说的,8个位是不是一个字节啊?

那么这里面还有一个 win 什么意思?是不是告诉对方他这边的缓存区还有多少可用空间吧?还有多少可用空间?这里面的每一个数字怎么来的?自己会算了吗现在?咱们还是重点说的,而且我这个图上是不是也有同学们,我记得那个图上有,是不是这个自己看一看就不再说了,这个封装的思想这个也不是死的,这个真正我们使用的时候,

你根据你的实际情况来是不是?咱们说的时候是不是?这上面咱们讲义上大纲,说让你这个在写函数的时候,这个名字是不是第一个用大写的?你不用行不行?,是不是也可以?这个不是死的,这里边给你红色标记的这个你得知道,还记得吗?同学们,这个阻塞函数是不是能够被信号打断?那被信号打断之后呢?它的返回,它的这个errno=EINTR是不是设置为这个 EINTR,是吧?这你知道这个自己看一看,然后这个粘包的概念以及解决方案也知道。

那么粘包的意思是不是这样的?就是说有可能两次或多次发送数据,在对方接收的时候,他是不是一次整个把一次的数据接收完啊,它可能放在两次了,是不是?那这种情况下就会产生粘包,那么解决粘包的这个办法最常见的是不是这个方法啊?是不是前面加一个包头了?包头加数据,那么这个包头最常见的就是什么呀?用一个长度,比如 4 个字节,两个字节都行,是不是把前面固定的这个长度,把这个数据的长度填在这个位置,是不是这样的?那么你注意有一点你得知道,就是你在用的时候,比如说我们这个数据长度是0010,是吧?同学们,如果说你发送的数据就是整型,你要进行转换,什么转换啊?是不是大写字段转换的?要主机字节序转换为网络的字节序?如果你发的是字符串的话,还用转吗?不用了,比如说0010,它这个字数串就不用转了。如果你发的10,比如 10 是个整型,你要转,这个要知道。

其实咱们在写代码的时候,是不是多次用到这个 IP 和端口进行转换的?是不这样的?好,那么方案 2 方案 3 这个你看一看。那最常见的是我给大家红色标记,这个是我们用的比较多的,是不是?那么这个高并发服务器呢?咱们就直接看作业了,讲两个,一个是多进程版本的,一个是多线程版本的,这两个要求大家能够把这里边的每一句话搞明白,能够独立写出来,清楚了吗?这是给大家要求。好,那咱们把那个作业看一看,其中咱们多进程版本的时候呢,咱们是这么给大家,是不是说要怎样进行完善?完善什么功能啊,是不是父进程使用信号完成对子进程的回收?好,那么咱们一块把这个程序再大体捋一捋,那么咱们这个创建这个服务端的这个过程,是不是咱们都讲过好几次了,是吧?第一步先创建socket ?第二步? 这个是设置端口复用,今天咱会说这个设置端口复用叫 setsockopt 的这个函数。

好。第三个是bind绑定吧,将文件描述和端口 IP 进行绑定,是不是后续我们是不是都在操作这个文件描述了?其实操作这个文件描述就是一共这个文件描述,在这个服务端有几类啊?两类吧,一类是什么监听文件描述符,一类是通信文件描述符。好,那么接下来调用 linsten函数进行监听,那这个时候我们的程序是不是就已经处于监听的状态了?大写的 linsten是不是还记得吧?嗯,好,那么接下来呢?这个先不说,先把整体流程说一下,那么接下来我们进入一个while语句 循环,然后是不是调用 accept 接收一个新的链接啊,大家应该知道在accept 之前,这个链接是不是已经可能已经建立了?是不是这个大家搞这个搞清楚,也就是说这个accept 函数,accept 的这个函数本身它不是说新建一个链接,而是说拿出一个可用链接来。

那么接下来接受一个新链接之后,他是不是调用 fork函数创建一个子进程啊,是吧?在父进程里面,是吧同学们,是不是只是用来接受新的链接啊,所以他不通信,所以这个要关掉是不这样的,这个应该知道为什么要关掉了吧?那么父进程fork出一个子进程来,父子进程是不是共享?那么应该说不叫共享,这两个是不是父亲有一个文件描述符?是不是这个啊?子进程是不是也有啊?是不是?那这个时候你说这个文件描述的引用计数是几啊?引用计数 ,引用计数,那么这个通讯文件描述符引用计数是不是 2 了?是不是这样的?那父进程关掉以后,是不是不影响另一个进程?这个知道好,那么这个是父进程,那么父进程好,那么做的事情就这些。

然后子进程是不是他要关闭监听文件描述符啊?对吧?那么关闭以后,因为他子进程咱们都知道他是不是不用监听啊,是不是父进程监听,然后子进程通讯,是这么一个规则,那接下来它进入while循环是不是收发数据啊?这是咱们这个第二天给你讲的时候,那个整体的流程吧?要求你加的逻辑是在父进程里面完成对子进程的回收,是吧?同学们,好,咱们看怎么加的?首先咱们看一下,前面为什么一开始我要阻塞这个SIGCHLD的信号?防止,防止什么?

防止什么防止?是不是你这个信号注册的过程还没完成,然后子进程的全部结束了?有没有可能?这是有可能的,所以我们在前面先阻塞这信号是不是这样的?好?那么接下来在这个父进程里面是不是要注册一个回调函数了?是不是?给谁给内核注册一个信号处理函数?这哪个信号了?是不是 SIGCHLD信号了?是不是这个信号?调用的是sigaction吧?是吧同学们?大家记得 signal 和 sigaction 的区别吗?什么区别?是不是一个可以移植啊?一个,另外一个 signal 是不是一直可能会出现问题?是不是?你用的时候尽可能用这个sigaction ,用尽可能用这个?那么接下来是不是调用 sigaction 注册了一个信号处理函数,那么注册完了以后是不是在解除对这个信号的阻塞啊?用的是sigprocmask这个函数的用法,要求你掌握。好吧?要求你掌握,这个,你们考试被考到了吗?这个好,那么这个时候就已经完成注册了,并且解除了对这个信号的阻塞了。

好,那么如果说这个时候子进程通信结束了,子进程要怎么样?要退出?是不是?那么那么想一下,这个子进程在什么情况下会退出?对方关闭链接,他是不是需要退出?是不是这样的?然后读失败是不是也有可能退出?那这个时候他退出之后,是不是这个内核会给他的附近发送一个SIGCHLD 的信号了?那么这个父进程收到这信号之后干什么呀?回收,是不是回收?所以这个函数出来了,

是不是调用 wait PID wait 这个函数完成?当然这个函数是不是完成对子进程的回收吧?这个函数这几个参数还记得吗?第一个参数是-1,什么意思?表示回收任意一个子进程,那第二个是NULL,那在这是不是这个子进程退出状态啊?对啊,你不关心就设置为NULL就可以了。

后面这个 WNOHANG什么意思?表示不阻塞,那么这个在这,我们在这个里面加了 while循环,其目的是什么还记得吗?是不是这个目的是为了防止有多个子进程,是不是同时退出的情况?是不是好,那么如果是有一个的话也没有关系,是不是大不了多循环一次,是不是这样的?那么这两个是他退出的一个条件,是不是?如果说子进程还活着 25,或者是子进程已经全部没有了 25?这个时候是break;。

好,这个里面我问大家一下,你说这个函数这个信号处理函数,一开始在调的时候,是不是肯定会能够回收一个呀?为什么呀?因为你这个信号不是产生了之后才去回收。所以他肯定会回收一个,那么也有可能说会循环回收多个,是不是这样的?对,是不是?当然这里面肯定有一个过程,是肯定会多循环一次,是不是这样的?也就是有一次无用的循环,是不是?不过也没有关系,我们用这个无用的循环是不是可以退出进,退出这个跳出循环的,是不是?好,这个还这个代码要求你能够完完全全掌握,因为这一块的话,只要是涉及到多进程开发的话,这一步这些东西是免不了的,清楚了吗?是免不了的。

好,那么下面看另外一个那么多线程版本了,那么这个流程我就不再说了,咱们从这开始说,这个是多线程版本的,那么在这个主线程里面接受一个新的链接之后,它是不是要调用 pthread_create的函数创建一个子线程?是不是?那么在这个子线程里面是不是完成对什么呀?完成对这个客户端数据的收发啊?是不是这样的?好,那么这个里面咱们要求你改进的方向是什么呀?还记得吗?改进的方向。

是不是让你定义一个结构数组啊,然后这个结构体,这个数组里面的每一个元素是不是存放这个线程相关的信息啊?这个咱们就给你定义了一个结构体,这个结构体看一下这结构体是吧?其中第一个是cfd,这个是不是通信的文件描述符了,这个值从哪来的?谁给的?

是不是这个主线程调用accept返回的那个值吧?是不是这样?第二个index,这个你不用看,这个是这个是索引,我们这个数组是不是都有下标? 01234 往后排,那这个就是那下标,待会告诉你这个干什么用的?好,第三个是线程id吧?第四个是不是客户端的地址吧?  是不是这样的?好,那么看一下这个函数怎么用?首先这里面我们进行了初始化,那么这个初始化是不是在一开始就初始化了?我们 把这个 cid 全部设置为- 1 了,那么意思是这是不是都是可用的?是不是这样的?因为一开始没有这个客户端的时候,是不是我们这个空间都是可用的啊?这个意思,

然后这个函数是干什么呢?你猜一下,findindex什么意思?是不是找一个空闲位置啊?他怎么找的?是不是在,是不是先循环?是不是整个循环?如果等于- 1  它是不是就 break 了,它返回的什么呀?同学们看返回的什么呀?数字下标,这个结构体数字下标,那么这儿来了,这玩意儿什么意思?如果找了一圈没找到,那意思是不是没有可用空间了?是不是这样的?好,这个好,咱们看一下这个在哪用?

监听完以后我们就在这儿进行了初始化,没问题吧,当然这个初始化是不是也放上面了?都行,当然你在他使用之前给他初始化了就可以了。

接下来咱们看一下,那么这个主线程调用accept 接收了一个新的链接以后,接下来是不是又开始找位置了,是吧?同学们 叫findindex 找位置,返回一个什么呀?数组下标,能知道什么意思吗?返回一个数下标,当然他这做了一个判断,如果是没找到是不是就 close ,close 他之后是不是相当于拒绝这个服务了?知道什么意思吗?因为这个服务端的这个链接处是不是已满了?满了之后你可以给它关掉,这个是不是相当于拒绝服务?是吧?当然你这个你也可以在这什么呀?你也可以在这加个sleep,是不是?也可以?是不是等待有这个客户端退出吧?是不是这样的?关闭链接也行,好在这他找到一个空闲的位置以后,他是不是把这个c、f、 d 填在这个结构体的数组里面去了,能明白吗?我这个没打开,我看看,这是那个结构体吧,同学们是不是?这是那结构体数组,那么结构体数组里面是不是都有一个下标啊? 012 是吧?是吧同学们 0123 等等这个函数的意思是不是返回一个可用的结构体元素的下标?嗯,那么一开始是不是返回0,一开始的话,那么这个i、d、 x 是不是等于就等于 0 啊?是不是?那么接下来是把这些值是不是填在这个里面去?能理解吗?就干这个事用的,那么你看一下我的这个 i d s 什么意思?看到没?是不是记录的就是这一块地址的那个什么呀?所以是不是下标位置啊?能理解吗?同学们,接着看。

好,这个是这块,这三个是不是相当于赋值啊?接下来是不是调用这个函数?调用pthread_create函数是不是创建一个子线程?嗯,这儿他是不是又把这个东西也赋过去了?看到没? thread info i d s 点 thread 是不是也填到这个位置来了 index 0?能看懂吗?好,这是创建完成了,那么这个最后的话,他是不是要把什么呀?把这个结构体是不是传到那个线程执行函数里面去啊?嗯,那么接下来你就看一下这个线程的执行函数,那么这个执行函数很简单,是不是就是收发数据啊,就是收发数据?当然这个限制这个执行函数,是不是首先他要接收这个参数啊?是不是他是不是用了指针啊?同学们,大家想一下,我们这个使用这个结构体数组来保存这个链接的信息的话,意图是什么呀?还记得吗?

本质上是这样的,本质上是不是有多个线程?如果你不这样干,是不是多个线程会共用同一块内存啊,还记得吗?咱们循环创建 5 个子线程,所以打印出去是几啊?5吧,我们后来怎么做的?是不是分开了?一个线程是不是只等于一块内存啊?我们这个意思是不是跟那意思一样啊?是不是要搞清楚这个?好,那接下来我把这个索引值打出来了,我打印索引值的目的是给你验证一下,我这个在执行的时候,我看一下这个找的这的位置是不是每次都不一样?知道什么意思吗?这是一个情况。

第二个情况是我如果说一个空间,我这一块内存空间腾出来了,下次再分配的时候应该是分配上了,能理解吗?这意思看一下这个里面是不是我们的这个解,我们的每一个元素。同学们,那么这个一开始用了,这都用了,那假如说后来这个对应的客户代链接给关掉了,下次你再分配空间的时候,是不是从这开始分配的啊?我给你打印的值呢?打印这个值的意思是不是这个?清楚什么意思吗?干这用的,别搞混了,好好接着看。

那接下来这是不是打印这个客户端地址?是不是?其实你只要得到那块内存是不是你想打印什么都可以,是不是?

好,接下来然后是不是就收完数据了?这一块是不是就一模一样了?当然对方关闭链接的话,

对方关闭链接或你读异常的话,你还要记得要关闭这个什么呀?关闭这个通信的文件描述吧?同时是不是要把这个设置为-1  49?如果你不设置为- 1 会怎么样,同学们?是不是会浪费很多内存?是不是因为这块内存你用完了是不是记得释放出来?是不是要让后续的链接去使用它?这个思想一定有,最后这个子线程退出是不是这样的?那这个我给你验证一下,这个验证一下,代码能看懂吗?同学们,代码我已经放到共享目录里面去了,你可以拿一下,你可以和你的比较一下到底是哪没写对啊,这个跑起来,咱们看看这个监听起来没?是不是起来了,

连一下 n c 127. 1  8888 回车,再来一个 n c 127. 1 8888

咱们先看两个,这个是不是已经都打出来了?第一开始是不是从 0

这个第0个位置开始分配,后来是不是从 1 开始分配的?好,我再给大家一个,应该是几了,下面是不是2了?

是不是?好,那我关闭一个,关闭一个,再启动一个,你说应该从几?假如我从,我把第二关掉,然后我再把这个启动起来,从几,从2,从几,也就是我这个空间给它腾出来了,是不是不用了?好把它关掉,看发数据行不行?发数据没问题是吧?发数据没问题,是不是回来了?我关掉之后他是不是这个客户的链接关闭了,他又打出来了?再使用,几?这还用说吗?你这个中间某一个位置空闲了,是不是要重新分配啊? 是不是这样的?其实打印这个 i d x 的目的就是给大家验证这个的这个东西其实本质上没有什么用,是吧同学们,本质上没什么用,对于我们这个使用来说用处不大,但是你这个你想看一下它到底分配对了没有?你可以把这个打印出来看一看,是吧同学们,嗯,关于这一块的复习就说这么多,这个多线程的版本的,这个比多进程版本呢,稍微复杂那么一些,复杂一些,特别是在在这个什么呀?给子线程传参的时候,这个你需要传哪些参数你得清楚,你切记一点,你不能让多个子线程共享一块内存,能理解吗?同学们,如果你非共享,那只能是加锁,是不是加锁有点不好使啊?对于这种情况,加速好使吗?

好使为什么好使?那么加速了会不会把这个原来的文件描述符给覆盖啊?会不会覆盖?这还用说吗? 我们用多个,用多块内存的话,是不是就避免这个问题啊?还记得吗?同学们,咱们在跟测试的时候,是不是那个文件描述是不是只是最后一个有效,还记得吗?中间那些什么全没有了,搞清楚。好,这我一点就你就通了。

好,那么接下来咱们讲今天讲这个,今天的一些主要内容,好看一下,这个自己看了看没?试了没,同学们,是吧?如果你read,读这个文件描述符,这个文件描述是非阻塞的,这个时候如果对方没有发数据,这个 read 函数是不是会立刻返回?他返回的错误是EAGAIN,这个你说是错误吗?同学们,这个不应该视为一个错误,不应该视为一个错误,这你可以自己试验试验。

好,那么接下来咱们看一下今天都学什么啊?第一个是要求你掌握 TCP 状态转换图,这个干什么用?这个有的时候我们排错的时候有用。再一个,你这个面试的时候人家可能会问你清楚了嘛?这些是这个进行,你学习网络编程的时候一些基本的理论知识,这个你得会,起码来说你能够把那个图看懂。

第二,掌握端口复用的方法,这个端口复用是不是咱们这个在写代码的时候 给你用过一次了?嗯,那个代码就那么用啊,就那么用啊,是死的,就咱们前面讲 f control 函数,是不是那三步也是死的?你记不住也没关系,直接查就可以了。好好,第三个让你了解一下半关闭的概念和实现方式,那么其这一块主要讲一个(satedang)函数,半关闭,那么关注可以关闭读端,也可以关闭写端,这是半关闭的意思。好,后面讲一下这个多路 IO 转接模型,这儿主要讲select,select,这是今天的一个重点, select函数,那么讲完 select函数以后给大家讲一下这个其他的与 select函数相关的一些函数,主要是一些辅助配套的函数,辅助的函数。最后给大家把这个 select 用 select 实现高并发服务器的这个思路跟你说一下,最后咱们写个代码就结束了,这今天就这么点东西,都预习了吧?好,那么接下来咱们要讲的什么呀?讲这个状态转换图,这个图你看着可能费劲,是的,可能看着费劲,那这样咱们要去掌握哪个?你把这个都掌握了,这个知道什么意思?其实上面那个图只不过是这个图的另外一种表现形式而已,你这个能看懂的话,像那个的话看着也应该不费劲,这样,那么咱们呢?我这给你打开一个图,咱们把这个图给你说一下,然后咱们再一块画一下就可以了。

我打开这个图,咱们一块来看一看,我把它放在一块了,把它放一块了,这样是不是看着要好一些?能看清吗?同学们,能看清吗?好同学们,看一下你前面那个 3 次握手过程和 4 次挥手过程,你掌握了以后,那么这个无非就是加了几个状态而已,是不是?看一下?好,那首先发起链接的一方是不是客户端的?是不是客户端?那么建立链接是不是要有 3 次握手过程?好,那么这个图上面这一部分就是建立链接的 3 次握手过程,那么这个过程完成以后,那么接下来是不是要进行数据传输了?数据传输结束以后是不是要关闭链接?关闭链接是后面那个 4 次挥手过程。

4 次挥手过程,那整个这个图呢?在咱们前面讲这个三次挥手, 4 次挥手那个过程是不是一样的?那么这里面就增加了一些状态,我们看一下客户端主动给服务端发起链接请求,那这个时候大家注意他的这个状态,什么状态,他自己的状态变成了SYN_SENT ,那么这个状态咱们在真正咱们写代码在测试的时候很难捕获到。为什么呀?非常的快,是不是非常的快?你可以把它理解成中间状态,可以吗?是可以的。

好,那么这个时候序号它是不是发的是x?序号 ,SYN表示什么?同学还记得吗?表示发起一个链接请求,发起链接请求大家再想一下,那么咱们在建立链接的时候,我是不是跟你说了,那么建链接是不是内核做的?内核在什么情况下才会去建链接?你是不是要在这个你的程序里面调用 connect 函数了?嗯,你调用 connect 函数以后,内核就会给你把这 3 次握手过程完成,把这个连接建好,明白了吗?所以它这调的就是 connect 函数,这个序号是x,那么这个你的服务端,注意服务端是不是一定要处于 listen 状态才行?处于监听状态,就是让它的状态是listen,那么它调用的哪个函数进行监听的啊?同学们是不是listen函数了?listen函数。

那么接下来他收到,注意这个服务端,收到客户端的链接请求之后,他这边其实就处于了 s y n receive 的状态,当然他是不是也发回去了?也就是说实际上到这以后,这个是不是我们认为这个半链接建好了?当然一定是到哪了?是不是到这了,朋友们,第二步完成以后,相当于半连接建好了,那么这个服务端处于SYN_RCVD,它同时是不是也会给客户端发送一个 syn 请求啊?它的序号多少?y吧?那么ACK 是 x 加1, x  加1,是不是就是 s 加1?这儿这个 x 加个 1 是不是?这个咱都给你讲过了。嗯,好,那这个时候客户端收到了服务端发过来请求和 ACK 以后,他自己就处于了 establish 状态。

establish 想什么意思?这时候叫链接建立了,其实到这儿以后,只是建立一半,是不是?同学们,咱们这个链接是不是一个链接对了?同学们,socket片吗?链接对,这个其实相当于建立了一半了,那这个时候客户端要给服务端回一个ACK,这个时候序号是 y 加1,是不是?同学们, y 加1, y 加 1 怎么来的?是不是上面那个 y 加个1,是不是?好,到此为止,服务端收到客户端发的 ACK以后呢?这个时候什么呀?这个时候链接就真正建立了,也就是三次握手,是不是已完成了?嗯,三次握手,完成链接建立。这个时候建链接是一个什么呀?是一个双向链接,叫 socket pair。

大家还记得我们讲那个图的时候吗?一个文件描述是不是对应两个缓冲区啊?那么这个两个缓存区里面是不是有一个叫发送缓存区,一个叫接收缓存区,还记得吗?嗯,这样的,这一个是吧?是吧同学们,是不是这一个,这一个,这一个,

这个是 send  的 receive 接收,是不是?那这个这是不是就读上去,接收缓冲区,这个写缓冲区,去, s 发送,是不是?他们是不是这样的?同学们是不是这样的?嗯,要搞清楚这是不是一个连接对啊?同学们,连接对,那么是连接对,后面所以就有这个半关闭的事情了,他是不是可以把这条链接关掉?是不是关闭一半?他是不是也可以把这一块关掉?是不是也可以一次全关掉都行?好,这,那么这个过程在这三四握手建立完成了,那么这个里面有有些状态你得看一看,一个什么呀?一开始是不是有一个SYN_SENT 的状态。

这个状态是客户端的一个什么呀?开始的状态吧?你得知道他在什么情况下会有这个状态?是他调用 connect 函数以后,是吧?那么这个对于服务端来说,它是不是有 listen?这个 listen 状态?它是不是要调用 listen 函数以后?好,然后它的这个服务端收到客户端发过来的链接请求之后,他自己就处于了SYN_RCVD的,是不是?当然他是不是也给这个客户端发了一个链接请求啊?嗯,是吧?好,那么最后客户端、服务端是不是都处于 establish 状态的?这两个进程就可以通信了,清楚了吗?就可以通信了。那么其实这个状态咱们是可以看的。怎么看?你前面这个我们看不到,是吧? listen 时候我们也可以看到 receive 的,能看到吗?朋友们,看不到,这个也看不到, SVN_SENT也看不到,但是这个 established 我们可以看到。嗯,好,我给你验证一下是不是这样的起来,来一个。

首先你看服务端这个状态是不是linsten状态?嗯,这是他的一开始状态。嗯,好,再看,你看这两个链接对上,这一个链接对上,是不是都是处于 ESTABLISHED 状态? 这个服务端状态还是客户端状态?这个ESTABLISHED ,是看这,同学们别看错了 mutl_thread_ad,是不是看这了,这个是不是服务端程序啊?这时候客户端程序清楚了吗?那么另外的话,左侧我跟你们说过,左侧是不是表示自己的啊?右侧是不是表示对方的啊?很显然,这个 8 个 8,4 个 8 是不是表示服务端的?这个端口是不是表示客户端的啊?搞清楚,这个好,这个大家搞清楚了。好,那么接着往后看,这个,这样,这个我想给你,先给你保存一下,这里面跟你说一点,

好,那么关于这个什么建立?第一个是 t c p 状态转换图,第一个过程是什么呀?是三次握手过程,是不是这样的?这里面需要你知道的状态是什么呀?叫SYN_SENT,这个谁的状态?同学们,客户端的话这边还有一个叫 SYN_RCVD,是不是?当然你这服务端一开始应该处于什么状态?同学们,是不是listen,这个知道listen,那么listen他掉哪还是出于listen状态?同学们,listen函数,listen函数,我这给你标一下,这你不用记啊,这你看我写就可以了。

那么这个的话,这个是不是客户端状态?是不是?嗯,客户端这个状态,那么他这个 SYN_SENT()他调用哪函数了?同学们,是不是connect,这个你得能够知道。好,再看。那么这个,这像这两个状态是不是都是一个中间状态?嗯,你是不是捕获不到?很难看到?因为速度非常快,是不是速度非常快?好,那么还有一个最后的话,最后链接建立好以后,是不是同学们,处于什么状态? 当三次握手,握手完成后,是吧?都处于什么状态?都处于ESTABLISHED状态,是不是这样的?都处于这状态?

好,这是这个,那么这一块这个 3 次握手完成以后,那么接下来的操作 我们完全清楚吧?是不是该进行数据传输了。嗯,那么大家注意,处于数据传输这个时候,这个期间他们的状态是不会有变化的。

其实都处于什么状态?连接状态, establish 状态,是不是 establish 状态?那这个其实咱们是可以测的。怎么测?我这是不是发个包就可以了?你好,是不是现在是不是发出去了?变了没有?看是不是还是这状态?同学们,状态没有变化,状态没变化,这个你要知道,这个我就不再重点说了,这里面我得给你,你只要知道这句话就可以了。数据传输过程中,那么状态都什么呀?发生变化都是什么状态?都是ESTABLISHED的状态。

第三个是这个怎样?四次挥手断开链接的过程吧? 看图。那么这个 4 的回收过程我们都清楚了。那么不清楚的是什么呀?这个状态你没见过,

咱们跟你说一下。好,那么如果说是客户端主动关闭链接,他是不是要主动发起断开链接的请求?那么他所以他发了一个什么呀?fin标识吧, 这时候你这个序号是x+2,这个 x+2,是不是?上一次他给你发的ACK啊?,上次给你发的ACK是不是x 加1啊 , x 加2?大家看这个图,这个序号是 x 加1,这是 x+2啊,这个里面其实有一点他没画出来,这里面他应该是携带了一个字节长的数据,是不是这样的?他携带一个自己一个字节长的数据之后,那么服务端给他发回来的ACK是不是就x+2了?是不是?快接着看,这你得知道,我后面给你这个,咱们讲咱们那个,我给大家发在文档上,后面给你写了,跟你说了,注意上有这个好,那么这个序号是x 加 1 发的这个这数据传输部分。

那这一块也就是说你客户端要给服务端发送一个 FIN 请求,是不是?那么同时是不是也给他回一个 ACK啊?同学们,这个时候他自己处于另一个状态叫FIN_WAIT_1,这叫 FIN_WAIT_1 这么一个状态, 这个状态是这个内核在设计时候设计好的,这个你要问为什么,这也不好说哈,人就这么设计的好。

那么这个服务端收到他的这个关闭连接的请求以后呢,这个服务端就处于了 close wait 啊, close wait 叫CLOSE_WAIT 好,到这儿为止,到这为止,那么这个服务端是不是要给他回一个ACK?是吧?你们回一个ACK,这个时候客户端收到服务端发过来的 ACK 以后,他自己就处于了 FN_WAIT_2,这个状态和这个状态是不是都是中央状态?是不是都是中央状态?你自己想观察的还不好观察到好,那么到这为止,到这个部分为止,同学们,到这为止,是吧?同学们,是不是我们认为这个半关闭已经完成了,是吧?同学们,半关闭是不是关闭一半链接?好,接着看。

那么这个服务端是不是也要主动给他发起一个关闭链接请求啊?那么他自己,他自己处于 LAST_ACK 大家注意,这个是他最后一次往给点发,所以这个是LAST_ACK ,那么大家想一下他调用哪个函数close啊?你就说他主关闭就可以了,是不是这样的?是关闭?关闭。

咱想一下调用 close 会真正的关闭吗?会吗同学们?你要注意,在这个进程里面调用 close 不会,不一定会使链接真正关闭,只是说使它的引用计数是不是减一啊,还记得吗?大家记得我们那个父子进程里面是不是要关掉一个不用的那个文件描述符了,就这意思,你要搞清楚,在这你就认为它是关闭就可以了,好吧?关闭,然后这个服务客户端收到了这个服务端发过来的这个关闭链接请求以后,他自自己就处于TIME_WAIT的状态,这个是一个最后状态,大家还记得吗?这个状态是不是会持续一点时间?我是不是跟你说过大概可能是什么 30 秒到一分钟,因为是不是我们在启动?是不是bind  error 了?还记得吗?对,就是由有这个状态存在造成的,我们可以解决这个问题。好,那么当这个客户他收到这个他的关闭请求以后,最后他是不是要记得给这个服务端发送一个ACK?同学们是不是发送ACK,那这个时候就链接就真正关闭了。

好,大家想一下,我给大家出一个题目,出一题目,假使说你这个进程现在处于了FIN_WAIT这个状态,那么你说发生了什么事情啊?我的话听懂了没有?假使说你这个进程是吧?同学们,我们是不是有一个服务端?客户端,假如说我们这个服务端,就说服务端现在它的状态是处于了这个FIN_WAIT这个状态,那么你说是什么情况发生了?是不是你给对方发了什么呀?关闭链接请求了?是不是对方没给你发?对方没有调用close,是不是给你发?这个时候你就处于了什么状态?是不是 FIN_WAIT了,知道什么意思嘛?那么如果说你最后在这里,如果说你最后处于了是TIME_WAIT的状态,那么一定是对方给你发了,能理解吗?而且这个时间大概会持续半分钟到 1 分钟时间,能理解问题吗?好,我再问一个问题,你说这个台位的TIME_WAIT这个状态出现在哪一方啊,就一定出现在客户端这一方吗?服务端能不能处于这个状态啊?可以,谁先关谁处理状态,是不是叫主动关闭  谁先主动发起关闭的,那么谁就处于这状态,能理解嘛?,就这个。

好,那么下面我给你写两句关于这里面的状态,你得知道这么几个,一个叫什么呀?客户端来说有一个FIN_WAIT_T  FIN_WAIT_2  TIME_WAIT, 那么对于这个服务端来说,它都处于什么状态?同学们,什么什么状态?,那么这个服务,这个就是说这个,你得知道这个是不是主动关闭方啊(FIN_WAIT_T  FIN_WAIT_2  TIME_WAIT)?是不是主动关闭方?那后面是这个是不是被动关闭方,那么这个主动关闭方和被动关闭方是不是这个客户端服务都有可能?是不是?你得知道,你不要这个老是认为什么呀?都是客户端先关闭链接的,那是不可能的,是不是?我觉得最直接的例子是不是服务端也可以把某一个客户端给踢掉,或者拒绝某一个服务,是不是也可以?这个时候是不是服务端相当于主动完毕的?嗯,好。

那么对于被动关闭方这边来说,是不是第一个它处于CLOSE_WAIT?是不是一个?最后一个它是不是要处于 LAST_ACK吧 ?是不是?好,这整个的过程就这样子?好,那么这个我先跟你说到这,先跟你说到这,

这个状态,你能够,如果说你能把那个 3 次握手过程和 4 次挥手过程能够理解了以后呢,这个无非就是加了几个状态而已,并不难。并不难,你现在也要,也不要求你把它给记住,以前给它捋透了,咱面试之前你把这个翻一翻,人家问的时候,你能答出来就可以。

好图,也就是我们这讲义里面有一个大图,是吧?你们这个图你可能看起来比较费劲,我告诉你一个绝招,怎么看?那么你看这个黑线的时候你就不要看虚线,能理解吗?你看黑线的时候就不要看虚线,那么这黑线什么意思?主动方,清楚了吗?是主动方,是不是主动打开?同学们看,没,这个虚线,是被动打开。那么你看虚线的时候你就不要看。什么呀?不要看实线。

那么这两个图我给大家这儿给你列出来两个图,你,你看,你先把这个完完全全的给看透,你再去看这个,理解什么意思吗?最后如果说你这个图,如果说你看见实在费劲,那么你别不要看它了,你把这个图给理解透了。右边的图理解什么意思?这两个图其实表达的意思是一样的,只不过是什么呀?对,右边,左边这个图是不是相当于右边这个图的另外一种表达形式理解吗?这个不说了,那么接下来咱们一块把这个图从头到尾给你画一遍。行吧,从头给你画一遍,你注意看,你注意看。

好,咱们呢?大家还记得上面那个图 1 什么是斜线吗?什么原因?同学们,那个是不是有时间有延迟?是不是延迟?是不是你建立连接需要时间的啊,同学们,建立连接是需要时间的。好,你看我画一下。首先你得知道这个图一端叫什么端客户端,那么一端叫服务端吧,叫服务端,两端客户端,服务端。那么首先我们这个图一共分三部分,3、建立链接过程,收发数据过程,是不是数据传输过程,还有一个是断开链接的 4 次挥手过程,咱们一点点来,首先看一下怎么样 3 次握手过程,这个 3 次握手是不是就是建立连接过程啊?嗯,建立连接过程。

好,那么看一下在这个过程里面是不是发起端是谁?同学们,发起方是不是客户端?客户端先主动发起建立链接的过程。这个是这样的,这第一步,第一步。好,那么看一下,那么你说这个客户端是吧?同学们,这客户端他建立链接的时候,他是不是要发一个什么呀?什么 ?SYN请求吧,是不是?那比如说这个序号是 x 可以吗? 现在数据长度,比如说是0,没有数据我不写就没有好。好,那么他自己的话调用哪个函数会发起一个这个链接过程发起一个连接请求啊?是不是connect?注意这有一个connect,是不是俩函数了?那么他自己处于什么状态?同学们,SYN_SENT ,是不是这个状态?同学们,这你知道就可以了。

好,那么这个服务端它是不是收到这个客户端发过来的链接请求之后,他是不是首先要同意啊?是不是同意?然后他自己是不是要回一个状态,是吧?咱们先看一下这个服务端状态,那么一开始的话,这个服务端处于什么状态?同学们,一开始是不close,你把它认为一个初状态,好吧?那么程序在没有启动的时候它是close,启动起来之后它是什么?什么状态?是不是LISTEN,那么调用哪函数这个啊?是不是Listen(),Listen函数, 放这,是不是这是他一开始状态,同学们,是不是他?那么他收到这个客户端发过来链接请求之后,他处于什么状态?什么?是不是SYN_RCVD啊,这个状态是不是?好接着看,那么接下来这个服务端是不是要给他回一个什么呀?也回一个ACL,同时也要发起一个建立链接的这个请求吧,这一块咱们也说过了,好,那么这一块呢?所以这个服务端也要发起一个,好这个,那这怎么写?首先看一下这个SYN:y,咱们就给咱们那个讲义的保持一致。

,那么 ACKNE ?是不是 x 加1, n 加1?好,那这个时候大家注意,这个时候,那么这个客户端收到这个请求之后,他是不是也是同意啊,是吧?同意。那么然后他一看这个ACK发回来ACK也对,那这个时候是不是相当于这个这一块,这一部分链接是不是建好了?

相当于半链接?是不是建好了?这个它处于什么状态?是不是ESTABLISHED 状态ESTABLISHED  是不是这样的?好,那么这个客户服务端,服务端这个时候如果说仅仅是这个客户端收到了他的这个回应和什么呀?建立链接的请求,那么并不意味着服务端这边链接已经建好了,他必须要等一个什么回去,他必须要等客户端给他回一个ACK 回去,所以这还有一个是不是有个 ACK回去?

所以这儿还有一个 ACK  ACK值多少?是 y 加1, y 加1, y 加1, y 加1。好,这个时候大家注意这个时候他这处于什么状态?它这是不是就处于了ESTABLISHED 状态?到这为止以后是不是相当于链接建好了?大家注意,建立链接是一个双向的socket片,是一个 socket片,是不是?双向链接?关于这个 socket片的概念一定要搞清楚,因为你不理解这个的话,你后面就不好说。理解什么呀,(shatedang)那函数。那么只有这个双向链接是不是才有可能有半关闭的说法了?是不是?好,接下来咱们看一下啊,那么这个链接建好以后,接下来是不是进行数据传输了?数据传输过程。好,这个数据传输过程咱们看一下。首先第一个,大家知道数据传输过程当中并没有什么变化,并没有状态的变化吗?

好,这个是数据第一次传输,咱们就来个简单就可以了。那么首先看一下这个序号有多少呢?序号多少?我们把这个序号叫Seq,这我给起个名,多少?同学们,这个序号,你这个数据传输过程是不是他得看上次对方给他回的 ACK 上次对方给他回的ACK是 多少?是不是 x 加 1 样?所以这就x 加1,为了携带数据长度为1,可以吗?这个数据长度你自己定好,咱就来个最简单的1,你数据传输你肯定有数据传输,你不能说一个不发吧,是不是?好,接着再看,那么这个服务端收到这个数据之后,他是不是要给它回个ACK啊,当然他也可以发数据,也可以不发吧,是不是咱们就来一个什么他不发的,那么他要回一个。当然他是不是这也有ACK啊?同学们,ACK有多少?这个是不是他上次给对方回的?给到对方刚刚回的这个。好看后面,那么这个服务端收到这个发过来的数据之后,他首先要肯定要把这个数据读读走,读走之后他要给他回一个ACK,这个ACK多少?

是不是 x+2 了? 这有ACK吗?同学们,这个是ACK,是不是?同学们? ,那这个时候是不是将那数据传出功能结束了?对啊,我能给你画一条这个别的线,给你标识一下,上面的过程是数据传输过程。

这个建连接过程,是不是下边这个呢。他这个东西,这个线不允许你随便画是吧?好,咱们再来一个线,我把这个线给你标个别的颜色,喜欢绿色,

好,那么接着看,咱们看下面这个,当然这个的话,这个也给他来一个,画一个直线。行,咱换个颜色,可以吧,这样标识的清楚一些,清楚一些。好,接着我看到这个线,这个短了,再给你延长一点

把这个线动一动,这个有点短了,怎么回事?好,让大家见笑了。刚才。好,咱们看一下这个最后一个过程,什么过什么过程?是不是这个断开链接过程啊?四次挥手过程,是不是就是这个断开链接过程?好,看一下这个状态怎么回事?好,首先这个首先看一下这个主动关闭方,主动关闭方是不是既可以是服务端,也可以是客户端的,是不是?好,咱们看一下。那么就相当于咱们一开始先认为这个是客户端传输数据结束之后他就关闭链接了,可以咱们用这种颜色来表示。

好,他这个首先看一下他这个发起一个关闭连接请求之后呢,他自己是处于什么状态,哪个是不是 FIN_WAIT_1啊?它调用哪个函数啊,同学是不是close,你认为调用 close 之后,反正就是发起一个关闭链接的请求就可以了。好吧,但是调用 close 未必真的会发起,知道为什么吗?只要它的引用计数是不是大于1,是不是一开始你关的话它就不会发起?是不是只有说这个文件描述这个引用计数等于 1 的时候,你 close 的时候是不是才会变为 0啊?变为0 的时候才会往外发。这个咱们前面跟你说过了,好,他是不是发起一个关闭联请求?他是这个发的什么呀?FIN 序号多少?序号多少?同学们,是不是 x 加 2 了? x 加2,是吧? ACK是不是上次你给他回的ACK,你给对方回的 ACK吧 是吧?是不是y加 1 。

接着我们往下看,那么这个服务端收到了这个请求之后,他这边处于什么状态,什么状态?是不是  CLOSE_WAIT?  是不这个状态?好,接着看,接下来这个服务端也同意了,而且他是不是这个时候要给你回一个ACK,回来表示啊?表示是不是这个你的链接,你的关闭链接的请求,我知道了,是不是?我同意了?给你回个ACK 回来,好,接下来他要回一个ACK,好,接着看。

那么这儿它回一个ACK 以后,你说这儿它处于什么状态?什么状态?那么对这个客户端来说,它是不是就处于了一个FIN_WAIT_2这么一个状态啊?这是他处的一个状态。再接着看,这个时候大家注意 这个时候相当于是不是半关闭了一样?是的,是不是关闭掉一半了?因为我们的链接是不是双向的?现在已经关掉了一方了,关掉一方,接着往下看,那么你说有这个关是关掉哪一方?

链接是双向的吗?是不是他对应他?这有两头吗?你说这个是关的是这个客户端发送的一方,还是说接收的一方?反了,你客户端你是不是知道你,你现在是不是你?我认为我已经不按发的书,我知道我不发了,但是你收不收你能确定吗?是不是对方给你发,你还有可能收啊?所以这个关的是什么?同学们,客户的主动关闭的,这一定是关的他自己不发的那一方,他的发送方式对应的对应对方的什么呀?是不是接收方?这才是一个连接对的,是不是这样的?这不要搞翻了,那么这个就关闭了。

好,那么接下来这个服务,这个客户端是不是?这个服务端是不是仍然需要给他发一个关闭链接的请求啊,那么这样的话是不是这个?好,那么他发了这个关闭链接请求之后, 它处于什么状态?是不 LAST_ACK? LAST_ACK,是吧?是不是这样的?那么它是不是也得发起一个?当然这儿的话,第二步的话这个ACK有多少?多少?是不是 x 加3?是不是 x 加3?是不是 x 加2再加1?好,那么这个他这个他这往回发的时候,他这个序号是多少?多少?就是一序号的意思, 这个没有这个号,我给你标记了一下多少,是不是应该是这个是吧?多少? y 加1,是不是你这个发的这个序号,是不是上次对方给你回 ACK 啊?对啊,然后ACK有多少?是不是 x加3?这个东西你应该很快能反映出来了。这个咱们到时候你都画了一遍,是不是给你讲了两遍,你自己又画了一遍?在这里我跟你说一遍,第四遍了已经。好,那这个时候他是一种状态。什么什么状态?他是不是已经出来最后一个状态叫TIME_WAIT?这个状态,是吧?那么他是不是还得给他回一个呀?是不是还要回一个?来一个什么呀?ACK 是不是这样的?这还有吗?同学们,到最后其实最后就你认为它是处于什么状态? close 就可以了。

那么这个完事之后是不是也是处于close啊? 当然一开始的话是处于什么状态?一开始其实就是处于 close 清楚了吗?只不过这个状态是不是相当于一个你看不到的一个状态?没复制过来。

这个不好放,你们理解就可以了。这个图,大家注意,这个图要求你自己画一画,好吧,自己画一画,那么关于这个图这左边这个图你看的时候,大家注意你看的时候怎么看,我大概跟你说一说,还是那句话,你看实线的时候你就不要先看虚线了,否则你搞混了。这个图其实对这个图意思是一样的,只不过是这个图的另外一种表现形式而已,清楚了吗?这个我上课的时候就不再给你啰嗦了。那这个里面有一些这个是比较这个非常少见的,你也就不用看了,比如说RST,是吧?这还有这个,还有这种有些线的你就不用看了,你把我们讲的这些,讲这些常见的状态搞定了就可以了,是不是?搞定状态搞定就可以了。

那么大家再想一个问题,我再给大家说一下,那么你说我们这个图里面,比如这个四次挥手里面,是吧?同学们有没有可能说这个有 3 次这个过程啊,  3 次过程,这是有可能的。我举个例子,这种情况很少见,什么意思?有没有可能我客户端和这个服务端在同一个时刻同时关闭,有没有可能? 你这个服务端也关,我也关,那么你俩呢?赶在一块了,有没有可能?你不能说没有,这种情况比较少见,是不是?你不能说没有,是有的话那么其实这个 4 步就分成了3 步了,知道什么意思吗?在这个图上有体现在这同学体现那同时关闭的时候,你比如说你这个服务端也关闭,我这客户端也关闭,二三步就合在 1 步了,怎么合的?他给你回一个ACK,同时他也给你发一个 FIN 请求。

,那这样的话是不是就合成一步了?这种合成一步的情况是存在的,只不过是这种情况非常的少见,那么研究的时候呢,你研究的时候这种非常少见的情况呢,你就可以不用过多去关注了,是不是?可能一万次,几万次,甚至十几万次也不会出现?那么偶尔一次是吧?啊,好,那么这个呢?这个图我就给你一块来画在这,好,画在这,好,说到这,

嗯,就到咱们的一个总结了。第一个是这样的,怎么思考题呢?第一个 SYN_SENT这个状态是吧?同学们,出现在哪一方,哪一方,是不是这个应该是客户端的啊?这个你得知道?第二个,SYN_RCVD这个状态出现在哪一方?这是不是出现在服务端啊。

是不是?第三个TIME_WAIT 状态出现在哪一方,这怎么说?这你还能说是客户端服务端吗?不是,怎么说?主动关闭的意思,这个你知道,主动关闭方?是吧?同学们,还有别的吗?你能想到的问题什么呀?咱们这LISTEN就不用说了,肯定是什么呀?肯定是服务端,是不是还有一点跟你说一点,就是说在数据,数据传输的时候是没有状态变化,是不是?那么大家还记得咱们这个在这个做测试的时候,有的时候会报一个错,binderror,还记得吗?是吧?这个 address already in use 是不是正在被占用?那么这种情况下你说是这个什么怎么发生的呀?如果让你模拟一下,你模拟的出来吗?怎么拟?

断谁?我服务端后那都开起来了,是不是?数据完也完成通信了?这个时候你说断谁就会出现这种情况?

搞清楚啊,不要,不要断错了,断服务端才有这种情况,我给验证一下看是不是这样的,但是我这个代码里面可能就有这个东西。

咱们就看咱们那头写代码这个里面的话,比如说这个零一这个里面,这个代码里面是不是我没有?我们没有设置这个端口复用技术,没有设置。没设置,咱们启动起来,启动起来,然后 NC 一下,现在你断开有用吗?现在是没有链接啊,是吧同学们,现在断开我再启动能起来吗?同学们,能起来的,它是不是?因为什么?因为没有链接嘛,快起来?是发过来了,现在没事吧?好,我现在主动关闭这个。你这样同学,我先给你测一下这个,我现在主动关闭客户端,看看有没有那种情况出现,是不是还可以启动起来啊。好,那这个时候咱们这样启动,启动现在发送数据是不是相当于链接建立好了,数据发过来了。嗯,现在再断开,报错了,发生的情况怎么产生的?同学们,总结一下, TIME_WAIT 是如何出现的?那么你的测试你就知道这个过程是不是这样的。

这个启动服务端,是不是启动什么呀?客户的这两者是不是都启动了,连接启动之后是不是这个连接建好了?这个,嗯,链接建好,而且也可以。什么呀?是不正常发送数据啊,这谁给谁发都无所谓,你客户端也可以给服务端发,是不是?服务端也可以给客户端发,总之链接是好使的,是不是?那么然后,然后我关闭,谁关闭?然后先关闭服务端,是不是?同学们?然后先关闭服务端,因为你这个服务端一完成,客户端是不是跟着结束了?就是吧,先关闭服务端就会出现,那么这个服务端就会出现TIME_WAIT状态,其实这个的话咱其实不这样说,你应该也知道吧,因为这个只要是你这个程序,这个服务端,你这个状态是TIME_WAIT的状态的时候,那么你再启动它也启动不了,是不是这样的?那么好,这个咱们都知道了,那么这个是不是有一个持续的时间呢?还记得吗?我是不是跟你说了,大概持续可能是半分钟到这一分钟的时间吧,是不是然后你再启动它就报错了?好,这里面有一个点,你需要知道为什么会需要这个事情,需要那个时间,叫什么呀? 2MSL

把这个给你讲一下,,这个什么叫MSL呢?看这个讲义,这叫最大生存。什么呀?这没写,叫最大生存时间节这么一个点,一个相当一个时间段,你把这个 MSL 理解成一个时间就可以了,就是一个时间段,是吧?同学们,那么下面我跟你说一下,为什么说需要这么一个时间,清楚了吗?要跟你说的这个问题,2MSL。

好,咱们看第一个原因,第一原因,你看这个图,是吧同学们,从这个图上我们看一下,那么这个第三次挥手和第四次挥手是不是相当于关掉最后一半链接到这个产生,那样,是吧?先看这个LAST_ACK这个状态,那么最后一个是不是应该是最后一块链接的话,是不是相当于这个被动管理方主动发起的,是不是?这个这一块链接是不是被动方,被动管理方主动发起的?那咱想一下有没有这种情况发生?那么这个当然我们这个里面这是不是相当于服务端啊?这是服务端,那么服务端在给他发送关闭链接请求的时候,有没有可能我这个客户端不给他回ACK,或者回了时间超长了,超时了,或者网络有异常等等?他总之他这没有收到,有没有可能?有没有可能有,这有可能的。

那你想你举个例子,你打电话的时候,你打电话是不是你这说话人对方听不见?是不是?有可能是网络异常?是不是?也就说呢?最后一次,最后一次这个服务端给客户端发这个关闭连接请求的时候他也收到了,是不是?然后他没有给他回ACK,或者回了他没收到?这都有可能,当然这是一种情况,那么有没有可能这种情况他发了他压根也没收到,有没有可能?这时候他是不是也跟他回不了ACK?那么回不了ACK是不是这一半块连接是不是就关闭不了?能理解吗?那么需要这个时间有什么好处呢?需要这时间有什么好处?这个时间是干这个用的,那么假使说这个最后一次ACK ,你不管是他到底有没有收到这个请求,还是没收到,是吧?最后一次 ACK 并没有给对方发过去,那么在这个 2MSL 这个时间段内呢,那么有可能这个这一方他会再次发送一个 FN请求,这什么意思?相当于补发吧?是不是相当于补发,那他给你补发,那补发之后补发以后,那么这样的话,这个客户端就有最大的可能把这个ACK 再给你发过去。

如果没有这个时间了,你比如说举个例子,那么你这儿发了ACK ,发了个ACK 请求过来,他那收到之后他另关掉了,但是这个ACK 确实没有回来,但是他进程结束了。那进程结束是不是所有的资源全部回收了?包括内核资源是不是全部回收了?那么你说他还有可能收到 ACK 吗?是不是没有可能了?那所以呢?第一个原因是什么呀?

第一个原因是, 也就是说让这个 ACK 呢?最大可能限度的发回去是不是?当然正常的话无所谓,那么异常的情况下是不是他会再次发送一个请求过来以后呢?,那么这个客户端会给他回一个ACK回去,那这样的话就它就可以收到这 ACK 了,这个时候这个链接是不是才真正被关闭了?这是原因。能搞清楚这个意思吗?相当于什么呀?这个的意思相当于说的简单一点。什么呀?就这意思,就说你提供这个时间是吧?提供这时间就让这个 ACK 有最大的可能让对方收到,知道什么意思嘛?有最大的可能让他收到。当然有没有可能在这个时间段也收不到啊?也有吧,但是你提供了这么一个时间,是不是这种可能就变小了?能理解吗?这是第一个原因,

看第二个原因,第二原因就有点不太好理解了,你听一听,那为了保证在这个时间内不能启动相同的 socket 片,咱们都知道什么叫 socket 片了吧,那么一个链接是不是相当一个链接对了啊?是吧?一个链接对,那么大家都知道这TIME_WAIT一定是出现在主动关闭的地方,这个没问题吧。

那么也就是说 2MSL 是针对主动官方一来一方来说的,这大家都知道,那么由于 TCP 可能会存在丢包重传,有没有可能?那比如说你发的包对方没收到,是不是你就收不到他这个ACK啊?你收不到ACK,你认为他就丢包了,那么你是不是给他补发?给他补发?那么如果丢包重传发给了已经断开链接之后相同的socket片,这句话怎么理解?看看这,丢包重传发给了链接断开之后相同的socket片,也就是说你原来那些是不是断开了?那后来是不是又建了新的链接?这个链接和上面链接完全一样。那什么叫这个链接完全一样啊?你这两边用的档口是不是完全一样啊。这个时候叫相同的socket片。

那么这个时候呢,你上次发完的包会经过这个新的链接发给对方,这句话能理解什么意思吗?如果会出现这种情况,可能会对以后的业务造成影响。大家想象我们那个粘包,粘包什么意思?一次发送的数据包是不是有可能会让对方两次收完了?这个是不是会产粘包了?那么你这个的话相当于什么呀?你想你两次发送的数据包,他是不是一次收了?有没有这种情况?同学们,这种情况存在的,那么这种情况有可能会对后续的逻辑造成影响,知道什么意思吗。

当然这种情况大家想一下,这个如果让你模拟一个相同的socket 片,你怎么模拟?其实也好模拟你把客户端给他绑定端口,是不是也可以啊?知道什么意思吗,你客户端绑定端口,你服务端也绑定端口,那这个时候你如果说这个链接关掉的时候,你再迅速启动起来,这个时候有可能会造成什么样?有相同socket片,当然它可能不让你启动,知道意思吗?它可能就不让你启动,能理解吗?其实这些东西都可以避免,因为这个在设计的时候像这些东西都考虑到了,那怎么避免?怎么避免这个问题,首先这句话听懂了没有?同学们,

说一下,这个是发送方,这是接收方是吧?同学们,那么他在发送的,他把数据是不是发送到网络上去了,是吧?发送到网络区域,当然你这个严格说不用这么画,我给你画一下,这是网络是吧?是吧同学们,不是发这来啊?发送方,接收方,是吧?发送方,接收方,那这链接的话,你认为,反正这个链接减小之后,你认为有链接了,是不是?那么发送完以后,这个链接这个异常了,变异常了,那么下次他又建好链接了,比如说建链接,是吧同学们,建链接,那么上次发送出去有可能会沿着这个新的链接会传送到这个接收方。

意思明白了没有?刚刚那个链接断了吧?是不是?这个时候有一个新的链接?但是你刚刚发送数据包是不是还在网络当中啊?

那这个时候你这个新的链接就让这个原来那个数据包就沿着新的链接发过来了,这个时候就对后续的这个数据造成影响了,知道什么意思吗?因为如果是相同的 socket 片,它是不是也就意味着一定是相同的这个缓冲区啊,能理解吗?是不是这样的?这个是不是叫socket 片啊?同学,是不是这就相当于socket 片,那么原来那个断开了,现在比如这个,这个已经断开了,是吧同学们,这个就建好了,那么原来本来这个数据包该是沿这个沿着这个链接发过来,后来他沿这个数据发过来了,那么后续一再发包的话是不是相当于这个会产生这个?比如说最小的是不是两次?你自己传了一次,上次还有一次啊?到这他是不是接两次?他是把这个包全接过来了,就会产生这种情况?这是这个,那么这个如何避免?很多操作系统在实现的时候,只要端口被占用,服务就不能启动,这个是不是咱们看到效果了?你这个进程的状态,你这个服务的状态,只要是处于TIME_WAIT的状态,那么你这个服务在启动的时候它就会给你报错 binderror 叫 address already in use,是不是这样的好?但实际上我们在真正在用的时候,有的时候,有的时候,我们是这个不应该让我们的服务在这个 2MSL 这个时间段内,让它起不起,让它启动不起来。

我举个例子,像这个阿里巴巴的服务,像这个什么什么什么淘宝什么一些电商,是吧同学们?对外提供服务的这种,还有什么京东啊等等这些。那么你说他停那么一分钟或一个小时得浪费多少钱?特别是在这个双十一的时候, 618 的时候 是不是损失更大啊,是不是?他肯定说这种情况肯定是不能让他出现,是吧?好,那我们如何去解决这个问题?朋友们,我们如何去解决?接下来就给你该说什么呢?该给你说这个了,端口复用这个问题了,也就是要用到端口复用了,这如何去解决?这个端口复用也很简单,就一个函数结束。

好,关于这一块,这个,关于这个为什么要用这个 2 MSL?这个时间呢,你把这两个原因看一看,把这个原因看一看,这个上面可以描述的很清楚,你自己看一看。一个第一个原因是不是第一个原因是在这个 2MSL 这个时间内,让最后一个 ACK 是不是最大可能的发送出去啊?是不是这样的?

那么第二个原因是什么呀?第二个原因是如果说这个原来的链接出现异常了,那原来的也发包了,是不是也发数据过去了?但实际上对方没收到,那这个时候如果说后来又新建了一个新的链接,这个链接和原来的链接一模一样,原来那个包可能顺着这个新的链接发过来,主要是这种情况造成的,清楚了吗?这两个原因也跟你说清楚了。然后这个如何解决这个问题?下面就是端口复用,如何使用这个端口复用来解决这个问题?你不可能说你这个正常情况下,比如说我们这个程序,我服务端关闭了再启动的时候,你不可能让他等 1 分钟才启动起来吧?这个时间 大家注意,这个时间的不同的操作系统可能时间不一样。清楚了吗,unix,他可能给 linux,不一样。

windows下 ,大家想一下,windows是有这个问题吗?windows是可能也存在这个问题,清楚吗?但是不管怎么着,大家再想一个问题,我两个进程能不能绑定同一个端口啊?两个进程,两个服务,两个网络服务能绑定同一个端口吗?一定要注意这个不能的,这是不能的,你你,你试试,不行,你把一个你,你这么的,我给你绑一个,你把你的端口绑成21 22,你试试21 22是不是被系统占用了?对,你启动不起来,清楚了吗?它会报什么呀?这个端口已经被占用了,清楚了吗?这个问题。

还有大家想一下这个,你说这个两一条链,这个新的链接和原来老的链接,那么出现这种这个相同的,相同socket片的,这种概率高吗?同学们,我问大家,你的客户端你用绑定吗?客户端用调用bind函数吗?不用。大家注意,不是说它不能,而是说它没有必要调用。如果你绑定了,这种出现概率是不是就高了?那么如果你不绑定的话,这个端口是不是内核随机分配啊?那么你刚才使用那块那个端口,这个时候你再分配的话,肯定不是同一个端口,是不是?这样的话也可以避免这个相同的socket片了,是不是这样的?你总之有些东西是你写程序的,可能是出现的问题造成的,但实际上,就客户端来说,它每一次使用相同的端口的情况是非常少见的,是不是这样的?当然你这个服务端是不是你用的都是通讯端口?是不是你绑定,因为你绑定了嘛是不是?好,那么关于这一块,为什么说要使用这个 2MSL 这个时间把这两句话多读两遍,好,多读两遍。

这个你服务端在和客户端进行通信的时候,如果说你服务端主动关闭了,你再启动的时候是不是会报错啊?binderror,就是 Aries address already use 会报这么一个错误,为了让我们这个服务在启动的时候不报这个错误,能够顺利启动起来,我们需要在我们这个代码里面加上一句话,哪个呢?叫 set sock opt这个函数?set sock opt,那么 set 是这个意思, sock 就是socket,就 socket 文件描述符,opt 是选项,是不是设置sock 选项的意思啊。

关于这个函数怎么用呢?你看这个帮助的时候他并没有写,你看一下 set sock o p t,是吧?同学们?他写的不是这个非常的详细,那我们在用的时候可能也不太好用,怎么办?关于这个,这个你看看这个,咱们这个unix环境高级编程第二版,这个我给大家找了一下,这个书我发给你们了,这是不是有一个 set sock off 的这个函数了?那么你看一下这个函数,看它的描述参数, level 标识了选项应用的协议。

第一个参数你应该清楚什么意思吧?这个参数用在谁呀用的这是,是不是应该是监听的文件描述符啊?大家注意,你在这不能使用什么呀?通信的文件描述符,不要搞混了。这个用的是监听的文件描述,分好第二个level,这个 level 怎么用?看一下如果选项是通用的套截字层选项套截字是不是就是 socket 创建 socket 返回的那个文件描述符了啊?嗯,是吧同学们,好level设置成SOL_SOCKET。现在这个是不是又找这个参数是不是又找着了?你就用SOL_SOCKET就可以了。

第三个参数,int option, option 看这个怎么用?关于这个和后面那些,你看后面,这是选项吗?同学们,是吧?我看看这个是option, option 是不是选项的意思啊,这个套接字选项,是不是这些?选项有这么些个,有这么些个,那么有同学说这么多,到底该用哪个?你先观察一下?你观察,如果你猜的话,你看这个,如果说帮助他写的不是十分的全面,如果让你用的话,你怎么用?明白马,你怎么用?你是不是要仔细观察一下,看哪个可能对象去试啊?是不是这样的?好,那么你看一下哪个像啊?同学们,这个像吗,就是,有同学一眼就看出来了,这个观察能力还是非常的强悍的。

是不是这个,这是 SO_REUSEADDR什么意思? REUSE是重用的意向,是不是地址重用的意思啊?嗯,能理解吗?那么你想一下,那个bind报的错,是不是 already use 是不是正在被使用?对,其实那个进程是不是我们也知道已经死掉了吧?已经死掉了。好,那么看一下这个选项,这个option,你就知道要填这个了。好,再看,那么这个参数 v e l 你该选哪个?是不是他告诉你该选一个int类型的值啊?是不是同学们,你应该选一个 in 它类型的值。

接着看后面这个描述,如果* v a l 非 0 重用bind中的地址,那言外之意,那么你在用的时候,你这个值是不能填0啊,你得填非 0 值,是不是非 0 值?你得填非 0 值?好,其实除了这个之后之外,还有一个可以用叫RESUE,有一个叫 RESUE port,我看看有吗?没有没有,你不用管他了,那个是这个是我们最常见的,那么用的时候怎么用?第一个,首先这个咱们知道什么意思了吧,是不是?好,还有一个是SOL_SOCKET吧,这个是不是刚才给你看过了?好。

第三个 SO_RESUEADDR,是不是就是刚才我们看那个 option 选项啊?SO_RESUEADDR。那么接下来这个val,这个 val是不是就这个呀?就是这个它对应的是不是就这个?是不是?这个?同学们,val这个你应该选一个什么值呢?应该选一个int的类型的值,然后把那地址是不是填过来就可以了? int的类型值这个,那最后一个,这一个描述一说清楚了,对吧?如果非 0 重用bind这种地址,所以你用的时候要用一个非 0 值,你只要不填 0 就可以。

好看,后面还有吗?后面是不是一个长度?这个长度指的谁的长度?这个长度指的这个长度,大家想想为什么在这用了个void*啊,为什么呀?因为你这个 val 它的类型是不是不都是Int?是不是还有其他类型来看了没? 所以在这用void *是不是一个万能指针呐?万能指针,好,那么这个函数我给大家说一下,就这个意思,那么接下来咱们看一下到底怎么用?咱这个的话,我看这个 set source off 和这个 get source off 是不是他的这东西一样的,是不是一样的?那么如果你再不会,你看后面有没有这个人家写过的,看例子叫setsockopt吧。

第一个 fd 是不是就是创建socket返回文件描述了?这个文件描述一定是一个监听文件描述,是不是这样的?接下来是不是SOL_SOCKET啊?第三个是不是SO_REUSEADDR,是吧?接来他是不是用的是 reuse的一个地址啊,

他赋的值是几啊?同学们是不是1啊?他赋的值是1没问题吧。好,后面他用的是这个,是不是这个参数长度?这个参数的长度,很显然是不是sizeof(int) 就可以了?这个函数的用法并不难。好,那么关于这个函数咱们给你找一个,咱把这个给你加上一句话可以吗?咱们给他加在哪加哪?同学们,你如果这句话加的话加在哪?同学们?这个错谁报的?bind 是bind报的 ,是不是应该设置bind之上啊?对,你设个bind之后还有用吗?是不是你要搞清楚。

设置端口复用怎么设置?首先是不是定义一个变量啊?同学们,setsockopt 第一参数是不是lfd,第二参数SOL_SOCKET?第三参数呢?记不住,看的是不是SO_REUSEADDR,第四个呢opt,第五个是不是这个长度 sizeof(int)就可以了,就这么用就可以了。那咱们看一下效果,起来发一下,没问题,然后我再关闭再启动。嗯,没编译怎么,是不是没编译 g c -o   server,然后这个零一是吧?然后是 rap 点c。

我把它删掉重新编?没问题吧。

 启动,再来一次,发了吧,发完之后,然后这个我关闭再启动,没问题吧。好,那么这个函数的用法也就非常简单,咱们一块给你写一下。那我问大家,如果说你在写代码,这个函数怎么用?忘了怎么办?

你记住,像这样代码只要是在这个服务端程序里面一定有,你直接一搜,看人家怎么用的,直接扒过来就可以了,清楚了吗?直接扒过来,好,setsockopt第一个参数。第一个参数是什么?是不是监听描述符了?第二个SOL_SOCKET,第三个参数 SO_REUSEADDR 第四参数,是不是 opt啊?后面呢 sizeof(int)?清楚了吗?你这个如果不会没有关系,但是你得知道有这么回事,那你查的时候你看看,别人用的直接扒过来就可以了,就这么简单清楚了吗?不要什么都靠记啊。int opt=1,这个值是不是得必须得付非 0的值啊?人家已经告诉你了,你像这种你能看明白是不是告诉你了,哪能看,是不是这个啊,我看是不是这个。

如果*var,这个 var是不是有指针?同学们,指针前面加个*是不是取值的意思?间接引用 那么重用bind主动地址,我们的目的是不是要重用?对,我目的要重用,那么其实还有一个这个也可以,其实哪个这个其实也是可以的,叫 SO_REUSEPORT也行,但是我们用的时候大家注意,我们用的时候用这个非常多,这种见的不太多见的不太多。如果说你见到别人用这个了,你要知道怎么回事就可以了,我把这给你换一下试试看行不行?

那咱们呢?咱们把这个,把这个换成 SO_REUSEPORT?你看 SO_REUSEPORT看行不行?起来,起来是不是发过来了?然后关闭再启动是不是也没事了?这两个都行,但是这俩用的时候 大家注意 这俩用的时候这个是用的居多 SO_REUSEADDR,清楚了吗?这个用的居多,这个用的比较少见 SO_REUSEPORT,但是你如果看了之后不要说不知道。好,这个就跟你说到这儿。

好的,接着咱们讲一下这个半关闭,这个半关闭这个咱们都知道,我们这个,嗯,链接是不是一个socket片啊,所以这个就有一个半关闭的这个说法好,看一下什么叫半关闭?如果有一方close,另一方没有close,那么我们认为它就是一个半关闭状态,那么到我们这个图上,在这里,到我们这个图上,那么是不是相当于它这个意思这一块关闭了,是吧?是吧同学们,是不是这一块没关闭啊,它是不是就是一个半关闭?这意思明白就可以了。

那么处于半关闭状态的时候是可以接收数据的,但是不能发送,这个现在理解了没有?同学们,你看客户端主动给服务端发起一个关闭链接的请求以后,那么相当于是不是客户帮接下来他不发了?他收吗,但是他收不收,那要看对方给他发不发吧,所以他不发这个是他主动的行为,他自己明白是不是?但是他收不收是被动的,是吧?被动的。

那么现在这个这什么意思?就是相当于他不发了,相当于他是不是把自己的这个内核这个发送缓冲区给关了,理解吗?同学们,因为这个你只要一把这块缓冲区关闭了,是不是他就写不了了?是不是写不了了?大家注意半关闭一定出现在主动关闭的地方,这能理解吗?是不是主动关闭的一方,你主动关闭了,你主动关闭那就意味着你的内核的这个什么呀?发送方是不是关闭了?但是你这个什么呀?你接收方是不是还有啊,是不是?是不是还可以接收数据 啊?这是合理的,对吧?这是合理的。好,这是半关闭的概念。

好,那么给这个半关闭对应的有一个函数叫 shutdown这个函数呢它可以实现半关闭。这个函数我给大家看一下,这个函数其实用起来很简单,这个也是一个命令,所以要用man 2 吧?看一下这个函数。首先这个头文件是不是要有啊,是吧?看一下shutdown函数有一个文件描述,这个很明显,大家想一下这个文件描述应该什么文件描述啊,是不是监听的?嗯,好,接着看。第二是how,那么你看一下 how 的取值有哪些?if how is SHUT_RD什么意思? RD 是不是read啊? further 什么意思? receptions will be disallowed.什么意思?

father 是进一步的意思,进一步的意思。 receptions 是不是接收的意思?大家记得这个收条什么意思吗?收条就是reception,收据,是不是有个跟这个差不多词了? receive 是不是接收的意思? receive 是吧? reception 是 receive 的一个名词形式,能理解吗? will be disallowed什么意思? disallowed首先allowed 什么意思?运行 disallowed不允许。

able 什么意思? able  a b l e 可能 ,disable 呢?不能,是不是也有残疾人的意思?好,看第二个 if how is WHUT_WR further transmission will be disallowed 什么意思?如果你是这个how ,用的是SHUT_WR,那么进一步的数据传输被什么呀?是不允许的,也就是你不能再往外发了吧,是不是?好,那么这是两个,这个是不是相当于关闭两头了?是不是关闭两端?一个是关闭写端,一个是关闭读端?好,接着看 if how is SHUT_RDWR? 是不是关闭读写两端啊?

further receptions and transmissions will be disallowed.

是不是相当于调用了close?同学们能理解?相当于是不是双向链接都关闭了?相当于这个链接是彻底关闭了。明白了嘛,同学们,一个是只关闭读端,一个是只关闭写端,那么第三种情况是不是全关闭?好,这个看它的返回值,成功返回0,失败反复1,并设置 lerrno吧,是吧?这个好,那么这两个函数有什么区别?这个shutdown和 close 有什么区别?这 close 是不是也是关闭的意思?那shutdown也是关闭的意思?首先第一个区别,你得知道我这给你写在外面了。

首先这个shutdown 可以什么呀?可以实现半关闭对吧?这个意思什么意思吗?你是不是可以关闭读端,也可以关闭写端啊?这是第一个,第二个,当然这个close不行吧,同学们,好,第二个 shutdown 关闭,关闭的时候不考虑什么呀?这个文件描述符的引用计数,不考虑文件描述符的引用计数,那么只要用就,只要一调用他就关闭,清楚了吗?那么是直接彻底关闭,好close呢,大家想一下, close 在关闭的时候它是它一定能关闭吗?是吧同学们, close 考虑文件描述符的引用计数,那调用一次 close 什么意思?只是什么呀?同学们,只是将引用计数减1,是吧?同学们,只有什么样?只有减小到 0 的时候。才会怎么样?真正关闭。

那么我给大家举个例子,再想一下,那么什么情况下这个引用计数可以不为1啊,你给我举两个例子我听听。咱们都讲过好几次了,你父进程创建一个子进程,那么如果说父进程里面有一个文艺描述,这个文艺描述是不是可以被子进程继承下来?嗯,那这个时候这个引用计数是不是变成2了,对,是吧同学们?这个时候你在子进程里面关闭之后,那么父进程里面是不是还可以用啊?这是一种情况,再一种情况,咱前面是不是讲过dup啊,同学们dup或dup2函数是不是也可以?那复制出一个来以后这个引用计数是不是要加1啊,你调用一次加1,那你close之后是不是减1?当减小到 0 的时候是不是才会真正的被关闭?关于这一点咱们都知道,是不是?这是我们经常用到的。那么我再问一个,我主线程,我创建一个子线程以后,我子线程关闭,这个可以吗?不行,不行。为什么呀?子线是共享的,你关闭之后是彻底关闭了。

那个引用计数,是不是为1啊?它和这个 folk 还有这个dup它是不一样的,嗯,这你要搞清楚,不要搞混了。好,这是它俩的区别,叫shutdown,这个能理解吗?同学们能理解吗?这个shutdown这个函数你自己可以试一试,如果那你的确有这样一个需求的时候,你可以用这个函数shutdown,比如说你的服务器就只收不发。

好,这里面我给大家出一个思考题。出一个思考题,你想一想这个东西能怎么去理解?那么咱们都说这个 TCP 链接是不是这个安全可靠的?那么安全可靠体现在哪有?是不是有这个有序号和什么呀?和确认序号吧。那如果说你关闭的时候,你关闭了一方,这个确认序号还能发回去吗?能不能?那如果这样的话他还是安全可靠的吗?如果让你这么想的话,注意注意这个shutdown只是在用户层面是这样的意思,内核当中还可以往往回发,那是内核的行为,是你的行为吗?那不是你的行为。那么调用shutdown是不是在用户层一些调用的?你调用shutdown以后你是不能往外发了?比如说你把这个写端关闭了,你是不能往外发了,是这意思吗?但是你能收,但是对内核来说,内核是双向的。

你是不是他给你发了个数据包过来?是不是内核的给他回一个ACK回去啊?那你能说他不能发了吗?不能的,那是内核的行为,要搞清楚这个东西是底层行为吧?是不是?如果不能的话,那他说的这个什么什么安全可靠还有用吗?搞清楚这个理解了吗?同学们能理解吗?不要搞混了,那个属于底层行为,内核行为不是你的行为,你只要调shutdown,你比如说你把这写端关闭了,你是不能写的,那就不能写意味着是不是你不能给对方发了?但是内核是不是还得给人家回ACK 啊?是不是给你回啊?内核是能给人家发的,知道什么意思嘛?内核是能发的,但是你不能清楚吗?你不能。

这个,这个关于这个shutdown和 close 区别以及半关闭,咱就说到这。那么说到这以后,那有一个概念需要给大家提一下,哪个概念呢?咱们这儿也跟你说了,长链接和短链接的概念,这个你如果你一起来应该能看懂,嗯,那么这个长链接、短链接什么意思?其实你从字面上是不是也能大概能猜得出来?那么所谓的长连接是什么呀?这个双方链接建立好以后,这个链接一直不关闭。

什么意思?链接建立好之后怎么样?一直保持吧?是这意思吗?同学们一直保持链接,那想一下,这个长时间一般用在什么时候?举个例子,那么我给你举个例子,比如说你这个双方通信的双方,它的这个数据传输非常的频繁,那这个时候你这个链接还需要保持一下,你可以建立一个长链接,如果说你的这个数据比如发送之后就完事了,很快完事了,这个还这个时候还有必要建长链接吗?大家注意链接也是一种资源。

什么资源?你这个链接是不是要占用文件描述了?是不是这样的?如果说你的这个系统上这个你这个进程占用的链接数太多的话,是不是你在这个你剩余的这个能够新建的这个文件描述符是不是就少了?它也是一种资源,清楚了吗?也是一种资源。那么什么叫短链接?什么意思?同学们,链接使用完以后。

就什么呀?立刻关闭,这是短链接。我给大家举个例子,什么意思?你比如说你去银行取款,同学们,这个银行的这个取款机是不是给他的后台有一个链接,有一个网线连着的?同学们不管什么还是能连在一起,那么你取款的时候你一点发送,比如你取款确认 100 点好了,一发确定他是不是就发送过去了,发到后台去了,那这个时候的钱就如果说那边受理了,唉,这个钱呢?吐出来了,是不是将那交易完成了以后,你这链接还有没有必要保持啊?没有,你想想那银行的取款机全部那么多,它如果保持的话,它够用吗?知道什么意思吗?它这个用完之后就马上关闭了,是不是用完之后就马上完毕了?我再给你举个例子,再举个例子,比如说类似于这种,比如说那像这种,这个,比如说奥运会也好,是什么什么开幕式这种是不是现场直播有的是?是不是那种的话,链接是不是需要长时间保持?能理解吗?你不能说你这刚发过去,然后关闭马上又建是不是很耗时啊?

要知道这个创建链接需要 3 次握手,这个时间还是需要时间的,是不是这样的?我们这个是我们这个链接一直保持着,因为我这个链接一直有用,一直在有数据传输,所以这个链接就不需要关闭了。我不知道大家听说过这个概念没有?就是有的时候比如说电信,或者是有的公司需要租电信的网,知道什么意思吗?需要租他的网这一年有多少钱?有的还是需要这个他的链路一直畅通,需要一直畅通,那个叫租专线,租专线一年好多钱啊?当然那个专线上是不是可以保持好多链接?同学们,你要搞清楚,你这个网线上啊,你这我们这个网线是不是可以有多个链接共同来使用?是不是这样的,你不能说,你是吧?你不能说。

当然电话的话肯定只有一个链接,比如说你打电话给别人打,是不是另外再来一个电话是不是就接不了了?但是你电脑的话,是不是我给小王可以聊天,我是不是给小花也可以聊天等等,是不是有好多呀?知道什么意思?这个好,那我关于这个长链接、短链接概念你就搞清楚就可以了,是吧?链接建好之后,链接一直保持不关闭,这叫长链接。

那短链接之后,短链接是链接使用完之后立刻关闭叫短链接。同时你得知道创建链接是需要三四握手,是需要耗费时间的,频繁的,如果你的这个业务量比较小,你使用长链接就浪费资源。如果说你的业务量非常大,你使用短链接,它就这个时间,这个效率低,能理解嘛?这个具体使用长链接还是使用短链接,要看具体的这个需求是不是好。

这块好,那么接下来咱们讲一下这个心跳包。那么在讲这个心跳包的时候,咱们先这个提出两个疑问,第一个的问题是什么是心跳包,是吧?能看到屏幕是吧?第二个问题,那么在什么情况下使用心跳包?

第三个如何使用?好,我把这个心跳包这一块讲完之后,你能够把这 3 个问题回答出来就可以了,搞明白就可以了。好,咱们先看一下,那么什么是心跳包?大家注意,心跳包主要是用于检测与对方的网络链接是否正常,是不是?这是第一个,第二个心链包,它主要用于长链接,为什么不能与短链接?同学们,用于短链接有意义吗?你这个数据发完之后收回来就关闭了,是不是?这个时候你再检测检测它有用吗?是不是没有意义?那么所以说这个心跳包主要用于长链接,那么检测对方的与对方的这个链接是否正常,如果不正常是不是他就发现了?发现之后怎么办?可以想,如果,你如果这,如果通过心跳包检测呢?检测到这个链接异常了,你说会怎么办呢?要重建链接,否则的话,你说用这个心跳包有什么意义啊,是不是?那么是不是让他早发现问题早解决啊,是吧?好,那么如何去使用这个心跳包呢?  大家注意我们这里面提到了两个方法,提到两个方法,那么第一个方法是用 set sock opt 这个函数,但是大家注意这个函数我们一般情况下很少去用它,因为用这个函数的话,它不能实时检测到网络情况,所以一般不用。

那么取而代之的是我们一般是在应用程序当中自己去定义心跳包,自己定义好,那么既然涉及到这个,是不是我这个心跳包,我要给这个对方发送心跳啊,是不是?你不要把这心跳的念的太复杂了,这个心跳的其实就是发送一个数据串或者字符串,这个串你是不是可以自己定?是不是同学们 可以自己定,那我举个例子啊,那么比如说这个是我们的一个程序,

是吧?这是一个程序,然后这又是另外一个程序。兄弟们,那么我们这个程序,我这个程序,我本地的程序和远端的一个服务有一个链接,当这个链接是个长链,短链接,同学们,长链接是不是长链接?好,这个叫本地服务。可以,这是远端服务,那么有链接,那么这个链接是一个长链接。

好,长连接,我们看一下,那我这本地服务是不是需要给这个远端发这个发包了?是不是发数据?同学们,那么你想一下这个有没有不发数据的情况了?是不是也有啊?当然你要是你正常情况下,我们本地服务往远端发送服务的,

发送数据的话,如果说链接有异常它,是不是就可以检测到了?那么这个心态包一般用于什么呀?比如说有空闲的时候,因为我这个本地服务和远端服务,它有时候它也不是说总是发出去,它有偶尔空闲的时候,那么有空闲的时候,那么也得保持这个链接的畅通吧,是不是这样的?那么假如说现在空闲了,这个时候这个心跳包就开始发送了,那发送的时候呢?那怎么发?我举个例子,那么你可以本地服务和远端服务可以定一个协议,看到了吗同学们,那么是不是涉及到双方通信就要有规则啊?是不是协议啊?这时候比如说规定好了,我本地服务,我发 4个A 可以吗,然后这个对方服务器那么远端服务呢,他收到 4个A 以后呢,他就知道了,这是一个心跳数据,知道什么意思?

这是一个心跳数据,那么他给你回一个什么呀?给你回一个 4 个B,那这样的话,那么这个B 收到了他的这个 4 个B 之后, 这个本地服务,收到了 4 个B 之后呢,他认为这个网络是畅通的,是不是?那假使说那么这个本地服务给他发送 4 个A的时候呢,他可能也收到了,也可能没收到,但是这个服务端这个本地服务端它始终没有收到 4 个B,

那么这个服务端这个本地服务 A 本地服务认为是不是网络是什么呢?不通的有异常,当然,那么你不能说检测一次就认为它是异常的,是不是可以多发一次?如果连续发送 3 次或 5 次仍然是数据发过去了仍然没有收到,那么你就认为,那么这个本地服务认为这个链接是有异常的怎么办?链接有异常,是不是接来需要怎么办?要重建链接?是不是这样的,过程给写下发送心跳过程一步是服务A,是吧?给谁给 B 发送心跳数据?当然这个 4 个A,能不能定这个自己随随便定好,你 4 个A也行, 4 个 B 也可以,总知道你这个规则,双方都要知道是不是好。

然后这个服务 B收到, 4 个 A之后,然后给谁给 A 回复什么 啊? 4 个B,此时,这个A收到 4 个 B 之后,是吧同学们?认为什么呀?认为链接怎么样?正常这种是不是链接正常的情况?那假如,这个 A 连续发送了多次,当这多次的话,你自己定好吧。

你这个 3 次也行, 5 次也行,但是你这个次数不能太多,你不能说发 100 次,是不是?这个不能,比如你发了 3 次, 5 次都行,肯定是有限的一个次数吧,是不是?而且这个次数不能太大,发送了多次。比如给你举个例子,如 3- 5 次,之后仍然,没有收到什么呀?如果仍然没有收到这个数据的话,我们认为什么呀?没有收到B 的回复,则认为怎么样?链接异常,好,那么既然说你知道链接异常了,那么你说该怎么办了?同学们,怎么办?

是不是重建链接?把这个往这移一下是吧?异常好,异常之后, A 应该重建链接好,那么这儿就涉及到重建链,那么怎么重建链接,对吧?如何重建链接?其中这个东西不要想那么复杂,所谓的重建链接就是把原来的 close 掉,然后再什么呀?再 connect 就可以了。清楚了吗?就这么简单,怎么重建链接?就是先close原来的链接,是不这样的,然后在重新connect 链接就可以了。

这意思,这个意思听懂了没有?嗯,好,

那么这个心跳这个意思大家懂了,那么我再提个问题,那么你说这个心跳它是不是也是数据?同学们,那么还有一个是不是正常的业务数据?嗯,那么如何让这两种信息,这两个数据不混淆呢,知道什么意思吗?我说话你明白了没有?接下来我给你提另外一个问题,如何让心跳数据和怎么样啊?正常的业务数据不混淆?这个例子听懂什么意思了吗?因为你正常情况下是不是这个,你本地服务A 是不是也会给本地服务B 发送这个数据啊,对吧?你想那么如何让他们之间不混淆,大家也可以思考一下怎么解决这个问题,怎么解决?

那对于这种情况,你只能说是什么呀? A和 B 之间要进行协商,我给大家教一个经常用的方法。解决办法。可以什么呀?这个双方可以协商什么呀?规则是不是叫协商?协议 协议和规则是不是一个意思?怎么协商呢?很简单, 比如说你可以在这个数据的前面加一个包头,包头的概念我跟大家说过吧,嗯,大家想一下我在哪说过包头是不是粘包的时候了?还记得吗?那么形成一个包头,比如说 4 个字节长度,加什么呀?加具体数据是吧?同学们,好,那假如说你要发心跳,你应该这么发,如果发送心跳数据,是吧同学们,应该这样,怎么该怎么办? 0004AAAA 能理解问题,如果你要发心跳,你就这么发。

如果你发业务数据呢,如果发送业务数据,怎么发,比如说举例子,这个数据长 10 个,是不是0010,然后1234567890?那么这样的话,大家注意 这样的话,那么这个发送方当然是不是A啊,同学们,A 发送给这个,也就是远端这个本地服务是不是发给这个远端服务了?那这个远端服务收到你的数据之后,他先干什么?先干什么?他怎么收?他是不是先收前四个?收到 4 个,当然这个长度他也知道,其实,大家注意,其实如果说如果是你们双方协调好了,如果说 A 直接给他发 4 个字节,是不是这个他接下来,接下来他要收再收 4 个啊。能理解吗。

当然这个如果这个发 4 个的话,是不是有可能后边就 4 个A啊?当然这个业务数据万一也是 4 个数据呢?,是不是这个你必须得收完吧?好,然后对方收数据的时候,对方收数据的时候先收 4 个字节,包头数据,那这个包头数据,这个把这四个字节收到之后,是不是他要这个算出长度来?是不是这样的?然后计算长度吧?那计算出这长度来以后,

是不是再收剩余的数据啊,是不是?好?这个数若最后计算长度为4,是吧同学们?且数据为4个A,什么意思?则认为什么呀?是心跳数据,那么他收到的是这个心跳数据,那么接下来他该怎么办了?朋友们,他是不是也要组织这个应答数据给A啊,是吧?这个认为是心跳数据,则 B 服务, B 服务会组织应答数据给A吧,怎么组织?是不是0004BBBB

是不是?那这样的话,这个 A 收到这个 0004BBBB之后,他是不是认为这个链接是畅通的?链接没问题,是不是链接没问题?当然如果说那个这个 A 连续发送了好几次,仍然没有收到任何回复,那么这个 A 认为这个什么呀?

链接异常了,那么链接异常他怎么办?是不是要先 close 它,然后再是吧 connect 重建就可以了,是不是这样的?整个过程,这个心跳的这个过程就这样,能听懂吗?同学们,你一定要注意一点。

注意点什么呢?你这个 A 和B 之间,它发送的数据是两类数据,哪两类?一个是业务数据,是正常的业务数据,同学们,一个是心跳数据,这两种数据之家再去要搞清楚要分开,是不是我们通过这种方法就完全给分开了?那么这个你把数据发送给对方的时候,对方先收 4 个字,也让他如果收到的是 4 个A,他是不是立刻给你回 4 个B啊,是不是?如果你发送的是正常的业务数据的话,没有关系,人家进业务处理之后是不是给你回复数据?是不是这样的?整体上就是这么一个套路,能听懂吗?同学们,这个好, 你把这个听完之后,那我给你提的这几个问题能搞清楚吗?

第一个,什么是心跳包 。

用于检测链接是否正常的?字符串就是吧?用于检测,当然长链接是否正常的什么啊?字符串能这么说吗?同学你不要想太复杂,就它其实就是一个字符串是吧? AAAA 四个 B 是不都是字符串吧?好。

第二个,什么情况下使用心跳包是不是?主要用于检测长链接是否正常?

第三个如何使用心跳包,怎么使用?同学们,是不是你得定义这个双方得协商规则啊,就说通信双方需要什么呀?协商,什么呀规则,这个规则是不是叫协议啊?比如说如, 4 个字节长度加什么呀?加数据部分的是这样的就可以了。当然你这个具体的话是不是 4 个字节,这个你自个定。那假如说你这个业务数据有可能会超过1000,是不是有可能会有四个数字长段,那这个时候你就用 4 个字节,那假如说这个你最多这个数据长最多就是几百,你是不是可以定义三个啊?嗯,是不是这个没有关系,自己定就可以了。

好,那么关于这个心跳包这个呢,这块咱们就说这么多,这几个问题你能够搞明白就可以了,是不是?那真正你们在这个参加工作的时候呢,这个心跳包是经常用到的,经常遇到的,到时候你看代码的时候,是吧?你注意点就可以了。一般情况这个心跳的数据是不是只是用来检测这个什么呀?检测这个链接是不是正常,并不进行任何的业务处理,搞清了吗?并不进业务处理。

好,那么看一下这个 select 这个,那么在讲这 select 之前,咱们可以先回顾一下咱们前面讲过的这个,前面讲过是不是服务端和这个客户端的开发,你先可以回忆一下咱们这个,嗯,第一天在讲这个客户端和这个服务端开发流程的时候,

咱们都知道在一个进程里面,大家注意在一个进程里面你说能够支持有几个客户端连接? 是不是只有一个?是吧?原因我是不是给你分析过了?后来咱们是不是又讲了一个多进程版本和多线程版本的,是不是这样的?好,那假如说我现在有这样需求,我就想在一个进程里面支持多个链接请求,怎么办?显然咱们前面做到,前面是不是都做不到啊?那么你可你要,当然你一个进程里面也可以做到,是不是?创建多线程啊?是不是?那如果说你也不使用多线程,

也不使用多进程,是不是不好做到啊?不好做到?为什么呀?第一,咱们都知道 accept的是不是阻塞的?是不是? read 函数是不是也是阻塞的?是不是?你当你 read 的时候是不是这个?如果阻塞了,它是不是调用不到accept?当你 accept的时候,它是不是调用不到read 啊?是不是很显然,那么我们做不到?如果不使用多线程或多进程的话呢,我们不好做到在一个进程里面处理多个客户端链接的情况。

那么下面咱们给大家说另外一种模型叫高并发服务器模型,叫select,这个它是一种,它是一个多路 lO 复用技术。那这个词你可能还第一次听说,当我把这个函数讲完之后,那么你自己写了篇以后,我想的话,你体会就应该深刻了。好,咱们看一下这个,那么这个 select它可以用于在一个进程里面支持多个客户端链接的情况,

看一下多路IO技术的定义:select,同时监听多个文件描述,同时监控多个,将监控的操作交给内核处理。我问一下大家。前面咱们在讲这个一个进程里面处理一个链接的情况的话,如果说对方不发数据的话,我们的 read 函数是不是会阻塞?嗯,是不是?当然accept是不是也会阻塞?对,是吧?大家注意这个时候,我们今天讲这个是把这个什么呀?把这个监控的操作交给内核去处理。去操作,什么意思呢?也就是说我们自己不用在那 read 或者是 accept 阻塞了,如果说有客户端链接到来,或者有数据发过来呢,内核会告诉我们,内核只要去告诉我们,我们就处,我们就去处理就可以了,就这么意思。

好,那么看一下这个函数原型,那么这个函数原型就是这个 select的这原型,关于这个,嗯,可以看帮助,

这原型我已经给你这个保存到这个里面来了。那下面我就在这给你把这个函数给你介绍一下,咱们一块来说一说。一块来说说看看第一个参数,首先函数作用,干什么的?这是?这个函数干什么的?委托内核监控?什么呀?委托内核监控什么呀?同学们, 是可读,可写,还有什么异常事件?一共有 3 类,那么这个 select 不只是监控可读,什么叫可读的?同学,想一下,链接,客户端有链接发过来,你说这个链接的数据在哪?同学们,大家注意,包括这个客户端发数据过来,这个数据是不是要发送到你的内核的读缓冲区里面去啊?那么如果说这个咱们不讲select,你不知道这个的话,是不是你在 read 函数的阻塞,还得是阻塞,是不是得阻塞?这是一个。那么假如说咱们交给内核处理了,你就不用阻塞等待了,只要有数据过来,内核告诉你再去读这个数,你还用阻塞吗?就不用了,就是这么点好处,好,看看它的这个函数。

第一个 nfds 这个什么意思呢?这个参数是这个参数表示要告诉内核监控的一个范围得,什么意思?那么你现在是不是要委托跟内核让内核帮你去监控,有没有这个客户端要来呀,有没有数据发过来等等一些情况,是不是那么好?你要监控的话,是不是你得告诉内核一个范围,你得告诉内刻监控哪些?是不是?这个是告诉内核要监控文件描述符的范围,这个值我们取值的时候,一般怎么取?这个值取的时候一般是最大文件描述符加1,一般取值为最大文件描述符,大家想一下为什么是最大文件描述数加1呢?大家可以想一下,这个文件描述符在分配的时候有一个什么规则?分配的时候给你分配的时候是不是给你分配的时候是最小的,且未被使用的同学们,是不是这样的?当然这个为什么在这说最大的呢?这个关于这个你可以想一下这个我给你画一个图,我举个例子,这个是我们的文件描述符表吧,是不是在这个表里面?是不是存放的是已经打开的文件描述符了?好,那假如说这个被占用了,这个被占用了,这个被占用了,这个被占用了,好几个都被占用了。

那后来中间你 close 了一个,这 close 了,下次再分配的时候是不是应该从这开始分配?很显然这个应该不是最大的,是不是?那么后面那个是不是最大的?如果说咱们取值是这个文件描述加1,是不是相当于取到这了?是不是到这?好,从这到前面的一个是不是整个的范围?这样话肯定是不会漏吧,能理解吗?同学们,你切记一点,不一定最新分配的文件描述一定是最大的,有可能是小的。清楚了吗同学们,这个是你切记的

第二点,第二个 readfds。 readfds是什么意思?这意思表示委托告诉内核,大家注意这个参数是比较特殊。第一个它是一个输入参数,同时它也是一个什么呀?输出参数,当然前面这个是个什么参数?同学,什么参数?这个值nfds是输入参数,这个readfds  大家注意这个readfds  它是一个这个类型的fd_set。关于这个fd_set类型呢?

你可以联想一下 咱们前面讲过的类型叫(seikeset_t)

那叫什么呀?是不是那个叫信号的集合,是吧?同学们,大家想一下,信号的集合是不是我给大家讲的是不是,它里面实际上是不是用的位图啊?是不是位图?里面的值是不是只有两种值?同学们,1或 0吧?1 表有 0 表没有,那么在这我们这个也是一样的,fd_set也是位图,那么这个呢? 0 表示什么意思? 0 表示没有让内核去监控,1表示让内核监控了,这个值如何设置?后面咱们会说

好,那么输入参数什么意思?是怎样?告诉内核要监控哪些文件描述符,能理解什么意思吗?那么你既然想让内核帮你监控了,是不是你得告诉内核监控哪些?是不是这样的,好?输出参数呢?你想一下,那么假如说有这个有可读事件发生了,比如说是有客户端连接到来了,或者有数据发过来了,这个时候内核是不是要告诉啊?他怎么告诉你? 大家注意 这个叫输出参数了,这个是内核告诉应用程序哪些文件描述符有变化,告诉应用程序,哪些文件描述符有变化,他把有变化的文件描述符给你记录到了这个readfds 这个上面,这里面去了,这个集合里面去了。

那么言外之意什么呀?也就是只要有变化的,他都把这个集合当中对应的位,干什么干什么,把对应的位干什么呢?如果有变化都置1了,明白了吗,同学们都置1了,那么只要是1是不是我们认为这个文件描述符有变化啊,那么所谓的有变化就是有可读数据过来了,或者是有客户再给你发过来,是不是这样的?好,这个你先你听一下,咱们写代码的时候写着写着我把思路到时候跟你说,你就明白了。先把这个函数介绍一下。

好,第三个参数,第三是不是writefds,大家想一下,一般情况下我们这个 write是不是写事件啊?同学们,再想一个问题,那么这个写的话是不是我们这一般是我们应用程序的主动行为啊,知道什么意思吗?但是读的话是不是你是被动行为啊?

你不知道这个数据什么时候过来,但是写的话你知道吗?你自己是不是能确定?一般情况下我们很少让这个内核帮我们去监控这个writefds,

倒不是说不可以,但是一般我们不去这么做,我们最常用的是让内核帮我们监控什么事件?读事件,因为这个是不是你被动的,

是吧?这个和什么呀?和这个上面这个是一个意思,是吧?是可以和这个是一个意思,后面还有一个什么呀? exceptfds。exceptfds什么意思?大家注意,这个也是什么呀?也是输入,输出,也输入输出,这个我大致给你写一下,因为这个咱们也用的不多。

输入输出参数,这个一般表示什么呀?异常事件,什么叫异常事件?比如说这个链接异常了,是吧?链异常了,本来不是对方关闭的是吧?这个时候发送发不过去了,等等,这些都是异常事件。好,这是这个瑞,这是个一个这个exceptfds 。那么还有一个叫什么呀?timeout,大家想一下这个timeout在哪见过啊?,是不是信号连,我记得跟你说过这个啊?和这个类似吧?同学们,这个表什么呀?超时时间,这个时间大家注意,这个时间有几种值?有三种值。

第一个,NULL:什么意思?表示永久阻塞,直到什么呀?直到有事件发生,什么意思?那举个例子,你服务端启动起来了,是吧?同学们,服务端启动起来,这个时候客户端一直没有链接那么好,这个 select 就在这一直阻塞,假使说有客户端链接请求过来了,或者是有事件有这个数据发过来了,这个时候这个 select 这个函数就立刻返回,能理解吗?

第二个,0,如果你赋值的时候,这个 time out 这个里面你赋值都是0,知道什么意思啊,表示不阻塞,不管有没有事件发生则都会立刻返回,知道什么意思吗?NULL 0 还有什么呀?我记得还有一个是- 1 吗?看手册,咱们看手册, time out。嗯,看看这边有没有。写的不太全。是吗?

怎么说?当然还有大于0的数,是不是吧?这个咱们说的大于0的什么意思?是表示阻塞的时长。

那么举个例子,比如说你这个时间设置的是 5 秒钟,假使说 5 秒钟期间没有事件发生的话,那么这个函数在 5 秒钟期间是不是一直阻塞啊?那假使说在 5 秒钟期间内有事件发生了,这个函数是不是会立刻返回啊?当然超过了 5 秒钟是不是也会立一个返回,对不对?超过了 5 秒钟也会立刻返回,没有超过时长,则一直什么呀?阻塞,这是一种情况吧?

若怎么样?在时长内,有事件发生的干什么呀?则立刻返回,是吧同学们,当然这个如果说有事件发生,是不是这个内核会把哪些事件发生?是不是给你传到这个参数里面来了?能理解吗?是不是这样的?好,还有若什么呢?若超过时长什么呀?则立刻返回,是不是? 这是一个这个函数,那么还有看它的返回值啊,返回值是吧你们,那你想一下,这返回值是什么意思?成功什么呀?返回几?大家注意,在这成功它就不返回 0 了。看手册,看手册,这个返回值其实给这个时间有关系的。看这个如果成功,兄弟们, select and 什么?或者是 p select tends number of five discreter contained in the three returned  descriptor  sets

这句话有点绕,那么这句话的意思 大致是这样的,就如果说这个函数返回值了,是不是返回了,它返回的值?返回的是一个数,是不是the number  of  file descriptors ,那什么的个数呢?就这 3 个,是不是 readfds  writefds  exceptfds,它们这三个三种事件里面,一个什么呀?发生文件描述符的总和吧?知道什么意思吗?总和,那假使说现在我们这儿是这样做的,我们只让内核帮我们监控这个事件readfds,读事件,其他我们都设置为NULL,可以吗?是可以的,都设置为NULL。

那么这个时候这个返回值你说什么意思啊?返回值,成功返回了,什么意思?是不是表示有几个可读事件发生了?知道什么意思吗,不理解啊。我举个例子,假如说有 3 个客户端链接发过来了,其中还有两个客户端发数据过来,现在是不是一共有 5 个客户端啊?你说这时候返回几啊?  是不是返回 5啊? 是不是3?那三个是不是有客户端链接发过来,然后有两个有数据发过来? 3 + 2 = 5,那么这个值就返回 5 ,清楚了吗

所以这个返回值是返回什么呀?发生变化的文件描述符个数吧,是不是这样的?返回发生文件描述变化的个数。好,这是这个 是select 函数,那么这个 select 函数呢?一共就这些,不知道大家有没有听懂我想的话,可能是好多人没听明白。别着急,咱们写代码的时候,这个去让大家了解,去大家理解。那么这个readfds是一个集合,这个集合里面我刚才给大家说过了,在这个集合里面,它实际上是位图操作,如果你看我那个,这个我给大家发的文档了,那里面有这个有细节,你可以看一看。我是从这个底层代码里面给你抠出来的,给你扒出来的,本本质上还是为了操作。这个和咱们前面讲过那个信号集,是一个意思。还记得信号集里面是不是也是有两种值啊?同学们, 1 和 0吧?1 表示有 0 表没有,我们这个也是一样的,如果发生变化了,那么那个为上就置1,没有发生变化就置0,知道什么意思吗?同学们?但是我们这个参数,这 3 个参数既是输入也是输出,这个就略微复杂一些,略复杂一些。

当然后面这个是什么呀?表示超时时长吧?那么这个是不是有三种取值?NULL表示永久阻塞,直到有事件发生,那么这个 0 表示不阻塞,不管有没有事件发生,都会立刻返回,能听懂吗?同学们 这个。如果说设置大于0呢,是不是表示一个阻塞时长?对,当然在这个阻塞时长之内,如果说有事件发生了,它是不是也会立刻返回啊?超过了这个阻塞时长仍然没有事件发生,他也会立返回,是不是?那返回的人是几呢?如果说有事件发生返回了,那么好,这个返回值就是发生变化的文件描述符的个数。

好,同学们,好,那么接下来大家想一个问题,那现在呢?第一,我们知道有几个文件描述发生变化了?第二,我们是不是得知道有哪些文件描述发生变化了呀?如果你不知道哪些的话,你能处理吗?是不是?如果让你想的话,你怎么想啊?现在比如说我有 20 个客户的链接和服务端进行通信,现在只有其中的三个有变化,有三个知道什么意思吗?是不是有三个和这个服务器有通信啊?你是不是得知道这 20 个客户端,这些 20 个文件描述符里面有哪个有变化?有哪三个?你现在只知道有 3 个变化,但是你现在来说你并不知道具体哪三个吧。接下来这个给我们提供了具体的函数,提供了几个宏,来帮助我们判断到底是哪个文件描述有变化,哪个?这个我给大家把这个拿出来,与这个 select 配套的函数呢?是这个。这几个,这有宏,我把这有宏给你扒出来。这几个宏什么意思?

好看,第一个,看第一个。同学们,第一个什么意思呢?快速说一下。

这个宏的意思是FD_CLR,你猜一下什么意思? 你如果能想到咱们前面的清空信号集,给这个是不是类似啊,这是清空的意思。清空什么呀?文件描述符集,这个文件描述集,你就把它理解成一个什么呀?类似一个表,这个表里都是文件描述。可以吧?你这么理解就可以了。

清空文件描述符集,当然你在使用之前是不是要定义一下啊同学们,是不是叫什么呀?fd_set  fds?你是不是要先进行初始化呀?同学们,这应该是他写的set,咱们也写set吧? set 是吧? set 好,这是清空的意思。

好,第二个,猜一下,同学们,FD_ISSET大家还见,还记得咱们前面那个是不是 is member 了?什么什么什么is什么什么的,是不是就是判断那个集合里面有没有这个值啊?这个也是一样的,说明。什么意思?判断什么呀? f d 是否在 set 什么呀?集合中吧,能理解吗?同学们,好看。

第三个set, FD_SET,后面是一个集合,什么意思?这个的意思是不是将 f d 添加到 set 集合当中去?对,就这意思,将 f d 添加到 set 集合中,是吧?后面那个呢?什么意思?好像咱们第一个是不是说错了,同学们,这个清空是不是应该放这了?这个说错了,

这个FD_ZERO才是清空,是吧?这才清空。

第一个这个什么意思?这个其实很明显,是不是将这个 f d 从这个集合当中清除吧,是不是这样的?从 set 集合中。什么呀?清除 f d?好,

这几个宏我都跟你说了一遍,这个函数咱们也讲了一遍,那么接下来咱们就把思路跟你说一下,说完思路之后,我给大家大概 20- 30 分钟时间,你自己等会写一写。好吧同学们,如果你觉得自己写的慢,你这样你看看你能不能用UE写,用UE写或者用其他工具能连上就行。好,这个你可以自己定。这行,我把这个再注意,我把这个给你,再给你说一遍,跟你说一遍。然后这个咱们就把这个思路跟大家一块来说一说。

那么关于这个,首先我们要使用这一个宏的话,第一步首先是不是要定义一个变量啊?是不是定义一个变量,这个变量就成这个样子了?比如说我们定义了一个变量是不是? 这叫什么呀?这个就叫集合, fd_set 是不是?这集合是不是?我个大家说过这个集合里面是不是都是位图操作?是吧同学们?都是位图操作?当然这个里面咱们就给你画一下,再来一个。

好,画这么几个就可以了。好,你看第一个,首先 fd_clear f d 是吧?在想这个文件描述时候是不是这个?你看一下,假如说这个 f d 在,这可以吗?同学们,这比如说是1,是吧?同学们,这 1 是表示有了,同学是不是进来了?好,那么第一个你看一下,执行这么一个 fd clear,那么执行这个操作以后,你说这个 1 变成几了?0,是吧同学们,这个 1 就变成 0 了,就这意思好。第二个 f d_set 是吧?假如现在让你判断这个 f d 是不是在这个阶段中,在吗?

是 0 吗?我们认为不在是吧?不在,是不是返回假啊?那么假如这是 1呢? 是不是表示在啊,就这意思清楚了吗。第三个 fd_set是不是把这个 f d 加入到这个结构当中去啊,那加入这个 一开始没有,一开始没有,那现在你做了一个fd_set ,fd 然后他这个set是不是相当于这是1啊?能理解吗?同学们?相当于这儿置1了,他后面那个是什么? f d_zero什么意思?是不是相当于将这个什么呀?将这所有的全置0啊,是不是全置0,这是不零00,是不是都是0?就这意思,和咱们前面是不是说那个信号集是不是类似啊, 也是,本质上也是位图操作,好,那么接下来咱们就给大家说一下这个什么呀?使用 select 模型的一个步骤,思路

好第一步先干什么?大家想一下我们这个开发这个 select是不是这个主要用于服务端?是不是?主要用服务端?但是不是说这个客户端的不能用?客户端也能用,也能用。比如说让 select的监控什么呀?监控标准输入可以吗?可不可以?大家注意,只要是文件描述符select都可以监控,包括管道也行。管道是不是也有两个文件描述符了?嗯,也行,也是可以的。好,咱们看一下

那么使用 select的开发流程,开发服务端 流程第一步先干什么?你开发服务器的时候先干什么?是不是创建socket啊?是不是?这第一步?其实这个前面这几步和咱们前面讲过的一模一样。是吧同学们,这个调用哪个函数,socket()。当然我们这个创建socket  那么得到了一个什么呀?同学们得到监听文件描述符吧?什么呀? lfd吧,对吧?好,

第二步。那第二步是不是你可以设置端口复用?对,还记得吗?设置端口复用调哪函数? setsockopt这个函数。

第三步是不是接下来绑定了?绑定谁和谁绑定,同学们,将 l f d 和 i p PORT是端口的意思,PORT进行绑定,这调哪函数?是不是bind啊,bind函数。

第四步,前面是不是都是一模一样的?同学们,都是一模一样。接下来第四步该干什么呢?是不是设置监听?是吧?同学们,是不是这个函数了 listen()?

第五步该干什么了?好,那大家注意啊,现在来说,是不是我们得到了一个监听的文件描述符了,而且我们的程序是不是也处于监听了?嗯,是吧同学们,这个监听文件描述符,是不是主要是用来监听的,不是用来通信的。这个搞清楚了。好,咱们看一下。那么你说接下来我们该干什么了?一定要想,那么现在我们这个是不是要这个使用 select 委托给内核帮我们监控?那么好,那么你要知道你要让内核帮你帮你监控什么?对的,在这我们主要是让他监控可读事件。

那么这个可读事件包含了两大类。哪两大类?一个是到底有没有客户端链接发过来,一个是连接请求吧。同学们,连接请求是不是也叫可读事件啊?那有同学可能对这个可读这个事件可能不太理解,我问大家,那么如果说有客户端连接发过来,对于我们服务端来说,你说是可读事件还是可写事件?为什么?因为你看一下,那么服务端是不是要调用 connect 的建立链接?调用 connect是不是有一个 3 次握手过程啊? 3 次握手,是不是服务端要给他发 syn请求啊?那发送了这个请求之后,这个数据到哪了?同学们想一下,这个是不是告诉内核了? 3 次握手过程是不是内核帮你实现的?嗯,内核知道,大家注意,内核是知道这个读缓冲区当中到底有没有数据的,他知道的,他是不是比应用程序先知道啊?是不是这样的?因为这个数据是不是在内核当中啊?他先知道?那么接下来是不是我应该知道让内核帮我监控什么事件,首先知道了是要可读事件。

好,那这个可读事件我刚才跟你说过了,有两类,一个是什么客户端有链接发过来,是不是调用 connect 了?第二是他发数据了,是不是两类啊?他是不是有可能发链接?有可能有数据发过来,那么链接建立好之后是不是接下来就发数据了?有两个。那现在呢?你现在是什么呀?到这为止,到这为止有链接吗?同学们,没有,是不是没有链接?没有链接过来的,当然,现在的话,你是不是要委托内核帮我们监控?是不是有这个客户链接发过来了,是吧?好,那么你说先监控哪个啊?该监控哪个文件描述符了,那么有没有客户端链接发过来?那么对于我们这个服务端来说,它是不是读的是这个监听文件描述了?accept ,那第一个参数是不是监听文件描述符?是吧?所以在这我们应该怎么样?将监听文件描述符加入到什么里面去?这个可读事件的集合当中去?当然前期是不是你得先定一个变量?同学,哪个变量?fd_set的是不是?比如说叫 readfds 可以吗?是这个。

那接下来干什么呀?将lfd加入到哪? read f d s 集合中,这句话理解什么意思吗?加入到这个集合当中,调用哪个宏呢?这句话不理什么意思啊?同学们,你既然想委托内核帮你监控读事件的话,那么你是不是要你得把这个文件描述是不是加入到这个集合当中去啊?你不加入怎么加框给你?是不是这样的?好,那么第一个参数是什么啊?不,这个应该调哪宏 啊?FD_SET ?第一个参数什么呀?lfd,第二个是不是这个&readfds?好,那么你这个,一般情况下,我们刚定义了这么一个变了之后,接下来干什么呀?第一步是不是要初始化呀?

叫什么呀?FD_ZERO是吧?   它是不是只有一个函数了?对,是不是也是一个地址?对,&readfds,是不是这样的?这个是,给你把这注解下,这个是什么呀?定义什么呀?文件描述符集,是不是这样的?变量,这是什么意思?这个就相当于初始化的意思,是不是?清空文件描述符及变量,那么这一步操作完了以后,是不是相对这个集合当中全是 0 了?是不是?好,这一步是不是我把这个加到后面去,是不是这样的?现在这个 lfd是不是进来了?

对,是吧?同学,进了,好,接下来你顺着这条线去想接下来的话该干什么了?现在这个 lfd是不是已经到这个集合当中来了?他到这个集合当中来,接下来是不是这个 select的函数开始这个内核帮你监控?对,那么再问一个问题,那么是不是我们应该让内核持续的不断的帮我们监控啊?是的,好,所以这个应该写在什么里面?是不是while语句循环里面啊,你要知道,是不是这样的,

同学们,你不能说让你让内核帮你监控一次就完事吧,是不是?好,接下来看一下,是不是要调用 select 了,比如说我们这个返回的是个数,是 n ready 等于什么呢?等于一个select,第一个参数是什么?是吧同学们,第一个参数是哪个?当然第一个参数是不是我们要告诉内核监控的一个范围?好,一开始的话,一开始这个文件描述是不是只是lfd啊?那这个最大的值是不是应该就是 lfd 加1 啊?对,当然我们应该定义变量。

什么变量?比如说有一个 max f d 可以吗?等于什么呀? lfd+1, 可以吗?你们合理吧?这样,再问一个问题,这个max f d的它的值是不是应该是变化的?是变化,为什么变化?因为你后续是不是有不断有新的文件描述符加入啊,这个值是变化的,这个值要改,现在第一次咱们先给他设置一个maxfd,maxfd吧,好,第二参数是不是&readfds啊?它是不是一个地址?同学们,好,第三个参数设置为NULL,第四个参数 NULL ,我们只是让内核保管监控可读事件,

是不是最后参数呢 NULL ?这样的话是不是可以永久阻塞,直到有事件发生?如果没有事件发生,这个select函数 是不是一直阻塞?是不是同学们?好判断异常情况,如果说这个 n ready<0,是吧同学们,好,咱们判断一下,<0是不是有异常情况发生了?在这我给大我前面给大家说过一个,说过一点,是不是这个信号可以将这个阻塞函数给中断的,

是不是?在这我们需要判断一下是不是被信号中断了?如果, error number 等于什么呀?你什么?EINTR?是这个,这是被谁啊?被信号什么样?中断。好,如果被信号中断的话,你说这个是一种错误吗?不是,你不应该把它视为一种错误。干什么呀? continue 吧,是不是这样的?continue 就可以了。那么除此之外,是不是break,除此之外,是不是错误?我这这么写可以吗?好,可以。好,那这个什么呀?这个是异常情况是吧?同学,好,下面。

下面是什么呀?是真正的有,有这个可读事件发生的,能理解吗?那这个可读事件我给大家说过了,有两类,一类是,有客户端链接发过来了,是不是链接请求啊?一类是有数据发过来了,两大类,大家注意要把这两类看清楚啊,一类什么呀?有客户端链接请求到来。

好,这怎么写?那么你怎么知道这个是不是客户端链接到来了?你怎么知道的?我问大家,如果说是有客户端链接到来的话,你应该怎么办?我先问第一步,第一个是第一个,你如何判断是不是客户端连接到来?怎么判断?

是不是你判断文件描述符就可以了,知道什么意思吗?那么这个只要是这个l、f、 d 这个文件描述说有变化,它一定是什么呀?他一定就是有客户端连接到来,是不是这样的?加 一个 判断

if什么呀?当然后面这些是不是有?肯定是接下来后面是不是有这个有客户端链接到来,或者是有这个数据发过来了,那接下来当然你是不是要分情况处理啊?

这个是什么呀?有客户端数据发来,是不是两类?同学们,是吧?同学们,好,你首先得判断一下,先判断一下如果什么呀?如果lfd 等于什么?等什么呀?什么?当然你这样这么写,这个不是等于什么?是不是你得判断一下这个 l f d 这个文件描述是不是有变化啊,调用哪个宏啊,然后怎么着?搞不清楚了,同学们,是不是FD_ISSET()?

是不是这个?然后 什么呀?是不是lfd?这个是 readfds是不是这样的?那么如果这个成立的话,是不是我们就认为它一定是有客户端链接发过来的,能理解吗?因为这个readfds 是不是一个输出参数啊?也是内核已经告诉你,发生文件描述符变化的,都给你保存到这个readfds 这个里面来了吧。

那么接下来是不是你得挨个去判断呀?但是内核没有告诉你具体是哪个,告诉你了吗,没告诉你具体哪个,咱们后面会有依附模型会具体告诉你哪一个,现在他没有告诉你具体哪一个,所以你要挨个判断,好这个宏,FD_ISSET()这个宏就是用来判断这个文件描述符是不是在这个集合当中。

如果在这个集合当中我们认为这个文件描述有变化,是不是?那么在这儿有变化指的是什么呀?指的是有客户端连接发过来了吧?是不是?好?如果是的话怎么办?说你最直接的想法,现在就是由客户的链接发过来的,你怎么办?是不是你要调用 accept接收新的链接啊,是不是这样的?然后接收,新的客户端链接请求,调用哪个函数,是不是应该返回一个新的文描述符吧?

accept  ,第一个参数是什么?是不就是lfd?可以吧,咱们先来简单的。好,接下来继续。好,现在我们已经得到了一个新的文件描述符,接下来该怎么办?仔细想,那么你是不是应该把这个新的文件描述符 继续委托给内核帮你监控啊,能理解吗?同学们,好,怎么写?是不是把这个 cfd 加入到这个集合当中去啊?怎么加?让我给你写个注释吧,将cfd 加入到readfds集合中,是不是这样的?这个怎么写?FD_SET(cfd,&readfds)

第一个参数什么呀?第二参数,第一参数是cfd,第二是不是&readfds啊?是不是这样的?好,这个加进来了是不是?同学们?加起来,你说这个你怎么判断是吧?同学们,你怎么判断这个处理完没处理完?我举个例子,假使说举个例子,假使说这个目前只有一个客户端发布链接来了,发布链请求来了,那么你说你这个代码还用往下走吗?

你这个是不是相当于处理完了?知道什么意思吗?如果现在仅仅是有一个客户端链接发过来了,那这个 nready 返回几?是不是返回1,那你说这种情况下你还用往下走吗?是不是你这是不是应该加逻辑?什么逻辑? if 什么呀? nready 什么?是不是--nready?等于0,知道这句话的意思吗?同学们,如果这=0了,干什么呀?continue?是不是继续往上循环吗?就不要往下走了吧,能理解吗?你或者是把这个lfd 写在一个大循环里面,是不是也可以 for i 等于0? i 小于nready,

nready是不是也可以,也行也行?这个知道什么意思吗?同学,好,这里边忽视了一个细节,哪个细节?我问一下大家,现在我们把这个cfdd加入到这个集合当中来了吧,你说这个让内核的监控的范围是不变了,要怎么改?如果什么呀? maxfd<cfd吧?怎么样?小于谁? c f d 怎么办?这个 maxfd是不是=cfd啊? 是不是这样的?反正你这不加 1 的话,你这就写个加 1 呗,可以吗?可以吗?同学们,你这是不是去掉这个就可以了?是不是一样?同学们,一样,这个能理解什么意思吗?这是不是修改谁啊?这是修改内核 监控的文件描述符的范围

你第一个参数是不是要告诉你和监控哪些?他总有一个范围吧,是不是?你不能让他监控一些无用的吧?是不是?监控无用的。好,这个大家知道了。好,接下来看后面这个。下边这些是有数据发过来的情况,是吧?同学们,

好,接着看。

好,那么下面是不是有数据发过来的情况啊?那么有数据发出来的情况,你说你该怎么办了?是不是接收数据发数据,完事了?好,接着看啊。好,那再想一个问题,你说有客户端数据发过来,是不是有可能有多个客户端同时发数据过来啊?有没有可能?所以这个应该是不是写在一个循环里面,是吧?同学们?for i 等于几?从几开始?这个文件描述符应该从几开始?

那么大家可以想这个问题,你说这个通信文件描述符和这个监听文件描述什么关系?第一个通信文件描述符应该是几?按正常来说是不是应该是 lfd 加1?你这么来说,知道什么意思吗? lfd 加 1 能理解什么意思吗?能看懂吗?同学,你第一个文件描述符,我们是不是你判断的时候是不是?接下来你在这个for循环里面,你是不是就不用再判断这个监听了?因为监听文件描述符,是不是我们上面已经做过了?好,那么你说这个 i 小于几?是不是maxfd啊,是不是?你或者写什么呀?或者写小于maxfd加 1 也行,是不是?

这样咱们就直接等于吧?等于吧,知道什么意思吗?i++,接着看大注意,这个i是不是就指的是文件描述符了,能理吗?好,接下来,你该干什么了?那么从i,这从这个文件描述,从 lfd加1 一直到maxfd吧?好,这些里面我给你画个图。

这个是不是在文件描述符之间啊?在这个集合当中,是吧同学们,这个集合当中,那么假如说这个是lfd,我给你稍微写一个,后面这些是不是都是通信文件描述符了?这个 这个   这个 等等是不是达到这什么呀?这叫maxfd吧? 可以吗?直接maxfd  好,那么看一下,从这儿到这儿这块区域里面,这个范围里面不见得每一个文件描述都有数据过来吧,对,是不是?你要判断?怎么判断?

是不是判断一下这个文件描述符, 是不是在这个集合当中啊,如果在的话,那么这个文件描述上一定有数据发过来,是不是这样的?叫什么?是不是if啊?FD_ISSET(),这是不是写i啊?对,后面&readfds吧?是不是?如果这个为真的话,是不是我们认为有数据发布来了?

有数据发过来,有数据发过来,你该怎么办了?怎么办?是不是接下来你要读数据了?是不是因为这个?是不是已经确定了?同学们,那么好,我再问一个问题,你说这个时候你读书数据, read 会阻塞吗?  可能会阻塞吗?一定不会阻塞。

为什么呀?因为内核告诉你有数据,有数据是不是read会返回啊?读数据,是不是n?等一个 read 是吧同学们,然后这个是 i 吧?是吧?当然你可以把这个 i 给他赋值成socket 也可以, 就这么写吧。

哎,接下来说是 buffer 了, sizeof什么呀? buffer 好,读完之后,那么大家想一下,那读数据的过程当中是不是有可能对方会关闭连接啊,是不是有可能这个读异常啊?好?读异常会怎么办?是不是m<=0就读异常了,是吧?读异常或者是对方关闭链接吧?怎么办?想仔细想 ,首先第一个,你首先得知道要关闭这个链接吧,是不是你得知道啊?是不是close?同学们,

close(i)

是不是这个?好,这是一个,再有一个还有别的吗?那么这个链接关闭了,你还有没有必要让内核帮你监控啊?是不是你要将这个文件描述内核当中给拿回来啊,是不是去除掉啊?调用哪个函数?将文件描述符 i 从哪从内核中去除,是不是去掉啊? 调用哪个函数

FD_CLR(i,&readfds)?是一个吗?第一个参数是什么?是不是i啊?第二个 &readfds 是不是这样的?这个能看懂听见没?那你不能说你光往内核当中加,你不去啊,你这样的话是不是内核当中迟早会满啊?是不是?而且正常情况下,这个链接一旦关闭,

这个文件描述是不是意味着没用了?没用是不是应该不让内核监控了?这是合理的,是不是有上就有下呀?有进有出是不是?好?这个好,后面是不是这个正常情况下是不是要发送数据给客户端啊?叫发送应答数据叫write应答数据给客户端,这儿怎么写?是 write就可以了。

i吧?第二个参数是buffer。第三个参数是n,当然,你这个数据自己组织好吧,反正这个给他发回去是不是?好,那么接下来是不是?当然正常情况下,我们这个是不是应该写在一个循环里比较合适啊?是不是这样的?

也有可能是不是对方给你循环发呀?所以这个我们写在一个循环里面,这比较合理吧?同学们,是不是?好,那么看一下我们这个思路,大家想仔细看这个思路。

在这个思路里面,第一个你要知道是只要是内核告诉你了,有数据可读了,那么你在调用read的函数的时候,它就不再阻塞了,这清楚什么意思吗?好,第二个问题,那么你说这个 accept
在这会阻塞吗?是不是也不会啊?因为内核已经告诉你了,是不是有客户的链接发过来了,这个时候你只要用调用accept,它是不是就立刻返回呀?是不是?好整体上呢?我们这个就这样,就这么一个,当然最后干什么呀?是不要close?谁?是不? l f d 然后 return 0,就结束了,然后我这个我看这个写在哪了?行吧?反正这个不一定是配对,反正这个是写在应该写在man函数里面吧,是不是?主程序里面?那么整体上我们这个程序就是这么一个套路,这么一个套路。

好,我再问大家一个问题,我是不是跟大家说过,这个参数是不是既是一个传入,一个传出啊?那么你这样写行吗?行不行?你这样写是不是下次给你改了?那么一改之后,你像这些东西是不是可能就没有了?应该怎么办?是不是我们应该定义一个什么呀?我们应该定义一个中间变量啊,能理解什么意思吗? fd_set  tempfds;

是不是我们这样比较合适?同学们看一下,tempfds=readfs; 是不等于一个谁啊?readfs 知道什么意思吗?然后的话,你前后的话这干什么呀?是不是要写这个呀?能理什么意思吗?那这样的话,他一旦改的话,是不是只改这个呀?是不是不改这个?那每次是不是你都给他赋一个值就可以了?但是后面的话,如果说这儿是不是也要改啊?

是不是加进来的时候加进来用改吗?同学们,加不用改吧,减呢是不是也不用改?是不是整个就合适了?哪个判断?那判断也得改,是判断这是不是得改?这得这个得改,是不是这也得改?因为你这个值是不是内核给你返回的值啊?这个值是不是才我们才知道到底有没有这个可读事件发生啊?是不是这样的?好,整体上我们这个思路就讲这么多,就讲这么多,下面给大家点时间,把这点咱们到时间了。

下面咱们就一块来写一下这个。好,那前面这几步什么创建socket,端口复用绑定监听这一步,是,这几步是不是都是固定的?好,这个咱们今天讲的,咱们一块来用一下。这怎么用?是不是?首先定一个什么呀?opt吧 等于一个什么呀?1,然后 setsockopt,第一个参数是什么呀?是不是 lfd? 第二个参数是什么呀? SOL_SOCKET,第三个是SO_REUSEADDR,第三个,最后一个是不是这个? &opt啊?是吧? 最后一个是size of 什么呀? int是吧?这个咱们再给你用一遍啊,好,这是端口复用绑定监听到这为止,是不是我们这个程序已经处于监听状态了?

那接下来该干什么了?是不是我们要将这个监听文件描述符加入到这个集合当中去?所以我们需要定义一个变量吧。

什么变量?定义 fd_set类型的变量。fd_setreadfds;是不是我们还需要一个中间变量是吧?fd_set tmpfds;

你定义了是不是要进行初始化啊?同学们,是不是初始化要清空也行吧,清空readfds 和tmpfds集合。

FD_ZERO(&readfds)

FD_ZERO(&tmpfds)

这个好,这个清空了,接下来该干什么了?顺着思路走,是不是我们要把这个监听文件描述符加入到这个readfds 这集合当中去啊,是不是要委托给内核,让内核帮我们监控啊?是不是这样的?好思路,写清楚,将lfd 加入到谁 read f d s 中,委托内核什么呀?监控?好

FD_SET(lfd,&readfds )  现在是不是你加进来了?同学,也就是说现在的这个,我们这个lfd已经在这个readfds 这个集合当中了,接下来该干什么了?是不是进入while 1循环等待事件的发生?是吧同学们,while 1,  是不是要写 while  1?

同学们, 这个里边是不是我们这个要调用 select 阻塞等待事件的发生啊?阻塞等待,那为此我们需要一些变量,定义 int nready 可以吧,是不是?咱们想到哪写到哪,好,接下来是不是调用什么样?这个 select select 第一个参数是什么呀?第一参数是不是一个范围啊?这个范围是不是我们应该是需要这个范围啊?开始这个什么呀? int maxfd 等于什么呀?是不是lfd 加1?

是不是这么写就行了,是吧?第一个是 maxfd 加1,第二参数是不是一个集合啊,哪个集合?是不是&readfds?对,第三参数是NULL,这是不是应该写这个?

tmpfds =readfds。

那当然我们要监控的是让他监控这个就可以了,因为这个是不是一个传入传出参数了?这个值是每次都改,同学们,只要这个 select 返回这个值就改(tmpfds),能理解吗?一定要注意这个,你要是不听我讲的,你可能自己看书可能看不出来,好,这个是不是后面也是NULL,我们让它一直阻塞可以吗?直到有事件发生 好,判断一下,如果 n ready 小于0,

是不是有可能是这个有异常情况,那你判断是不是被信号打断了呀?errno =EINTR  ,这是被信号什么呀中断?这个咱们下面是不是跟你说过啊,说过吧同学这个信号的能够,能够使这个阻塞的函数是不是这个解除阻塞是不是立刻返回呀,对吧?

那么对于这种情况下怎么办? 是不是直接continue就可以了,后边呢,同学们 break 是break,那么 break 相当于跳出这个循环吗?是不是认为是发生了异常了?

这个时候我们就不再让 select 在那帮我们监控了 直接退出程序可以了,后面的话,接下来现在写的话是不是 两大分支啊?同学们,是吧。一种什么呀?有,有客户端链接请求到来。好,那么另外一种情况是什么呀?有数据发来的情况,是不是两种情况?同学们,两种情况。

好,那么先看一下这个有客户端链接发过来的情况。那么怎么,我怎么知道是有客户端链接发过来的?那么有客户端连接发过来。这个 l f d 这个是不是有变化呀? 是不是?好,判断一下 if 什么样?(FD_ISSET(lfd,&tmpfds)),因为你要搞清楚。 同学们,每次 select  一返回这个值是不是就有变化,这个值就改了给你,那么你要检测的是不是应该是检测这个集合?你不要搞反了,不要搞检测它readfds,清楚了吗?你检测它之后肯定成立,因为你是不是上面加进去了,是不是你检测他肯定成立,那这个就未必了。

如果没有这个新链接到来的话,那么这个条件是不是一定不成立啊?好,那么现在假如说他成立了,这个里面你做什么啊?是不是接收这个链接啊?同学们,这链接我们需要一个什么呀? c f d吧。用到哪个写哪个,

这个 c f d 的什么呀?是接收新的客户端链接请求,是不是 c f d 等于什么?

Accept(lfd,NULL,NULL);

关于这个,关于这块我给你把这注释加清楚点,因为这一块有同学可能第二个参数tmpfds 是什么呀?是输入输出参数。好,那么第一个对输入来说什么叫输入?输入指的什么意思?同学们是不是告诉内核要帮我们监控哪些文件描述符了?嗯,是告诉内核要检测哪些文件描述符 是不是这样的好。输出什么意思?是不是内核告诉我们内核告诉谁?告诉应用程序有哪些文件描述符发生了变化?当然他是不是没有告诉你具体是哪些呀?你是不是还得自己去判断呀?好,怎么判断是不是就用这个宏来判断呀FD_ISSET?是吧同学们,好,这个好。现在我们已经接受这个客户端链接了。

好,这个新的链接是不是也要加入到这个集合当中去,是吧?将 c f d 加入到,是吧?集合当中吧?哪个集合?同学们, readfds,这怎么写? FD_SET(cfd,&readfds)?这不加起来了吗?是不是现在进来了?那么到这是不是这个就结束了?同学们,没有。

那么这个内核帮我们监控的这个范围是不是实时动态的?是不是变化的?明白吗,这个应该是变化的,所以这个值是不是要调整一下,这当然上面是不是已经改过了,这再判断一下,这是修改。修改什么呀?select的什么呀?监控范围?当然这个应该是的监控范围,是不是内核是不是调用一个 select函数了?内核的监控范围,怎么修改?判断一下吧?如果 maxfd<cfd, maxfd 等于个cfd,但是在这里为1,不一定说每次这个 cfd一定大于maxfd吧。

我是不是跟你说过了,中间那种挖空的情况是不是再分配的话它就不一样了?对,你要搞清楚,这可以测的好这个,那么还有一个情况,那假如说只有一个客户端链接到来也没有数据发过来,

这个时候是不是我们就没有必要往下走了?再加一个什么呀?如果 nready 这个 减减应该写前面吧,如果等于 0 这个的话,这个的意思是不是只有一个客户端链接请求啊,

并没有任何其他的这个链接,或者是有数据发过来的情况吧,这个时候你还有必要往下走吗?写continue就可以了吧,这个意思知道什么意思吗?同学,

好,这个是不是写完了?下面是不是有数据发过来的情况啊?好,那么你怎么知道有数据发过来?是不是还得使用FD_ISSET进行判断啊?当然有数据发过来的情况是不是一定是通信文件描述符了,是不是?好,所以是不是需要写个循环的?当然我们是不是需要一个什么呀?int i 吧?,循环因子。

好,看一下for i 等于多少,他从几开始?同学们应该是从监听后面啊,是不是应该从监听后面了?你想一下,这个通信文件描述符肯定比谁大呀?比监听文件描述符大,因为你先有监听文件描述符后有谁?是不是通信的?i<=maxfd, 这个lfd加 1 是不是?当然你写它,写它的话是不是大不了后面的判断不成立吧,是不是?相当于多做了一次循环吧?对,好,这个是不是i++啊?好,先给大家看一下。

那么是不是你要挨个判断它有没有变化?怎么判断是不是if, FD_ISSET,那么看这个 i 是不是在这个集合当中啊?在哪个集合当中啊? &tmpfds 是不是?这个为了引起这个不必要的混淆的,我们可以再定义变量,比如说int sockfd 可以吗?

我们把这个什么呀?这个 sockfd等于个什么呀?i,所以你说这样可读性好一些,因为我读或者是写,是不是都写这个sockfd啊,这样容易理解一些?好,这个如果成立的话说,是吧同学们?这个如果成立的话,是不是我们认为这个i这个文件描述是不是有变化了?

对,有变化,是不是?你说这种变化是什么变化呀?同学们是不是正常情况下应该是他发数据过来了? 或者他断开链接,是不是我们这也可以监测到啊?是不是?因为这个?大家还记得吗?我们在测试的时候如果说对方关闭链接了,是不是 read 函数返回0啊?还记得 read 函数返回 0 好看一下,咱判断一下。

那么接下来该干什么了?是不是读数据啊,是不是读数据?咱们是不是需要一个n?同学们,n= Read 第一个参数什么呀?sockfd,第二个参数是buf,第三个参数 size off,是不是这两个变量上面需要定义一下吧?,是吧?你自己定义。当然我们在每次读之前要记得干什么,是不是进行初始化啊,像这些小细节,我们在思路当中就不跟他写这些玩意了,是不是你只要把大体思路搞清了,细节你慢慢去填0x00,然后sizeof(buf)。好判断一下。

那么如果说这个 n 小于等于 0 什么意思?是不是对方关闭链接或者读异常了?这个时候是不是你要这个关闭链接了? 是不是 close 什么呀?sockfd就可以了。同时是不是这个因为这个链接已经关掉了,我们是不是应该让内核不再监控它了?怎么让内核不再监控它了?是不是从readfds 当中把这个文件描述给它抠除掉啊?怎么写?将sockfd 从readfds中删除,怎么样删除?同学们,FD_CLR(sockfd,&readfds)?

这样的话,这个readfds是不是有变化了?是不变化了?那这个变化是不是我们,是不是我们人为修改的?好,到这了,这种是异常情况吧,是吧?同学们,好,后边这些呢?咱这儿的话是,这儿是不写else,知道什么意思吗,那么上面这些是异常的,下面是不是正常的? 当然你在这写是不是也写continue也可以啊

是不是继续这个循环也行啊?你这写else 也行,是不是一个意思啊?好,这样的话,那么除了这种异常情况以外,接下来这些是不是相当于我读到数据了?读到数据之后你可以把这个数据打出来,可以吗? 是吧?那接下来咱们做一个什么样的这个事情?咱把 小写转换大写,

然后再发回去,可以,这个 for i 等于一个0,你想想这儿用这个i行吗?你要注意,这样的细节你得搞清楚,你用 i了不得了,这儿是不是全乱套了?所以在这儿我们是不是需要再进一个变量啊? int k等一个 0 可以吗?

这个相当于是个内部循环吧,k=0,然后 k 小于个几 n ,k ++,这怎么写?是不是这个 buf[k] 啊?等一个 two buffer,这个相当于是不是转化完了?转化完以后,你调用 write函数给他发回,给他写回就可以了,write 第一参数是这个sockfd,第二参数是buf,第三参数 是 n,这个是不是相当于回去了?是吧?同学们

,好,发回去了。那么好,有些细节你还得需要关注一下,看一下。

这是什么呀?这是我们在判断是不是这个有变化啊,是吧?给你加注释,判断 sockfd文件描述符是否 有变化,所谓的有变化是不是有数据到来?嗯,是不是就是有数据到来的意思?大家注意,这些都叫事件,是不是我们现在监控的是不是可读事件啊?

可读事情对于我们来说是不是有两大类朋友们,一个是有客户端链接发过来,一种是有客户端有数据发过来吧,当然这个客户端关闭链接是不是也是可读事件的?也是可读事件,好这个这一块就写完了。

那么还有一个,你是不是得判断一下这个 nready的值?是不是得判断一下nready的值,

这个判断在哪判断?同学们, 你是不是应该是写在这个里面?是不是写在这里面?写在外面行吗?同学,你要注意,像这样的细节你务必要搞清楚。 nready是返回的,是有变化的文件描述符个数,你写在外面,它是吗? 不一定是吧?所以最好一定是得写在里面,是不是?不要说一定了就应该一定写在里面,是不是应该写在这个里面?

是不是应该写在是这吗?我看里边是不是这里面?是不是这样的?是这吗?嗯,这是异常的,这是另外一种,是这吧?if什么样?减减nready等于个0,干什么呀?这直接 break 就可以了吧?是不是?那么大家想一下这么写有什么好处?是不是可以减少循环次数?能理解吗? 我给你举个例子,这些。

是吧同学们,假如说现在这个maxfd 到这了,那么你要装内核监控是不是这么一个范围啊?是不是同学们, 这个时候这里面只有一个有变化,就数据发过来了?是吧同学们,那么前面是不是你只要弄了一次这个玩意儿,是不是 nready加减等于0了?是不是就直接这个就不再判断了?能理吗?是不是后面就不再执行了?所以这个nready 的加减是有必要的,可以帮助我们减少循环次数。

那么有同学问老师,我不写这个nready 加减行不行?也行,是吧?因为什么呀?因为你这个是不是有没有变化?你在这里面有判断呀?没有变化,是不是相当于做空循环啊?相当于做了一些空循环,无用的循环,倒不是不可以。那只能说我们这样写是不是更优化呀,是吧?更优化。好,这个我写在这,这写完了,咱们编译一下看,对不对呢?

g c c 杠 o 是吧? select s 这个 wrap 点c,看来同学们,咱们写代码是不是一步套k?好,咱们试一下是吧?端口多少?端口是不是 4 个 8 啊?nc 127. 1  8888 回车。hello,没问题,这是不是也有反应?这个 n 指的是什么呀?指的这个字节数吧。

再来一个 nc 127. 1  8888   你好,是不是也没问题?

是不是也有啊?再来一个 nc 127. 1 ,是不都没事了?现在一共有几个链接了?

没问题吧,我们这个是不是都可以收到数据啊?同学们,都可以收到数据,那么这个先给你说到这,然后这个我们看这个反馈值,这个 n ready 是不是返回值?同学们,这个值是这个数吗?咱们把它打出来看一看,

再编译一下,起来,是几?是不是一?你说再来一个是还是返回几?是不是也是一?你要注意一点,只要 select 给你返回了,而且你处理了,那么下次就不再给你返回那个数了。

知道什么意思马?我举例子,假如说这个内核当中这个文件描述是不是有变化,但是你没处理,下次是不是还给你返回?对,要搞清除,下次还通知你,是不是又返回1,

当然你发数据的时候是不是还给你返回1?知道什么意思吗?这是不是连着写两个1啊?第一个 1 是不是建立链接那个1啊,新建链接那个,这个 1 是不是发数据的?现在刚才这是不是有 3 个1了?看清楚再给你来一个,接下来是不是连着 2 个1啊?没问题吧。

好,那么看一下退出的情况,结束一个是不是也有一啊,是吧?同学们,现在的数据是有几个链接啊?这个是,这个退出了吧,是不是应该是有两个链接啊?同学们看是不是两个nc吧?是不是两个啊?那么主动关闭方是不是处于TIME_WAIT的状态?那么主动关闭方谁啊?同学们,是不是nc?客户端是主动关闭一方,所以他处于了TIME_WAIT的状态,这个状态是不是大概持续这个一分钟不到啊?可能一分钟不到是不是?好,这个咱先说到这,这先说到这,好,看一下。嗯,关于这个 select 这一块,这个代码实现咱们就讲完了,一个基本的版本是不是都有了?好,看一下。

关于这个 select 的思考,第一个思考是这个,那么如果有效的文件描述符比较少,会使循环的次数太多,知道什么意思吗?这句话能看懂什么意思吗?

我举个例子看一下这个是不是我们的文件描述符集啊,这个集合当中是不是你委托内核帮你监控这个文件描述符啊?同学们,好,那么有没有这种情况发生?我举个例子,那假如说,前面这个都加进来,是不是同学们,都加进来了,看一下是不是有这么多啊?前面这些一直是不发数据的

它只建连接,不发数据的,知道什么意思,那么只有最后一个发,那么你说我们那个,后面那个有数据到来的情况,那么你再做判断是不是要循环一圈了?是的,是不是这样?大家注意,这种是一种极端情况,但这种情况是不是也存在?那么如何去解决这问题呢?

知道什么意思吗?同学们, 我这个话听懂什么意思了吗?同学们啊?再看,再看,那么有没有这种情况出现?同学们,有没有这种情况?那么这些都被挖掉了,是吧同学们,这些都挖掉了,有没有可能?唉,都挖掉了,

后来也没有链接过来,那么这个也就说这个里面中间有好大一部分,是什么呀?是前提是不是不再让内核帮你监控了,是吧?不再让内核帮你监控了,那这个时候你说你应该怎么办呀?那你在循环的时候是不是?你这样的话,你,你这个 maxfd是不是在这儿了?

知道什么意思吗。同学们,你的maxfd是不是让内核帮你监控那个范围啊?那么也就是中间是不是有好多?是没有的?有没有可能?是不是?那么这种情况你该怎么办?我这上面跟你说了解方法,我把这个解决方法跟你说清楚以后,你自己改一改。

那怎么办呢?可以将有效的文件描述符放到一个数组当中,那么你在遍历的时候呢,你还用非遍历到这个位置吗?知道什么意思吗?你这个数组当中存放的都是什么呀?有效的文件描述符,这句话理解什么意思吗?我举个例子,现在你这个有效,是吧?这个有效,那么是不是我们可以把这些有效的放在一个单独数组当中,给它记下来啊,是不是?这放过来,然后呢?这个放过来,咱这个什么呀?也放过来。

那这样的话,我这个数组当中你在循序环的时候,是不是你循环 4 次就够了,你还用循序环?这么中间这都好多无用的吗? 是不是不用了,意思听明白了没有?明白,那么其实这个优化的方便在哪?让你尽可能的减少循环的次数,是不是?让你的循环一定是什么呀?最好是有效的,因为像中间这些好多是空的,是不是就是无效循环啊?能理解,是不是这样的?先给你打个招呼,明天讲东西有点难,这个不能蒙的,这个一定要保持清醒的头脑啊。

这个意思听明白了没?同学们,这是这个优化的方向,你把这个优化一下,那么其实这个优化的数量其实也很简单,我大致给你说一下,那么你看一下我这个代码里面,这代码里面,是不是你只要这个获得一个新的链接,你要把这个新的链接加入到这个数字当中去才行啊?

当然最后如果说这个文件描述符呢,这个关闭链接了,是不是你要把这个数组当中这个给他清掉啊?是不是给他清掉啊?你不能是光加不减的啊?是不是?好,怎么标识?它已经被清掉了,怎么标识?是不是加 flag 的同学们还记得吗?

是不是?当然我们这个的话是不是文件描述符是不是都大于 0 的啊?你可以定一个什么呀?整形数组可以吗?整数组,那这个数组里面是不是每一个元素都是整形的?那么这个整形值是不是就是用来保存文件描述符的,可以吗?你初始化为-1,

表示未被占用。如果说大于 0 是不是就已经被占用了?那么你找的是不是要找这个- 1 的情况了?找到之后填在这就可以了吗?是不是?而且你最好是你记录一下这个数组的这个什么呀?最大下标,知道什么意思?那我在这个数组里面填充的时候,那么是不是我肯定有一个最大值,这个下边是不是有一个最大值?再想一个问题,再想,那么有没有可能我这个数组里面有空的情况?有没有?这肯定有啊,这用说吗?如果说中间的某一个文件描述被关掉,是不是它就置为- 1 了?嗯,这个- 1 的你还用往下走吗?

同学们还用去判断一下这个FD_ISSET吗?是不是不用了?你是不是判断一下他对他就可以了,判断这个总比你判断那个FD_ISSET 要快吧,是不是?是这个,而且你得知道,你得记住一个下标,这个数组是不是有一个下标?你你,你总不能说把这个数字整个再循环一遍吧?

你如果这样的话还改还有什么意义啊?能理解吗?是不是同学们?改的方式,我给你写两句,我看在那这个半天不吱声。不吱声有两种情况,一种情况,这确实学会了,懒得说。另一种根本是不会。没听懂,你们处于哪种情况啊?

代码优化方向。

第一句话,将文件这个是不是将通信文件描述符啊?   是不是?将文件描述符? 保存哪了?保存到一个什么呀?整型数组中。那在遍历的时候,是不是你遍历这个数组就可以了?是不是?应该什么呀?使用一个变量记录什么呀?数组,

已用数组中最大元素的下标,你记住这下标的目的什么?同学们,是不是你在循环的时候,是不是循环到这个下标就可以了?还有往后循环吗?还有什么呀?反正你知道什么样,你还得记得什么样?如果数组中,什么呀?有无效的文件描述符是吧?直接跳过好,那么你使用的时候是不是我们这儿是不是应该这样?我是不是应该写这个?

比如说第一个什么呀? int client[1024] 是吧?首先你是不是有一个循环?是不是将这个所有的全置?为什么呀? -1 吧,client[i] =-1; 是不这样的?那么你后续在用的时候,是不是只要这个等于-1,代表示没有被占用吧?没有被占用,是不是你可以把这个文件描述保存到这个位置啊?是不是?同时是不是要不断的修改这个值啊?最大下标这个值啊,比如说叫 maxi可以吗?

是不是修改这个值,这个值应该是一个变化的值,是不是和 select 第一参数是类似的?好,整体的方向就这样子,你自己去改,好吧,优化一下。这个优化听懂了吗?你先把这个代码给他,完完全全看明白了,好吧?然后你再去优化。

好,这说到这个简单跟你说一下这个关于 select的优缺点,一块来说一说。第一, select是不是可以做到一个进程 可以支持多个客户端啊,是不是这样的?第二, select支持跨平台,这个你当做一个常识知道就可以了。那么 select可以用在 unix上, linux上都可以。但是后面咱们明天讲的这个pool,它不能,是不能用在unix,不支持跨平台是这意思,这是它的优点。

缺点是第一个 代码编写困难,这个困难吗?同学们?困难不困难是相对的,是不是?你如果写的多了,写的比较熟了,这个一点也不困难,是不是?第二个会涉及到用户区到内核区的来回拷贝?这个知道什么意思吗?哪一块涉及到这个了?

是不是 select的第二个参数了?第三个、第四个参数了,是不是?你告诉内核,是不是内核把数据拷走了?内核告诉你是不是内核把数据拷到用户区的那个readfds那去吧,是不是这样的?所以这个涉及到了用户区去到内核区的来回拷贝。

第三个当客户端有多个链接,但少量活跃的情况, select的效率比较低,这个很明显的是不是?我跟你是不是说了一个极端情况,是前面都不活动,这是不是都后面一个活动?是不是这个是一种极端情况?那最多它支持 1024 个客户端链接啊?这个什么意思啊?同学们,是不是这个?他的意思是不是你最多可以用 1024个客户端同时发起链接?再多了还有吗?同学们,还能支持吗?支持不了了,如果想支持也行。

你要修改内核代码,修改哪个?修改这个宏叫FD_SETSIZE,这个宏是1024 ,这个你要修改那个宏的话,是不是你还得干什么呀?你还得编译内核吧 是不是?你不编译能行吗?你要编译,前面咱是不是跟大家说过这个,我们一个进程可以打开的文件描述,

应该打可以打开的文件描述,个人最多多少啊?这个数字可以改的FD_SETSIZE,大家还记得我们这个ulimit -a吗?这个命令吗?是吧同学们,这儿这个什么 open files,这个是不是?我在这是2048,当这个我改过的,这是可以改的,那这个值就告诉你,你这一个进程可以打开的文件描述符最多个数多少,就是2048,这个值在哪改呢?明天咱们会说,明天会说那个是以后可以改的。

但是 select 它这个限制不是受这个限制,它受的这个宏的限制,这个宏知道什么意思吗?它俩是不是一样的?这个红你改大了它就大,改小他就小。但是你改之后呢,这个东西以后,你要编译内核这个比较麻烦,这个通常你是没有权限这么做的,是不是?那么你连这个 root 用户的权限没有,

你能改这个吗?是不是要改这个?是不是要有 root 用户权限啊?改这个完之后是不是要把整个内核代码重新编一下?编译完以后之后干什么呀?要重启吧?是不是很显然你是没有权限这么做的。

嗯,作业的话,这个作业你有兴趣就写一写,有兴趣写一写,没有兴趣的话,那么你就把我给你留的那个作业哪个还记得吗?优化的那个,你优化优化。

行,明天来以后咱把那个说一说。好,好,这个关于这个优缺点咱就说这么多,说这么多,现在我们这一个进程里面是不是就可以支持多个客端了?是不是我们也没有用什么多进程,也没有动多线程吧?好,这里面我给大家出一个错题,看你们能不能想到看一下,假使说同学们 这是我们的代码吧,假使说我们的代码里面,同学们注意看,我们这个代码里面,如果说处理数据的时间比较长的话,会不会造成我其他的客户端的数据得不到及时响应啊?

为什么呀?因为你这个处理数据是不是?

是不是一个链接?一个链接来的?是不是文件描述符?是不是循环来的?如果让你解决,怎么解决好?这个你知道就行了。怎么解决?对,创建子进程或子线程,知道什么意思吗?明白什么意思?同学们,你可以用这个,你可以使用多线程或多进程的方法,都可以,人家问你的时候你能说出来,对吧?但是这个明显是不是你能看出来?

同学们,我们这个处理这个,我们处理这个链接是不是一个 一个 来的,对,是吧?我再举个 最直接的例子,我在这里面加一个sleep 100,那就意味着是不是其他的线程,其他的这个链接是不是等 100 秒钟之后才能处理啊?是不是?但是如果说你这里边你 select 返回以后一个链接,

你用一个子进程或子线程去处理,那么就没有这个,没有这个弊端了, 是不是?没有这弊端了?知道什么意思吗?同学们,你能看出来,是吧?你们那么这样的话,这个的话是不是如果说你这个里面又使用多线程,是不是我们就相当于 select 这种模型,加什么呀?多线的模型了,这种处理的效率更高清楚了吗,这种处理效率更高。好,同学们,好,这就说到这。

29.Linux网络编程熟练掌握 TCP 状态张换图熟练堂握端口复用的方法了解半关闭的概念和实现方式了解多路10 转接模型熟练掌握 select 函数的使用熟练使用 fdset 相关函数的使用能够编写相关推荐

  1. Linux网络编程---详解TCP

    Linux网络编程---详解TCP的三次握手和四次挥手_shanghx_123的博客-CSDN博客_tcp的协议数据单元被称为 TCP协议详解(TCP报文.三次握手.四次挥手.TIME_WAIT状态. ...

  2. 【Linux网络编程笔记】TCP短连接产生大量TIME_WAIT导致无法对外建立新TCP连接的原因及解决方法—实践篇

    1. 查看系统网络配置和当前TCP状态         在定位并处理应用程序出现的网络问题时,了解系统默认网络配置是非常必要的.以x86_64平台Linux kernelversion 2.6.9的机 ...

  3. 27.Linux网络编程socket变成 tcp 高并发 线程池 udp

    好,咱们开始上课了,从今天开始咱们连续讲 8 天的,网络编程这个还是在linux环境下去讲,咱们先看一下咱们这 8 天都讲什么东西,跟大家一块来梳理一下,你先有个大概的印象,这些你也不要记,那么网络编 ...

  4. linux tcp 服务器 c,Linux网络编程篇之Tcp协议介绍, C/S通信及聊天室实现

    基于tcp协议的网络程序流程图如下: tcp协议网络程序流程图 服务器调用socket().bind().listen()完成初始化后,调用accept()阻塞等待,处于监听端口的状态 客户端调用so ...

  5. Linux网络编程-UDP和TCP协议详解

    1|0一. 引言 网络协议是每个程序员都要掌握的基础知识,干啥都离不开网络,就算在家里新买了个路由器不是吗,同事连不上网,你的女朋友手机没有网看剧了正看到高潮部分,到那时候你打开百度......那嫌弃 ...

  6. Linux网络编程——浅谈 TCP 三次握手和四次挥手

    一.tcp协议格式 二.三次握手 在 TCP/IP 协议中,TCP 协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建立连接时,客户端发送 syn 包(tcp协议中syn位置1,序号为 ...

  7. linux网络编程-多线程实现TCP并发服务器

    客户端跟服务端通信流程 服务端流程步骤 socket函数创建监听套接字lfd; bind函数将监听套接字绑定ip和端口: listen函数将服务器设置为被动监听状态,同时创建一条未完成连接队列(没走完 ...

  8. 【Linux网络编程】基于TCP流 I/O多路转接(poll) 的高性能http服务器

    服务器比较简陋,为了学习poll的使用,只向客户端回写一条html语句.启动服务器后,浏览器发起请求,服务端向浏览器写回html,响应字符串,然后可以看到,浏览器解析并显示 Hello Poll!. ...

  9. Proxy源代码分析--谈谈如何学习linux网络编程

    Linux是一个可靠性非常高的操作系统,但是所有用过Linux的朋友都会感觉到,Linux和Windows这样的"傻瓜"操作系统(这里丝毫没有贬低Windows的意思,相反这应该是 ...

最新文章

  1. 什么是StackOverflowError?
  2. 小学文凭有计算机知识,重大版小学信息技术毕业复习题
  3. ubuntu下和开发板下播放音乐
  4. cache:缓存在asp.net中如何管理?服务器端缓存?Session, Application, Cache objectscache ,客户端缓存?Cookies,ViewState...
  5. array(2019CCPC网络预选赛 hdu6703主席树+set)主席树求大于等于k的最小值
  6. 机器学习:算法模型:决策树
  7. Linux(CentOS6.4、CentOS6.3)下安装、配置PostgreSQL9.2
  8. centos中,tomcat项目创建文件的权限研究
  9. 端口扫描实验和Dos攻击实验
  10. 怎么把程序内部坐标转为屏幕坐标_各位老大,如何把屏幕坐标转换成游戏的坐标...
  11. 读《我喜欢生命本来的样子》记(一)
  12. 批量爬取网站图片-“优美库”篇(爬虫实战)
  13. SAD和SATD的区别
  14. [贪心]leetcode55:跳跃游戏(medium)
  15. key、keyCode 和 which
  16. zbb20170811 linux 给用户授予文件夹权限
  17. 爬虫selenium + chrome (PhantomJS)模拟浏览器 抓取京东商城为例
  18. ffmpeg学习笔记
  19. ttkefu微信二维码对话链接如何生成?
  20. SDL教程零基础入门 简单操作 day1

热门文章

  1. Vega数据可视化工具——教你轻松玩转大数据可视化 | 附代码
  2. 马哥linux运维15~25讲笔记(未更完)
  3. 程序员创业的瓶颈及破解方法
  4. c语言while将字符循环,C语言 while语句的用法详解
  5. python爬虫之通过pyquery爬取大众点评评论信息
  6. cd命令的各种使用方法
  7. 电脑可以开机但是无法进入到桌面怎么办?
  8. 谷歌浏览器无法安装插件解决方法分享
  9. html5blog简单特效代码,个人博客网站背景视觉滚动特效代码
  10. 时间戳转化为年、月、日、时、分、秒。