狐狸梦见乌鸦

当坚持成为一种习惯,目标将不在遥远```
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

网络编程IO模型--学习笔记

Posted on 2012-02-08 15:29  灬啊U  阅读(979)  评论(0编辑  收藏  举报

网络编程IO模型:

1.主要的4种IO模型:

①阻塞IO:最常用,简单,效率最低

②非阻塞IO:可防止进程阻塞在IO操作上,需要轮询。。。

③IO多路复用:允许同时对多个IO进行控制。

④信号驱动IO:

2.阻塞IO:

--read函数:----阻塞,需要内核去唤醒该进程。。。。

--write阻塞:主要发生的情况??

用户缓冲区:???数组。。

UDP无发送缓存区,写操作sendto永远都不会阻塞。()

 

3.非阻塞IO------使用比较少,了解有这种模式

--缓冲区满,不阻塞,它返回一个错误编号

--例如 waitpid(-1,&a, WHOHANG )

--例如 fifo  open()参数中加入非阻塞参数

 

fnctlfd cmd arg)函数:改变一个描述符的属性,设置为O_NONBLOCK来实现非阻塞。

--cmd:命令  F_GETFL获得属性,并存放到flag中,arg忽略,传入0即可;F_SETFL设置属性

--arg:  

4.多路复用IO:(重点)

--一个进程处理多个人进程。。。。

--基本思想:先构造一张有关描述符的表fd_set  rdfs,然后调用select函数。

①fd_set  类似申明了一个一维数组  int  rdfs[256];

②FD_ZERO 作用相当于数组清零-----00000000000000

③FD_SET(0, &rdfs) 将文件描述符为0的地方(套接字缓存区),置一

  FD_SET (LIStenfd,&rdfs)

③如果select 返回值大于0;则遍历数组

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

{

         if (FD_ISSET(i, &rdfs)>0)

{

         if (i==0) fgets。。。。

         if (i== listenfd)

accept。。。。

}

}

 

select函数特点:

①     返回 n = select函数(,,,,, 0)就绪的描述符的个数。。

②它把没就绪的位清0.   使用FD_ISSET函数遍历数组,为0的位置调用accept函数

③作用查看缓冲区状态:

 

服务器模型:

1循环服务器

―――TCP服务器

―――UDP服务器

2.并发服务器

 

*********IO多路复用并发服务器*********

初始化(socket---bind---listen)

①fd_set  类似申明了一个一维数组  int  rdfs[256];

②FD_ZERO 作用相当于数组清零-----00000000000000

②     FD_SET(0, &rdfs) 将文件描述符为0的地方(套接字缓存区),置一

  FD_SET (LIStenfd,&rdfs)

fd_set  f1,f2;

FD_ZERO(&f1)

FD_SET( ,&f1)

while(1)

{

         f2 = f1;

         if (select(。。。f2.。。。。)>0)

         {       

                   for (i= 0; i <= maxfd; i++)

{

                            if (FD_ISSET(i, &f2)>0)

{

                            if (i== listenfd)

connfd = accept();

                                     FD_SET(f1);

                            }

                            else

                            {

                                     recv(…….);

}                          

}

}

}

 

TCP并发服务器:

while(1){

                   connfd = accept(。。。);

                   fork()

                   if (pid == 0) -------

close(listenfd);

while (1)

{

         。。。。。。

}

exit(0);//发送结束信号,异步方式解决僵尸问题

         else------父进程

            close(connfd);

}

void  SignalHander(int  signo)异步方式解决僵尸问题

{

         while(waitpid(-1, NULL, WHOHANG)>0);

}

网络属性设置:

getsockopt()

setsockopt(sockfd, level, optname, *optval, socklen_t)

level: SOL_SOCKET

optname:  SO_BROADCAST

optval: 存放选项值的缓冲区长度地址

socklen_t: 缓冲长度

 

网络超时检测:

i.在网络通信中,很多操作都会使进程阻塞;TCP套接字中的recv/accept/connet和UDP套接字中的recvform;

超时检测的必要性:

i.避免进程在没有数据时无限的阻塞;ii.当设定时间到时,进程从原来的操作返回继续进行。

 

三种方法检测网络超时:

方法一:

设置套接字属性:SO_RCVTIMEO

struct  timeval  timev;

timev.tv_sec = 5;

timev.tv_usec = 0;

…….

setsocketopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &TIMEV, sizeof(timev)); //设置接收超时

………

recv()/recvfrom…..//从套接字读取数据。。。

 

方法二:

用select检测socket是否准备就绪;

struct  fd_set  rdfs;

struct  timeval  tv = {5, 0}; // 设置5秒时间

FD_ZERO(&rdfs);

FD_SET(sockfd, &rdfs);

if (select (sockfd, &rdfs,NULL,NULL,NULL,&tv) > 0) // sockfd就绪

{

         recv()/recvfrom()// 从socket中读取数据

}

 

方法三:

设置定时器timer,捕捉SIGALRM信号

void  hander(int  signo){ return ;}

struct  sigaction  act;

sigaction(SIGALRM, NULL,&act);

act.sa_hander  =  hander();

act.sa_flags  &=  ~SA_RESTART; //将某位清0

sigaction(SIGALRM, &act, NULL);

alarm(5);

if (recv(,,,) <0) ………

 

记住一两个错误编号和宏:

errno = 11 是连接超时

errno = 4  是中断system call  宏:EINTR

 

广播:

1.数据包发送方式只有一个接收方,叫单播;

2.同时发给局域网中的所有主机,称为广播;广播只能用户数据报,即UDP协议,套接字才能广播;

3.广播地址:

广播发送流程: 创建数据报套接字---à设置setsockopt套接字属性---à指定接收方为广播地址--à指定端口信息---à发送数据包

sockfd = socket(; ; );

………..

int  on = 1;

setsockopt(socket,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));

……..

sendto(………);

广播的接收流程:创建数据报套接字---à绑定本机IP地址和端口---à绑定的端口必须和发送方指定的端口相同----à等待接收数据;

注意绑定本机IP地址一般为(0.0.0.0)  端口 htonl(INADDR_ANY)

 

 

组播:

只用加入某个多播组的主机才能收到数据;

网络地址:ABCD类地址

 

下面是广播和组播图:

 

 

 

 

组播的发送方流程:

socket--àsendto---à选择D类地址---àsleep(1)

 

组播的接收方流程:创建用户数据报套接字--à加入多播组---à绑定本机的IP地址和端口--à等待接收数据

socket--àsetsockopt---àbind---àrecvfrom

 

libcap编程: