(原)Socket API 编程模型
在网络程序里面,一般的来说都是许多客户机对应一个服务器。为了处理客户机的请求,对服务端的程序就提出了特殊的要求。我们学习一下目前最常用的服务器模型:
循环服务器:循环服务器在同一个时刻只可以响应一个客户端的请求 。
并发服务器:并发服务器在同一个时刻可以响应多个客户端的请求 。
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