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()模型即支持水平触发,也支持边缘触发,默认是水平触发。

      

  • 相关阅读:
    linux shell if 参数
    SHELL输出颜色和闪烁控制
    http层负载均衡之 haproxy实践篇
    linux系统查找大文件脚本
    Nginx的try_files指令和命名location使用实例
    Java SpringMVC实现PC端网页微信扫码支付完整版
    IPTABLES基本例子
    MAC上反编译android apk---apktool, dex2jar, jd-jui安装使用(含手动签名)
    spring mvc 集成freemarker模板
    Python—正则表达式
  • 原文地址:https://www.cnblogs.com/xuelei/p/6681534.html
Copyright © 2011-2022 走看看