网络编程基础

(1)bind 函数如何选择绑定地址:bind 函数的基本用法如下:

                 struct sockaddr_in  bindaddr;

                 bindaddr.sin_family = AF_INET;

                 bindaddr.sin_addr.s_addr = htonl(INADDR_ANY); //

                 bindaddr.sin_port = htons(3000);

                if (bind(listenfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -1)

                {

                        std::cout << "bind listen socket error." << std::endl;

                        return -1;

                }

    如上所示,INADDR_ANY是一个宏,相当于地址0.0.0.0,如果应用程序不关心bind绑定的ip地址,可以使用INADDR_ANY(如果是IPv6,则对应in6addr_any),这样底层的(协议栈)服务会自动选择

一个合适的ip地址,这样使在一个有多个网卡机器上选择ip地址问题变得简单。bind函数可以绑定多个可选的IP,如果指定本机访问,可设置127.0.0.1,如果是局域网内部访问,设置内网IP,如果是外

网访问,设置0.0.0.0或 INADDR_ANY

(2)bind函数绑定端口号:如果将 bind 函数中的端口号设置成0,那么操作系统会随机给程序分配一个可用的侦听端口,windows的端口号是0~65535,实际可用的更少,此外,除了服务器可以调

用bind绑定指定端口外,客户端也可以调用以指定的端口号去连接服务器。另外,Linux 的 nc 命令有个 -p 选项(字母 p 是小写),这个选项的作用就是 nc 在模拟客户端程序时,可以使用指定端口

号连接到服务器程序上去,比如 命令nc -v -p 9999 127.0.0.1 3000 则指定9999区连接127.0.0.1 的3000端口。

(3)socket的阻塞和非阻塞模式:所谓阻塞模式,就当某个函数“执行成功的条件”当前不能满足时,该函数会阻塞当前执行线程,程序执行流在超时时间到达或“执行成功的条件”满足后恢复继续执

行。而非阻塞模式恰恰相反,即使某个函数的“执行成功的条件”不当前不能满足,该函数也不会阻塞当前执行线程,而是立即返回,继续运行执行程序流。无论是 Windows 还是 Linux 平台,默认创建

的 socket 都是阻塞模式的。

            windows下使用ioctlsocket() 函数 将 socket 设置成非阻塞模式,ioctlsocket() 签名如下:

                      int ioctlsocket(SOCKET s,long cmd, u_long *argp);   //cmd 参数设置为 FIONBIO,argp设置为0即可将 socket 设置成阻塞模式,而将argp 设置成非 0 即可设置成非阻塞模式

           linux下有三种方式可将socket设置成 非阻塞模式:

           1.使用 fcntl() 函数或 ioctl() 函数给创建的 socket 增加 O_NONBLOCK 标志来将 socket 设置成非阻塞模式:

                     int oldSocketFlag = fcntl(sockfd, F_GETFL, 0);

                    int newSocketFlag = oldSocketFlag | O_NONBLOCK;

                    fcntl(sockfd, F_SETFL,  newSocketFlag);

          2.socket() 创建函数时直接设置非阻塞,函数签名如:int socket(int domain,int type,int protocol);给参数type 参数增加一个 SOCK_NONBLOCK 标志即可,如下:

                     int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);

         3.使用扩展函数 accept4(),直接将 accept 函数返回的 socket 设置成非阻塞的,函数签名:int accept4(int sockfd, struct sockaddr *addr,socklen_t* addrlen,int flags); 例如:

                    int clientfd = accept4(listenfd, &clientaddr, &addrlen, SOCK_NONBLOCK);

         send 函数本质上并不是往网络上发送数据,而是将应用层发送缓冲区的数据拷贝到内核缓冲区中去,至于什么时候数据会从网卡缓冲区中真正地发到网络中去要根据 TCP/IP 协议栈的行为来确

定,这种行为涉及到一个叫 nagel 算法和 TCP_NODELAY 的 socket 选项。recv 函数本质上也并不是从网络上收取数据,而只是将内核缓冲区中的数据拷贝到应用程序的缓冲区中,当然拷贝完成以

后会将内核缓冲区中该部分数据移除。

        当 socket 是阻塞模式的,继续调用 send/recv 函数会导致程序阻塞在 send/recv 调用处;当 socket 是非阻塞模式,继续调用 send/recv 函数,send/recv 函数不会阻塞程序执行流,而是会立即

出错返回-1,我们会得到一个相关的错误码,Linux 平台上该错误码为 EWOULDBLOCK 或 EAGAIN(这两个错误码值相同),Windows 平台上错误码为 WSAEWOULDBLOCK。

(4)nagle算法:nagle算法的是操作系统网络通信层的一种发送数据包机制,如果开启,则一次放入网卡缓冲区中的数据(利用send或write等)较小时,可能不会立即发出去,只要当多次send或者

write之后,网卡缓冲区中的数据足够多时,才会一次性被协议栈发送出去,操作系统利用这个算法减少网络通信次数,提高网络利用率。对于实时性要求比较高的应用来说,可以禁用nagle算法。这

样send或write的小数据包会立刻发出去。系统默认是开启的,禁用方法如下:

        long noDelay = 1;  

       setsockopt(m_hSocket, IPPROTO_TCP, TCP_NODELAY,(LPSTR)&noDelay, sizeof(long));       //noDelay为1-禁用,0-启用

(5)linux epoll模型:Linux 从内核 2.6引入epoll模型,首先创建fd函数:

       #include<sys/epoll.h>

       int epoll_create(int size);  //size 从 Linux 2.6.8 以后就不再使用,但是必须设置一个大于 0 的值 ,返回-1表示创建失败

       int epoll_ctl(int epfd,int op,int fd,struct epoll_event* event); // 绑定事件,返回-1表示失败,参数 epfd 即上文提到的 epollfd,参数 op表示操作类型,EPOLL_CTL_ADD(添加)、

EPOLL_CTL_MOD(修改)、EPOLL_CTL_DEL(删除),当操作类型为NULL时,第四个参数 可以设置NULL,参数 fd,即需要被操作的 fd,参数 event,一个 epoll_event 结构体的地址,

epoll_event 结构体定义如下:

         struct epoll_event

        {

               uint32_t     events;      /* 需要检测的 fd 事件,取值与 poll 函数一样 */

               epoll_data_t data;        /* 用户自定义数据, 本质上是一个 Union 对象,8个字节*/

         }; 

     int epoll_wait(int epfd,struct epoll_event* events,int maxevents,int timeout); //检测事件 ,参数 events 是一个 epoll_event 结构数组的首地址,这是一个输出参数,函数调用成功后,events 中存放 

的是与就绪事件相关 epoll_event 结构体数组;参数 maxevents 是数组元素的个数;timeout 是超时时间,单位是毫秒,如果设置为 0,epoll_wait 会立即返回。当 epoll_wait 调用成功会返回有事件

的 fd 数目;如果返回 0 表示超时;调用失败返回 -1。如下例子所示:

while (true)

{

    epoll_event epoll_events[1024];

    int n = epoll_wait(epollfd, epoll_events, 1024, 1000);

    if (n < 0)

    {

        //被信号中断

        if (errno == EINTR)

            continue;

        //出错,退出

        break;

    }

    else if (n == 0)

    {

        //超时,继续

        continue;

    }

    for (size_t i = 0; i < n; ++i)

    {

        // 处理可读事件

        if (epoll_events[i].events & POLLIN)

        {

        }

        // 处理可写事件

        else if (epoll_events[i].events & POLLOUT)

        {

        }

        //处理出错事件

        else if (epoll_events[i].events & POLLERR)

        {

        }

    }

}

          epoll_wait 与 poll 的区别:epoll_wait 函数调用完之后,我们可以直接在 event 参数中拿到所有有事件就绪的 fd,直接处理即可(event 参数仅仅是个出参);而 poll 函数的事件集合调用前后

数量都未改变,只不过调用前我们通过 pollfd 结构体的 events 字段设置待检测事件,调用后我们需要通过 pollfd 结构体的 revents 字段去检测就绪的事件( 参数 fds 既是入参也是出参)。

           与 poll 的事件宏相比,epoll 新增了一个事件宏EPOLLET,这就是所谓的边缘触发模式(EdgeTrigger,ET),而默认的模式我们称为水平触发模式(LevelTrigger,LT)。这两种模式的区别在于:

          对于水平触发模式,一个事件只要有,就会一直触发;(读取数据时可选择读取字节数目)

          对于边缘触发模式,只有一个事件从无到有才会触发。(因此读取数据时要一次性全部读完,循环调用 recv 函数直到 recv 出错,错误码是EWOULDBLOCK(EAGAIN 一样))

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容