(原)Socket API 编程模型

(原)Socket API 编程模型

孔令春 posted @ 2009年10月15日 00:14 in  网络安全 with tags  Socket API 编程模型 , 1946 阅读

      在网络程序里面,一般的来说都是许多客户机对应一个服务器。为了处理客户机的请求,对服务端的程序就提出了特殊的要求。我们学习一下目前最常用的服务器模型:

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

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


1、 循环服务器:UDP服务器  
     UDP循环服务器的实现非常简单:UDP服务器每次从套接字上读取一个客户端的请求,处理, 然后将结果返回给客户机。

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

  

?
1
2
3
4
5
6
7
8
socket(...);
bind(...);
while (1)
  {
       recvfrom(...);
       process(...);
       sendto(...);
}



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

 
2、 循环服务器:TCP服务器  
     TCP循环服务器的实现也不难:TCP服务器接受一个客户端的连接,然后处理,完成了这个客户的所有请求后,断开连接。  

算法如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
socket(...);
bind(...);
listen(...);
while (1)
{
         accept(...);
         while (1)
         {
                 read(...);
                 process(...);
                 write(...);
         }
         close(...);
}



      TCP循环服务器一次只能处理一个客户端的请求。只有在这个客户的所有请求都满足后, 服务器才可以继续后面的请求。这样如果有一个客户端占住服务器不放时,其它的客户机都不能工作了。因此,TCP服务器一般很少用循环服务器模型的。 

3、 并发服务器:TCP服务器  
      为了弥补循环TCP服务器的缺陷,人们又想出了并发服务器的模型。 并发服务器的思想是每一个客户机的请求并不由服务器直接处理,而是服务器创建一个 子进程来处理。 

算法如下: 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
socket(...);
   bind(...);
   listen(...);
   while (1)
   {
         accept(...);
         if (fork(..)==0)
           {
               while (1)
                {       
                 read(...);
                 process(...);
                 write(...);
                }
            close(...);
            exit (...);
           }
         close(...);
   }



      TCP并发服务器可以解决TCP循环服务器客户机独占服务器的情况, 不过也同时带来了一个不小的问题。为了响应客户机的请求,服务器要创建子进程来处理,而创建子进程是一种非常消耗资源的操作。  

4、 并发服务器:多路复用I/O  
      为了解决创建子进程带来的系统资源消耗,人们又想出了多路复用I/O模型。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
初始话(socket,bind,listen);
         
     while (1)
         {
         设置监听读写文件描述符(FD_*);  
         
         调用select;
         
         如果是倾听套接字就绪,说明一个新的连接请求建立
              {
                 建立连接(accept);
                 加入到监听文件描述符中去;
              }
        否则说明是一个已经连接过的描述符
                 {
                     进行操作(read或者write);
                  }
                         
         }



      多路复用I/O可以解决资源限制的问题.着模型实际上是将UDP循环模型用在了TCP上面. 这也就带来了一些问题.如由于服务器依次处理客户的请求,所以可能会导致有的客户 会等待很久.  

5 并发服务器:UDP服务器  
      人们把并发的概念用于UDP就得到了并发UDP服务器模型. 并发UDP服务器模型其实是简单的.和并发的TCP服务器模型一样是创建一个子进程来处理的 算法和并发的TCP模型一样.  

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

 

6、一个简单的TCP循环服务器实例:时间获取程序

服务器端接收客户端的连接,并将欢迎信息和当时时间发送至客户端。代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
   * timeTcpServ.c
   * 简单的时间获取服务器
   */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <time.h>
 
#define SERVER_PORT 3333   //服务器端口号
#define LEN_OF_LISTEN_QUEUE 10 //最大监听队列数
#define WELCOME_MESSAGE "welcome to commect the server.\n" //欢迎信息
 
int main( int argc, char *argv[])
{
     int servfd, clifd;
     struct sockaddr_in servaddr, cliaddr;
 
     //创建socket
     if ( (servfd = socket(AF_INET,SOCK_STREAM,0)) < 0 )
     {
         printf ( "Create socket error!\n" );
         exit (1);
     }
 
     //设置服务器地址结构
     bzero(&servaddr, sizeof (servaddr)); 
     servaddr.sin_family = AF_INET;
     servaddr.sin_port = htons(SERVER_PORT);
     servaddr.sin_addr.s_addr = htons(INADDR_ANY);
 
     //绑定服务器地址
     if ( bind(servfd, ( struct sockaddr*)&servaddr, sizeof (servaddr)) < 0 )
     {
         printf ( "Bind to port %d failure!\n" ,
                         SERVER_PORT);
         exit (1);
     }
 
     //开始监听
     if ( listen(servfd, LEN_OF_LISTEN_QUEUE) < 0 )
     {
         printf ( "Call listen failure!\n" );
         exit (1);
     }
 
     printf ( "Wait for connect...\n" );
 
     while (1)
     {
         char buf[BUFSIZ];
         socklen_t clilen = sizeof (cliaddr);
 
         //接受连接
         if ( (clifd = accept(servfd, ( struct sockaddr*)&cliaddr, &clilen)) < 0 )
         {
             printf ( "Call accept failure!\n" );
             exit (1);
         }
 
         //打印客户端ip及端口号
         printf ( "connect form %s:%d\n" , inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
 
         //整合要发送的信息
         strcpy (buf,WELCOME_MESSAGE);
         strcat (buf, "\nCurrent time is:" );
         long currtime = time (NULL);
         strcat (buf, ctime (&currtime));
 
         //发送信息
         send(clifd, buf, BUFSIZ,0);
         close(clifd);
     }
 
     close(servfd);
     return 0;
}

 

客户端程序连接上服务器端之后,接收服务器端信息并将之打印到屏幕上,代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
   *时间获取客户端程序
   */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
 
#define SERVER_PORT 3333  //服务器端口号
 
int main( int argc, char *argv[])
{
     int servfd;
     struct sockaddr_in servaddr;
     struct hostent  *hp;
     char buf[BUFSIZ];
 
     if ( argc != 2 )
     {
         printf ( "Please input %s <hostname>\n" , argv[0]);
         exit (1);
     }
 
     //创建socket
     if ( (servfd = socket(AF_INET, SOCK_STREAM,0)) < 0 )
     {
         printf ( "Create socket error!\n" );
         exit (1);
     }
 
     //设置服务器地址结构
     bzero(&servaddr, sizeof (servaddr));
     servaddr.sin_family = AF_INET;
     if ( (hp = gethostbyname(argv[1])) != NULL )
     {
         bcopy(hp->h_addr, ( struct sockaddr*)&servaddr.sin_addr, hp->h_length);
     }
     else if (inet_aton(argv[1], &servaddr.sin_addr) < 0 )
     {
         printf ( "Input Server IP error!\n" );
         exit (1);
     }
     servaddr.sin_port = htons(SERVER_PORT);
 
     //连接服务器
     if ( connect(servfd,( struct sockaddr*)&servaddr, sizeof (servaddr)) < 0 )
     {
         printf ( "Connect server failure!\n" );
         exit (1);
     }
 
     //接收并打印信息
     recv(servfd,buf,BUFSIZ,0);
     printf ( "\n%s\n" ,buf);
 
     close(servfd);
     return 0;
}

 

参考网址:http://fanqiang.chinaunix.net/program/netpro/2001-05-08/1968.shtml

你可能感兴趣的:(编程,C++,socket)