黑马linux网络编程

目录

一、网络编程

1、协议

2、分层模型结构

3、c/s模型: client-server

4、b/s模型:browser-server 

 5、网络传输流程:

 6、以太网帧协议:

 7、IP协议

8、UDP

9、IP协议: 

 10、网络套接字  socket        

11、网络字节序

 12、IP地址转换函数

 13、sockaddr地址结构

 14、网络套接字函数

 15、TCP通信流程分析

16、三次握手协议

17、四次挥手

18、滑动窗口

19、错误处理函数:

20、readn、readline:读N个字节,读一行 

21、多进程并发服务器

22、多线程并发服务器 

 23、TCP状态时序图:

 24、2MSL时长

25、 端口复用

26、半关闭

27、select多路IO转接:

28、select优缺点

29、多路IO转换

30、poll:

31、read函数返回值:

32、突破1024文件描述符限制: 

33、epoll       

 34、epoll实现多路IO转接思路:

 35、epoll事件模型

 36、反应堆模型

 37、eventset函数:

38、 eventadd函数:

40、线程池模块分析

41、TCP和UDP通信各自的优缺点

42、UDP实现的C/S模型

43、本地套接字 

44、对比本地套接字和网络套接字

 45、libevent库

 46、源码包安装:

47、特性:

48、libevent框架:

49、创建事件event

50、添加事件到event_base

51、从event_base上摘下事件【了解】

52、销毁事件

53、未决和非未决

54、 带缓冲区的事件bufferevent

55、创建、销毁bufferevent

56、给bufferevent设置回调

57、启动、关闭bufferevent的缓冲区

 58、连接客户端

59、创建监听服务器

60、释放监听服务器

61、服务器端libevent创建TCP连接


一、网络编程

1、协议

         一组规则。

2、分层模型结构

OSI七层模型:物、数、网、传、会、表、应

                物理层、数据链路层、网络层、传输层、会话层、表示层、应用层

TCP/IP 4层模型:

                网络接口层(链路层)、网络层、传输层、应用层(网网传应)

        应用层:http、ftp、nfs、ssh、telnet。。。

        传输层:tcp、UDP。。

        网络层:IP、ICMP、IGMP

        链路层:以太网帧协议、ARP

3、c/s模型: client-server

        优点:缓存大量数据,协议选择灵活,速度快,

        缺点:安全性,跨平台,开发工作量

4、b/s模型:browser-server 

        优点:安全性,跨平台,开发工作量较小

        缺点:不能缓存大量数据、严格遵守http协议

 5、网络传输流程:

        数据没有封装之前,是不能再网络中传递。

 6、以太网帧协议:

        ARP协议:根据Ip地址获取mac地址

        以太网帧协议:根据mac地址,完成数据包传输

 7、IP协议

        版本:IPv4、IPv6----4位

        TTL:time to live。设置数据包在路由节点中的跳转上限。每经过一个路由节点,该值-1。当减为0的路由,有义务将该数据包丢弃

        源IP:32位-----4字节      192.168.1.108 ----点分十进制  IP地址(string)---二进制

        目的IP:32位-----4字节

 ·      IP地址: 可以在网络环境中,唯一标识一台主机

        端口号:可以在网络的一台主机上,唯一标识一台进程

        IP地址+端口号:可以在网络环境中,唯一标识一台进程

8、UDP

        16位:源端口号,           2^16=65536

        16位:目的端口号

9、IP协议: 

        16位:源端口号,           2^16=65536

        16位:目的端口号

        32序号:

         32确认序号:

        6个标志位

        16位窗口大小             2^16=65536

 10、网络套接字  socket        

        一个文件描述符指向一个套接字(该套接字内部由内核借助两个缓冲区实现)

        在通信过程中,套接字一定是成对出现的

11、网络字节序

        小端法:(pc本地存储)        高位高地址,低位存低地址。        int a=0x12345678

        大端法:(网络存储)        高位低地址,低位存高地址   

        htonl   --》本地  --》网络(IP)

        htons  --》本地  --》网络(port)

        ntohl  --》网络 --》本地(IP)

        ntohs  --》网络  --》本地(port)

 12、IP地址转换函数

        int inet_pton(int af, const  char *src, void *dst)        本地字节序(string Ip)--》网络字节序

                af:AF_INET、AF_INER6(指代选择什么协议)

                src:传入参数,IP地址(点分十进制)

                dst:传输参数,转换后的网络字节序的IP地址

                返回值:

                        成功:返回1

                        异常:0,说明src指向的不是一个有效的IP地址

        const char *inet_ntop(int af, const void *src, char *dst,socklen_t size)网络字节序--》本地(string Ip)            

                af:AF_INET、AF_INER6(指代选择什么协议)

                src:网络字节序IP地址(点分十进制)

                dst:转换后的本地字节序的IP地址

                size:dst的大小

                返回值:

                        成功:返回dst

                        失败:NULL

 13、sockaddr地址结构

        struct sockaddr_in addr;

        addr.sin_family=AF_INET/AF_INET6

        addr.sin_port=htons(9527)

        1、       int dst;

                inet_pton(AF_INET, "192.157.22.45", &dst);

        addr.sin_addr.s_addr=dst;

        2、addr.sin_addr.s_addr=htonl(INADDR_ANY);        //自动取出系统中有效的任意IP地址。二进制类型

        bind(fd,(struct sockaddr *)addr,size)

 14、网络套接字函数

socket模型创建流程图

        

 bind():绑定IP+port

listen():设置监听上限

accept():阻塞直到客户端连接

connect():绑定IP+port

(1)socket函数

        #include <sys/socket.h>

        int socket(int domain, int type, int protocol)        创建一个套接字

                        domain:所选用的IP地址协议是什么AF_INET,AF_INET6,AF_UNIX

                        type:创建套接字所选用的数据传输协议默认是什么:SOCK_STREAM、SOCK_DGRAM

                        protocol:0

        返回值:

                成功:新套接字所对应的文件描述符

                失败:-1 errno

 (2)int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)给socket绑定一个地址结构(IP+port)

        sockfd:socket返回值

                struct  sockaddr_in  addr;

                addr.sin_family=AF_INET;

                addr.sin_port=htons(8888);

                addr.sin_addr.s_addr=htonl(INADDR_ANY);

        addr:(struct sockaddr *)&addr

        addrlen:sizoef(addr)                地址结构的大小

          返回值:

                成功:0

                失败:-1,errno

(3)int listen(int sockfd, int backlog);        设置同时与服务器建立连接的上限数(可以同时进行3次握手的客户端数量)

         sockfd:socket返回值

         backlog:上限数值.最大值为128

         返回值:

                成功:0

                失败:-1,errno

(4)int accept(int sockfd, struct sockaddr *addr, socklen_t addrlen)阻塞等待客户端建立连接,成功的话,返回一个与客户端成功连接的socket的文件描述符

         sockfd:socket返回值

         addr:传出参数。成功与服务器建立连接的那个客户端的地址结构(IP+port)

          addrlen:传入传出参数     入:addr的大小     出:客户端addr的实际大小

      返回值:能与服务器进行数据通信的socket对应的文件描述

(5)int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen)使用现有的socket 与服务器建立连接

         sockfd:socket返回值

         addr:传出参数。服务器的地址结构

        socklen_t addrlen:服务器的地址结构大小

         返回值:

                成功:0

                失败:-1,errno

如果不适用bind()绑定客户端地址结构,采用“隐式绑定”

 15、TCP通信流程分析

        server:

                (1)socket():创建socket

                (2)bind():绑定服务器地址结构

                (3)listen():设置监听上限

                (4)accept():阻塞监听客户端连接

                (5)read():读socket获取客户端数据

                (6)小写--》大写:toupper()

                 (7)write(fd)

                (8)close();

        client:

                 (1)socket():创建socket

                (2)connect():与服务器建立连接

                (3)write():写数据到socket

                (4)read():读转换后的数据

                (5)显示读取结果

                (6)close();

16、三次握手协议

         主动发起连接请求端,发送SYN标志位,请求建立连接。携带序号、数据字节数(0)、滑动窗口大小

        被动接受连接请求端,发送ACK标志位,同时携带SYN请求标志位。携带序号、确认序号、数据字节数(0)、滑动窗口大小

        主动发起连接请求端,发送ACK标志位,应答服务器请求连接。携带确认序号。

17、四次挥手

        主动关闭连接请求端,发送FIN标志位

        被动关闭连接请求端,应答ACK标志位                  ------半关闭完成

         被动关闭连接请求端,发送FIN标志位

        主动关闭连接请求端,应答ACK标志位                  ------连接全部关闭

18、滑动窗口

        发送给连接对端,本端的缓存区大小(实时),保证数据不会丢失

19、错误处理函数:

        封装目的:在server.c编程过程中突出逻辑,将出错处理与逻辑分开,可以直接跳转man手册。

(1)wrap.c

        存放网络通信相关常用  自定义函数

        命名方式:系统调用函数首字符大写,方便查看man手册。如:listen(),accept()

        函数功能:调用系统调用函数,处理出错场景

                在server.c和client.c中调用自定义函数

        联合编译 server.c和wrap.c生成server

                        client.c和wrap.c生成client

(2)wrap.h

        存放 网络通信相关应用  自定义函数原型(声明)

20、readn、readline:读N个字节,读一行 

read函数的返回值:

        (1)>0  实际读到的字节数

        (2)=0  已经读到结尾(对端已经关闭)

        (3)-1  应进一步判断errno的值

                     errno = EAGAIN  or EWOULDBLOCK :设置了非阻塞方式  读:没有数据到达

                        errno = EINTR 慢速系统调用被中断

                        errno = “其他情况” 异常

21、多进程并发服务器

        (1)socket();         创建  监听套接字lfd

        (2)bind();        绑定地址结构struct socketaddr_in addr;

        (3)listen();

        (4)while(1){

                        cfd=Accept();接收客户端连接请求

                        pid=fork();

                        if(pid==0){           子进程  read(cfd)-----小写--》大写----write(cfd)

                                        close(lfd)        关闭用于建立连接的套接字lfd

                                        read()

                                        小写--》大写

                                        write()        

                                }

                        else if(pid>0){

                                        close(cfd)        关闭用于与客户端通信的套接字cfd

                                        while(1){

                                            waitpid(0,NULL,WNOHANG);     

                                           }

                                       

                                        continue;
                                }

                }

        (5)子进程:

                  close(lfd)

                  read()

                  小写--》大写

                   write() 

        父进程:

                close(cfd)

                注册信号捕捉函数      SIGCHLD

                在回调函数中,完成子进程回收              

                        while(1){

                                            waitpid(0,NULL,WNOHANG);     

                                           }

22、多线程并发服务器 

        (1)socket();         创建  监听套接字lfd

        (2)bind();        绑定地址结构struct socketaddr_in addr;

        (3)listen();

        (4)while(1){

                cfd=Accept(lfd,)

                pthread_create(&tid, NULL, tfn, NULL);

                pthread_detach();

                }

        (5)子线程:

                void  *tfn(void  *arg){

                          close(lfd)

                          read()

                          小写--》大写

                          write(cfd) 

                         pthread_exit((void  *)10);

                }

 23、TCP状态时序图:

        1、主动发起连接请求端:

CLOSE--发送SYN --SEND_SYN--接受ACK、SYN--发送ACK--  ESTABLISHED(数据通信态)   

        2、主动关闭连接请求端

ESTABLISHED(数据通信态)--发送FIN--FIN_WAIE1---接受ACK --FIN_WAIE2(半关闭)   --接收到对端发送的FIN--  FIN_WAIE2(半关闭)--回发ACK--TIME_WAIT(只有主动关闭连接方,会经历这种状态)--等2MSL时长--CLOSE

        3、被动接收连接请求端

CLOSE -- LIDTEN -- 接收SYN -- LIDTEN -- 发送ACK、SYN -- SYN_RCVD -- 接受ACK --  ESTABLISHED(数据通信态) 

        4、被动关闭连接请求端:

ESTABLISHED(数据通信态)-- 接受FIN--  ESTABLISHED(数据通信态)--  发送ACK -- CLOSE_WAIT(说明对端【主动关闭连接端】处于半关闭状态)-- 发送FIN -- LAST_ACK

 24、2MSL时长

 一定出现在【主动关闭连接请求端】  --  TIME WAIT

 保证,最后一个ACK能成功被对端接收。(等待期间,对端没收到我发的ACK,对端会再次发送FIN请求)

25、 端口复用

                int  opt = 0/1;         // 设置端口复用

                setsockopt(lfd,SOL_SOCKET, SO_REUSEADDR,  (void  *)&opt, sizeof(opt))

26、半关闭

        通信双方中,只有一端关闭通信   ---FIN_WAIT_2

        close(cfd);

        shutdown(int  fd, int  how)

                how:        shut_RD:关读端

                                shut_WR:关写端

                                shut_RDWR:关读写

                                

27、select多路IO转接:

 原理:借助内核,select来监听 ,客户端连接、数据通信事件         

         (1) void FD_ZERD (fd_set  *set):--------清空一个文件描述符集合

                fd_set  rset;

                FD_ZERD(&rset);

         (2)void FD_SET(int fd, fd_set  *set);        将待监听的文件描述符,添加到监听集合中

                         FD_SET(3,&rset); FD_SET(5,&rset);

         (3)void FD_CLR(int fd, fd_set  *set);       讲一个文件描述符从监听集合中移除

                        FD_SET(3,&rset);  

         (4)void FD_ISSET(int fd, fd_set  *set);       判断一个文件描述符是否在监听集合中

                        FD_SET(3,&rset);

 int select(int nfds, fd_set  *readfds,  fd_set  *writefds,  fd_set  *, struct  timeval  *timeout)     

              nfds:监听的所有文件描述符中,最大的文件描述符+1

               readfds:读 文件描述符监听集合 ·        传入、传出参数

               writefds:写 文件描述符监听集合          传入、传出参数         NULL

                exceptfds: 异常 文件描述符监听集合 传入、传出参数        NULL

                timeout:        >0:设置监听超时时长

                                        NULL:阻塞监听

                                        0:非阻塞监听,轮询

        返回值:

                >0:        所有监听集合(3个)中,满足对应事件的总数

                0:         没有满足监听条件的文件描述符

                -1:        errno

思路分析:

        lfd=socket();创建套接字

        bind();绑定地址结构

        listen();设置监听上限

        fd_set rset,allset;创建r监听集合,所有集合

        void FD_ZERD (&allset);将集合清空

        FD_SET(lfd,&allset)将lfd添加至读集合中

        while(1){

        rset=allset;                保存监听集合

        set=select(lfd+1,&rset,NULL,NULL,NULL)监听文件描述符集合对应事件  

          if(ret>0){                                        有监听的描述符满足对应事件

                if(FD_ISSET(lfd,&rset))                 //1在,0不在

                   {

                        cfd=accept();            建立连接,返回用于通信的文件描述符

                        FD_SET(cfd,&allset)                添加到监听通信描述符集合中

                        }

                for(i=0;i<=最大文件描述符;i++){

                        FD_ISSET(i,&rset)                         有read、write事件

                        read();

                        小--大

                        write();

                     }

                }

        }     

28、select优缺点

        缺点:监听上限受文件描述符限制。最大1024

                        检测满足条件的id,自己添加业务逻辑提高小。提高了编码难度

        优点:扩平台。win、linux、macOS、Unix、类Unix

29、多路IO转换

select:

30、poll:

        int poll(struct  pollfd  *fds, nfds_t  nfds, int  timeout);

                fds:监听的文件描述符(数组)

                        struct pollfd{

                                int fd;        待监听的文件描述符

                                short  events;        待监听的文件描述符对应的监听事件

                                                                取值:POLLIN、POLLOUT、POLLERR

                                short revents:传入时,给0。如果满足对应事件的话,返回非0--》POLLIN、POLLOUT、POLLERR

                        }

                nfds:监听数组的,实际有效监听个数

                timeout:>0        超时时长,单位ms

                                -1:        阻塞等待

                                0:不阻塞

                返回值:返回满足对应文件监听事件的文件描述符的总个数

优点:

        自带数组结构。可以将监听事件和返回事件集合分离

        拓展监听上限。超出1024限制

缺点:                         

        不能跨平台。只能在linux、类linux下使用

        无法直接定位满足监听事件的文件描述符。监听难度大       

31、read函数返回值:

        >0:实际读到的字节数

        =0:socket中,表示对端关闭。close()

        -1:如果errno=EINIR        被异常终端。需要重启

              如果errno=EAGIN或EWOULDBLOCK 以非阻塞方式读数据,但是没有数据。需要再次读

              如果errno==ECONNRSET说明连接被重置。需要close(),移除监听队列

                错误  

32、突破1024文件描述符限制: 

        cat  /proc/sys/fs/file-max  -->当前计算机所能打开的最大的文件个数。受硬件影响。

        ulimit -a:-->当前用户下的进程。默认打开文件描述符个数。缺省为1024

        修改:

                打开sudo  vi /etc/security/limits.conf.        写入

                *soft  nofile  65535                        -->设置默认值,可以直接借助命令修改【注销用户,使其生效】

                *hard nofile 10000                        -->命令修改上限

33、epoll       

        int epoll_create(int  size)                创建一颗监听红黑树

                size:创建的红黑树的监听节点数量(仅供内核参考)

                返回值:指向新创建的红黑树的根节点的fd

                        失败:-1,errno

        int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);操作监听红黑树

                epfd:epoll_create函数的返回值。epfd

                op:对该监听红黑树所做的操作

                        EPOLL_CTL_ADD:添加fd到监听红黑树

                        EPOLL_CTL_MOD:修改fd在监听红黑树上的监听事件

                        EPOLL_CTL_DEL 将一个fd从监听红黑树上摘下(取消监听)

                fd:        待监听的fd

                event:本质struct epoll_event结构体  地址

                        events:EPOLLIN/EPOLLOUT/EPOLLERR

                        data: 联合体

                                int fd:对应监听事件的fd

                                void *ptr;

                                uint32_t   u32;

                                uint64_t   u64;

                        返回值:成功0;失败:-1,errno

        int epoll_wait(int epfd,struct epoll_event *events,int  maxevents,int  timeout);                阻塞监听

                 epfd:epoll_create函数的返回值。

                events:传出参数。【数组】。满足监听条件的fd的结构体。

                maxevents:数组元素的总个数。1024  struct epoll_event events[1024]

                timeout:>0        超时时长,单位ms

                                -1:        阻塞等待

                                0:不阻塞

                返回值:>0:满足监听的总个数,可以用作循环上限。

                                0:没有fd满足监听事件

                                -1:失败,errno

 34、epoll实现多路IO转接思路:

                lfd=socket();                监听连接事件

                bind();

                listen();

                int  epfd= epoll_create();                        epfd监听红黑树的树根

                struct epoll_event  tep, ep[1024];        tep:用来设置单个fd属性,ep是epoll_wait()传出的满足监听事件的数组

                tep.events=EPOLLIN;

                tep.data.fd=lfd;

                 epoll_ctl( epfd,EPOLL_CTL_ADD,lfd,&tep);      将lfd添加到监听红黑树上

                while(1){

                          ret= epoll_wait(epfd,ep,1024,-1);        实施监听

                        for(i=0;i<ret;i++){

                                if(ep[i].data.fd==lfd)                                //lfd满足读事件,有新的客户端发起连接请求

                                {

                                                cfd=Accept();

                                                tep.events=EPOLLIN;                初始化cfd的监听属性

                                                tep.data.fd=cfd;                        

                                                epoll_ctl(epfd, EPOLL_CTL_ADD,cfd,&tep)

                                }else{              cfd们满足读事件,有客户端写数据来

                                      n=read();

                                        if(n==0){

                                                close();

                                                epoll_ctl(epfd, EPOLL_CTL_ADD,cfd,&tep);将关闭的fd,从监听树上摘下

                                                }          

                                        }else if{

                                                小-->大

                                                }

                        }

                }

 35、epoll事件模型

        ET模式:边缘触发

                缓冲区剩余未读尽的数据不会导致epoll_wait 返回。新的事件满足,才会出发

                struct  epoll_event  event;

                event.events=EPOLLIN | EPOLLET 

        LT模式: 水平触发-->默认采用模式   

                缓冲区剩余未读尽的数据会导致 epoll_wait返回

结论:epoll的ET模式,高效模式,但是只支持非阻塞模式

                struct  epoll_event  event;

                event.events =EPOLLIN | EPOLLET;

                epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &event);

                int  flg=fcntl(cfd, F_GETFL);

                flg |= O_NONBLOCK;

                fcntl(cfd, F_SETFL,flg);

优点:高效。突破1024文件描述符

缺点:不能跨平台。Linux

 36、反应堆模型

        epoll ET模式+非阻塞+void *ptr

        原来:socket、bind、liisten -- epoll_creat 创建监听红黑树 --返回 epfd--epoll_ctl()向树上添加一个监听fd -- while(1) -- epoll_wait监听 -- 对应监听fd有事件产生 -- 返回监听满足数组。 -- 判断返回数组个数 -- lfd满足 -- Accept -- cfd满足 -- read()-- 小-》大 -- write回去

        反应堆:不但要监听cfd的读事件,还要监听cfd的写事件

        socket、bind、liisten -- epoll_creat 创建监听红黑树 --返回 epfd--epoll_ctl()向树上添加一个监听fd -- while(1) -- epoll_wait监听 -- 对应监听fd有事件产生 -- 返回监听满足数组。 -- 判断返回数组个数 -- lfd满足 -- Accept -- cfd满足 -- read()-- 小-》大 --cfd从监听红黑树上摘下-- EPOLLOUT -- 回调函数 -- epoll_ctl()-- EPOLL_CTL_ADD 重新放到红黑树上监听写事件 -- 等待epoll_wait()返回 -- 说明cfd可写 -- write回去 -- cfd从监听红黑树上摘下-- EPOLLIN --  epoll_ctl()-- EPOLL_CTL_ADD 重新放到红黑树上监听读事件  -- epoll_wait监听 

 37、eventset函数:

                设置回调函数。        lfd-->acceptconn()

                                                cfd--》recvdata()

                                                cfd--》senddata()

38、 eventadd函数:

                        讲一个fd,添加到监听红黑树。设置监听read函数,还是监听写事件

 网络编程中:read  --  recv()

                        write -- send()

39、 struct  threadpool_t{

        pthread_mutex_t lock;          用于锁住本结构体    、                、

        pthread_mutex_t   thread_counter        记录忙状态线程个数的锁

        

         pthread_cond_t   queue_not_full;        当任务队列满时,添加任务的线程阻塞,等待此条件变量

        pthread_cond_t   queue_not_empty;        当任务队列不为空时,通知等待任务的线程

        pthread_t  *threads; 存放线程池中每个线程的tid

        pthread_t  adjust_tid;        存管理线程tid;

        threadpool_task_t  *task_queue        任务队列(数组首地址)

        

        int min_thr_num;        线程池最小线程数

       int max_thr_num;        线程池最大线程数       

        int live_thr_num;        当前存活线程个数

        int busy_thr_num;      忙状态线程个数

        int wait_exit_thr_num;      要销毁的线程个数

int queue_front;        task_queue队头下标

int queue_rear;        task_queue队尾下标

int queue_size;        task_queue队中实际任务数

int queue_max_size;        task_queue队列可容纳任务数上限

int shutdown;                标志位,线程池使用状态,true或false

}

typed  struct{

        void *(*function)(void *)        函数指针,回调函数

        void *arg;                                        上面函数的参数        

}threadpool task_t                        各子线程任务结构体

40、线程池模块分析

(1)main()

                创建线程池,向线程池中添加任务。借助回调处理任务。

                销毁线程池

(2)pthreadpool_create()

                创建线程池结构体指针

                初始化线程池结构体(N个成员变量)

                创建N个任务线程

                创建1个管理者线程

                失败时,销毁开辟的所有空间(释放)

(3)threadpool_thread()

                进入子线程回调函数

                接收参数void *arg --》pool结构体 

                 加锁--》lock--》整个结构体锁

                端条件变量--》wait------170

(4)adjust_thread()

                循环10s执行一次

                进入管理者线程回调函数      

                接收参数void *arg --》pool结构体 

                 加锁--》lock--》整个结构体锁

                获取管理线程池要用到的变量。        task_num. live_num, busy_num

                根据既定算法,使用上述3变量,判断是否应该被创建,销毁线程池中指定步长的线程

(5)threadpool_add()       

         总功能:

                模拟产生任务。        num{20]

                设置回调函数,处理任务。sleep(1)代表处理完成

        内部实现:

                加锁;

                初始化任务结构体。          回调函数function,arg

                利用环形队列机制,实现添加任务。借助队尾指针挪移%实现

                唤醒阻塞在条件变量上的进程

                解锁

(6)从(3)中的wait之后继续执行,处理任务。

                加锁;

                获取任务处理回调函数及参数              

                利用环形队列机制,实现添加任务。借助队尾指针挪移%实现

                唤醒阻塞在条件变量上的server

                解锁

                加锁

                改忙线程数++

                解锁

                执行处理任务的线程

                加锁

                改忙线程数--

                解锁

(7)创建销毁进程

               管理者根据task_num. live_num, busy_num

                根据既定算法,使用上述3变量,判断是否应该被创建,销毁线程池中指定步长的线程

                如果满足创建条件

                        pthreadpool_create();回调任务线程函数   live_num++

                 如果满足销毁条件

                        wait_exit_thr_num =10;

                        signal给阻塞在条件变量上的进程发送假条件满足信号

                        跳转至  --170 wait阻塞进程会被假信号唤醒。

                        判断:wait_exit_thr_num >0 pthread_exit()

41、TCP和UDP通信各自的优缺点

        TCP:面向连接的 ,可靠数据包传输。对于不稳定的网络层,采用完全你补的通信方式。丢包重传。

                        优点: 稳定。数据流量稳定、速度稳定、顺序

                        缺点:传输速度慢。相率低,开销大

                        使用场景:数据的完整型要求较高,不追求效率

                                        大数据传输、文件传输

        UDP:无连接的,不可靠的数据报传递。对于不稳定的网络层,采取完全不弥补的通信方式,默认还原网络状况。

                        优点:传输速度快。相率高。开销小。

                        缺点:不稳定。  数据流量、速度、顺序

                        使用场景:对时效性要求高的场合。稳定性其次

                                        游戏、视频会议、视频电话

                                        腾讯、华为、阿里--应用层数据校验协议,弥补udp的不足

42、UDP实现的C/S模型

        recv()/send()只能用于TCP通信。替代 read、write

       accept();-----Connect()---被舍弃

        server:

                lfd=socket(AF_INET,STREAM,0) ;        SOCK_DGRAM---报式协议

                bind();

                listen();---可有可无

                while(1){

                        read(cfd,buf,sizeof)--被替换--recvfrom()--涵盖accept传出地址结构

                        ssize_t recvfrom(int sockfd, void *buf, size_t len, int  flags, struct sockaddr *scr_addr, socklen_t  *addrlen)

                                sockfd:套接字

                                buf:缓冲区地址

                                len:缓冲区大小

                                flags:0

                                scr_addr:( struct sockaddr *)&addr传出。对端地址结构

                                addrlen:传入传出

                        返回值:成功接收数据字节数。失败:-1.errno。  0:对端关闭

                小--》大

                write()---被替换 --sendto()--connect             

ssize_t sendto(int sockfd, const  void *buf, size_t len, int  flags, const  struct sockaddr *scr_addr, socklen_t  *addrlen)

                                sockfd:套接字

                                buf:存储数据的缓冲区

                                len:数据长度

                                flags:0

                                scr_addr:( struct sockaddr *)&addr传入。目标地址结构

                                addrlen:地址结构长度

                        返回值:成功写出数据字节数。失败:-1.errno。  0:对端关闭

                        }

                        close();

        client:

                connfd=socket(AF_INET,SOCK_DGRAM,0);

                sendto(“服务器地址结构”,地址数据大小)

                recvfrom()

                写到屏幕

                close()

43、本地套接字 

        IPC:pipe、fifo、mmap、信号、本地套(domain)-- CS模型

        对比网络编程TCP C/S模型,注意以下几点:

        1、int socket(int domain,int type,int protocol);

                参数domain:AF_INET --》AF_UNIX/AF_LOCAL

                type:SOCK_STREAM/SOCK_DGRAM        都可以

        2、地址结构        sockaddr_in --> sockaddr_un

                struct  sockaddr_in srv_addr;-->struct  sockaddr_un srv_addr;

                srv_addr.sin_family=AT_INET --> srv_addr.sin_family= AF_UNIX;

                srv_addr.sin_port=htons(8888);

                strcpy(srv_addr.sun_path,"srv.socket");

                srv_addr.sin_addr.s_addr=htons(INADDR_ANY);

                len=offsetof(struct  sockaddr_un, sun_path)+ strlen("srv.socket");

                bind(fd,(struct  sockaddr *)&srv_addr, sizeof(srv_addr));

-->         bind(fd,(struct  sockaddr *)&srv_addr, len);

        3、bind()函数调用成功,会创建一个socket,因此为保证bind成功,通常我们在bind之前,可以使用unlink("srv.socket")

        4、客户端不能依赖隐式绑定。并且应该在通信建立过程中,创建且初始化两个地址结构

                (1)client_addr -->bind()

                (2)server_addr -->connect()

44、对比本地套接字和网络套接字

 45、libevent库

                开源、精简、跨平台(所有平台)、专注于网络通信

 46、源码包安装:

参考readme

47、特性:

         基于“事件”异步通信模型---回调

48、libevent框架:

        1、创建event_base        (乐高底座)

                struct  event_base  *event_base_new(void)

              struct  event_base  *base  =event_base_new();

        2、创建事件event

                常规事件event --> event_new();

                bufferevent  -->  bufferevent_socket_new();

        3、将事件添加到base上

                int event_add(struct  event *ev,const struct  timeval  *v)

        4、循环监听事件满足

                int  event_base_dispatch(struct  event_base  *base);

        5、释放event_base

                event_base_free(base)

49、创建事件event

        struct event *ev;

        struct event *event_new(struct  event_base  *base,evutil_socket_t  fd,short what, event_callback_fn  cb,void *arg);

              base:event_base_new()的返回值   

              fd:绑定到event上的文件描述符

                what:对应事件(r、w、e)

                        EV_READ        一次读事件

                        EV_WRITE        一次写事件

                        EV_PERSIST        持续触发。结合event_base_dispath函数使用。生效。

                cb:一旦事件满足监听条件,回调的函数。

                typedef  void(*event_callback_fn )(evutil_socket_t  fd, short, void *)

                arg:回调的函数的参数

                返回值:成功创建的event

50、添加事件到event_base

        int  event_add(struct  event *ev,const struct  timeval  *tv)

                ev:event_new()的返回值 

                tv:NULL

51、从event_base上摘下事件【了解】

        int  event_del(struct  event *ev)

                ev:event_new()的返回值 

52、销毁事件

         int event_base_free(struct  event *ev)

                ev:event_new()的返回值 

53、未决和非未决

         非未决:没有资格被处理

        未决:有资格被处理,但尚未被处理

        event_new---》event ---》非未决 --》event_add --》未决--》dispatch()&&监听事件呗触发--》激活态--》执行回调函数--》处理态 ---》非未决 event_add && EV_PERSIST --》未决--》event_del()--》非未决

54、 带缓冲区的事件bufferevent

        #include<event2/bufferevent.h>

55、创建、销毁bufferevent

        struct bufferevent *ev;

        struct bufferevent *bufferevent_socket_new(struct  event_base  *base,evutil_socket_t  fd, enum bufferevent_options  options);

                base: event_base

                fd:封装到bufferevent内的fd

                options:BEV_OPT_CLOSE_ON_FREE

        返回:成功创建的bufferevent事件对象

        void bufferevent_socket_free(struct bufferevent *ev)

56、给bufferevent设置回调

        对比event:   event_new(fd,callback);

                              event_add()--挂到event_base上

                                bufferevent_socket_new(fd)  bufferevent_setcb(callback)

        void  bufferevent_setcb(struct bufferevent *bufev, bufferevent_data_cb  readcb, bufferevent_data_cb  writecb, void *cbarg)

                bufev:       bufferevent_socket_new()返回值

                readcb:设置bufferevent读缓冲,对应回调readcb(bufferevent_read()读数据)

                writecb:设置bufferevent写缓冲,对应回调writecb--给调用者,发送写成功通知。

                eventcb:设置事件回调。也可以为NULL

                        typed  void(*bufferevent_event_cb)(struct bufferevent *bev,short events, void *ctx)

                        void  event_cb(struct bufferevent *bev,short events, void *ctx)

                        {

                                ...

                        }

                        events:BEV_EVENT_CONNECTED

                cbarg:上述回调函数使用的参数

                read回调参数类型:                                

typed  void(*bufferevent_data_cb)(struct bufferevent *bev, void *ctx)

                        void  event_cb(struct bufferevent *bev,short events, void *ctx)

                        {

                                。。

                                bufferevent_read()---read();

                        }

                bufferevent_read()函数的原型():

                size_t  bufferevent_read(struct bufferevent *bev, void *buf,size_t  bufsize)

                write回调函数类型:

                int bufferevent_writestruct bufferevent *bev,const void *data,size_t  size)

57、启动、关闭bufferevent的缓冲区

        void bufferevent_enable(struct bufferevent *befev,short events)   启动

                 events:EV_READ、EV_WRITE、EV_READ|EV_WRITE

                默认、write缓冲是enable、read缓冲是disable

                        bufferevent_enable(evev,EV_READ);---开启读缓冲

 58、连接客户端

socket();connect();

int bufferevent_socket_connect(struct bufferevent *bev,struct sockaddr *address, int addrlen)

        bev:bufferevent事件对象(封装了fd)

        address、len:等同于connect()

59、创建监听服务器

                ---socket();bind();listen();accept()

        struct  evconnlistener *listener

        struct  evconnlistener *evconnlistener_new_bind(struct event_base  *base, evconnlistener_cb  cb,  void *ptr, unsigned  flags, int backlog,  const struct  sockaddr *sa, int socklen);

                base:event_base

                cb:回调函数。一旦被回调,说明在其内部应该与客户端完成,数据读写操作,进行通信。

                ptr:回调函数的参数

                flags:LEV_OPT_CLOSE_ON_FREE |LEV_OPT_REUSEABLE

                backlog:listen()

                sa:服务器自己的地址结构体

                socklen:服务器自己的地址结构体大小

                返回值:成功创建的监听器

60、释放监听服务器

        void evconnlistener_free(struct  evconnlistener *lev);

61、服务器端libevent创建TCP连接

        1、创建event_base

        2、创建bufferevent事件对象。bufferevent_socket_new()

        3、使用bufferevent_setcb()函数给bufferevent的read、write、event设置回调函数

        4、当监听事件满足时,read_cb会被调用,在其内部bufferevent_read()读

        5、使用evconnlistener_new_bind创建监听服务器。设置其回调函数,当有客户端成功连接时,这个回调函数会被调用。

        6、封装listener_cb()在函数内部。完成与客户端通信

        7、设置读缓冲、写缓冲的使能状态enable、disable

        8、启动循环event_base_dispatch();

        9、释放连接

 62、请求协议        --        浏览器组织,发送

 63、应答协议:

 1、getline()获取http协议的第一行

 2、从首行拆分出GET、文件名、协议版本。获取用户请求的文件名

3、判断文件是否存在。stat()

4、 判断是文件还是目录。

5、是文件 --open -- read --写回给浏览器

6、先写http应答协议头:http/1.1 200 ok

                Content-Type:  text/plain;charset=iso-8859-1

7、写文件数据

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值