zoukankan      html  css  js  c++  java
  • TCP网络编程模型

      TCP网络编程有三个例子最值得学习研究,分别是echo、chat、proxy。

      echo的作用:熟悉服务端被动接受新连接、收发数据、被动处理连接断开。每个连接是独立服务的,连接之间没有关联。在消息内容方面echo有一些变种:比如做成一问一答得方式,收到的请求和发送响应的内容不一样,这时候要考虑打包与拆包格式的设计,进一步还可以写成简单的HTTP服务。

      chat的作用:连接之间的数据有交流,从a收到的数据要发给b。这样对连接管理提出了更高的要求:如何用一个程序同时处理多个连接?fork()-per-connection似乎是不行的。如何防止串话?b有可能随时断开连接,而新建立的连接c可能恰好复用了b的文件描述符,那么a会不会错误地把消息发给c?

      proxy的作用:连接的管理更加复杂:既要被动接受连接,也要主动发起连接;既要主动关闭连接,也要被动关闭连接。还要考虑两边速度不匹配。

      这三个例子功能简单,突出了TCP网络编程中的重点问题,挨着做一遍基本就能到基础层次。chat例子如腾讯的qq,今天发现一个很好的范强软件lantern,就是proxy的应用。另外推荐两个学习socketAPI的神器:IPython、netcat。IDE用CLion,不要太好用。

      依个人水平,目前把Linux服务端编程模型分为三个层次:

      第一层次——简单的多进程、多线程、线程池。缺点:一个线程阻塞一个IO操作,耗资源

      第二层次——多路IO复用。特点:阻塞IO,把accept()、read()等的阻塞放在select()/poll()/epoll_wait()上

      第三层次——reactor proactor模型。优势:高性能、高并发

    注:服务器程序一般不会频繁地启动和终止线程。甚至create_thread只在程序启动时调用,在服务运行期间是不调用的。

    参考:https://www.zhihu.com/question/21516827

    http://blog.csdn.net/caiwenfeng_for_23/article/details/8458299

     一、TCP服务端多线程模型:

    void *worker(void *arg)
    {
        int client = *((int *)arg);
        while (read(client) > 0)
        {
            write(client);
        }
        close(client);
    }
    
    int main()
    {
        socket();
        bind();
        listen();
        while (1)
        {
            int client = accept();
            if (client > 0)
            {
                pthread_t pt;
                pthread_create(&pt, NULL, worker, &client);  //one thread per connect. bad model!
            }
        }
    }

    二、TCP服务端epoll模型:

    int main()
    {
        int listener = socket();
        bind();
        listen();
        
        int epfd = epoll_create();
        epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev);
        while (1)
        {
            int n = epoll_wait(epfd, events, MAX_EVENT, -1);
            for (int i=0; i<n; ++i)
            {
                if (events[i].data.fd == listener)
                {
                    int client = accept();
                    setnonblocking(client);
                    epoll_ctl(epfd, EPOLL_CTL_ADD, client, &ev);
                }
                else
                {
                    handle();
                    if (done())
                    {
                        epoll_ctl(epfd, EPOLL_CTL_DEL, client, &ev);
                        close(client);
                    }
                }
            }
        }
    }

    epoll的两种触发方式:水平触发LT、边缘触发ET。

    Level_triggered(水平触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你!!!如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率!!!

    Edge_triggered(边缘触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!!

    阻塞IO:当你去读一个阻塞的文件描述符时,如果在该文件描述符上没有数据可读,那么它会一直阻塞(通俗一点就是一直卡在调用函数那里),直到有数据可读。当你去写一个阻塞的文件描述符时,如果在该文件描述符上没有空间(通常是缓冲区)可写,那么它会一直阻塞,直到有空间可写。以上的读和写我们统一指在某个文件描述符进行的操作,不单单指真正的读数据,写数据,还包括接收连接accept(),发起连接connect()等操作...

    非阻塞IO:当你去读写一个非阻塞的文件描述符时,不管可不可以读写,它都会立即返回,返回成功说明读写操作完成了,返回失败会设置相应errno状态码,根据这个errno可以进一步执行其他处理。它不会像阻塞IO那样,卡在那里不动!!!                    

     select(),poll()模型都是水平触发模式,信号驱动IO是边缘触发模式,epoll()模型即支持水平触发,也支持边缘触发,默认是水平触发。

      

  • 相关阅读:
    消息中间件(一)MQ详解及四大MQ比较
    SIP协议
    PAT (Basic Level) Practice 1008 数组元素循环右移问题
    LeetCode-Algorithms 1. 两数之和
    PAT (Basic Level) Practice 1040 有几个PAT
    PAT (Basic Level) Practice 1023 组个最小数
    PAT (Basic Level) Practice 1021 个位数统计
    PAT (Basic Level) Practice 1007 素数对猜想
    PAT (Basic Level) Practice 1006 换个格式输出整数
    PAT (Basic Level) Practice 1004 成绩排名
  • 原文地址:https://www.cnblogs.com/xuelei/p/6681534.html
Copyright © 2011-2022 走看看