1.概念
与前面介绍的循环服务器不同,并发服务器对服务请求并发处理。而循环服务器只能够一个一个的处理客户端的请求,显然效率很低. 并发服务器通过建立多个子进程来实现对请求的并发处理,但是由于不清楚请求客户端的数目,因此很难确定子进程的数目。因此可以动态增加子进程与事先分配的子进程相结合的方法来实现并发服务器。
2. 算法流程
(1)TCP简单并发服务器:
服务器子进程1:
socket(...);
bind(...);
listen(...);
while(1)
{
accept(...);
recv(....);
process(...);
send(.....);
}
close(....);//关闭客户端
服务器子进程2:
socket(...);
bind(...);
listen(...);
while(1)
{
accept(...);
recv(....);
process(...);
send(.....);
}
close(....);//关闭客户端
...............
...............
...............
(2)UDP简单并发服务器
服务器子进程1:
socket(...);
bind(...);
while(1)
{
recvfrom(....);
process(...);
sendto(.....);
}
服务器子进程2,3....与1相同.
这样,同时到达多个请求,就分别由多个进程并发的处理,此算法的的性能取决于请求的数目,如果请求数目越多,这样的子进程就越多,那么进程的切换开销就会很大.而且还不容易确定子进程的数目,所以这是一种简单的并发服务器模型.
3.相关实例
(1)简单并发服务器TCP
服务器:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
/**
TCP并发服务器,预先建立进程,同时到来的客户端分别由不同的进程并发处理
**/
#define PORT 8888
#define BUFFERSIZE 1024
#define PIDNUM 2
static void handle(int s){
int sc;
struct sockaddr_in client_addr;//客户端地址
char buffer[BUFFERSIZE];
int len;
int ret;
int size;
len=sizeof(client_addr);
time_t now;
//接收客户端的连接
while(1){
memset(buffer,0,BUFFERSIZE);
sc=accept(s,(struct sockaddr*)&client_addr,&len);
size=recv(sc,buffer,BUFFERSIZE,0);
if(size>0&&!strncmp(buffer,"TIME",4)){
memset(buffer,0,BUFFERSIZE);
now=time(NULL);
sprintf(buffer,"%24s\r\n",ctime(&now));
send(sc,buffer,strlen(buffer),0);//发送到客户端
}
close(sc);
}
}
int main(int argc,char*argv[]){
int s;
int ret;
struct sockaddr_in server_addr;
pid_t pid[PIDNUM];
int i;
s=socket(AF_INET,SOCK_STREAM,0);
if(s<0){
perror("socket error");
return -1;
}
//绑定地址到套接字
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
ret=bind(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(ret<0){
perror("bind error");
return -1;
}
ret=listen(s,10);//监听
//建立子进程处理同时到来的客户端请求
for(i=0;i<PIDNUM;i++){
pid[i]=fork();
if(pid[i]==0){
handle(s);
}
}
while(1);
close(s);
}
客户端:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
#define PORT 8888
#define BUFFERSIZE 1024
int main(int argc,char*argv[]){
int s;
int ret;
int size;
struct sockaddr_in server_addr;
char buffer[BUFFERSIZE];
s=socket(AF_INET,SOCK_STREAM,0);
if(s<0){
perror("socket error");
return -1;
}
bzero(&server_addr,sizeof(server_addr));
//将地址结构绑定到套接字
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(PORT);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
//连接服务器
ret=connect(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(ret==-1){
perror("connect error");
return -1;
}
memset(buffer,0,BUFFERSIZE);
strcpy(buffer,"TIME");
size=send(s,buffer,strlen(buffer),0);
if(size<0){
perror("send error");
return -1;
}
memset(buffer,0,BUFFERSIZE);
size=recv(s,buffer,BUFFERSIZE,0);
if(size<0){
perror("recv error");
return;
}
printf("%s",buffer);
close(s);
return 0;
}
运行结果:
[root@localhost 14章服务器模式]# ./circle-tcpc14
Sat Feb 18 10:28:59 2012
(2)UDP简单并发服务器
服务器:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
/**
UDP循环服务器处理完一个请求之后才能处理下一个请求,而不能同时处理请求
UDP并发服务器通过事先建立进程,并发的处理来自客户端的请求
缺点:事先建立好子进程,但不知道客户端的数目,难以适应动态变化
**/
//建立2个进程,并发的处理客户端的请求
#define PORT 8888
#define PIDNUM 2
#define BUFFERSIZE 1024
//两个子进程,分别并发的处理客户端的请求
static void handle(int s){
char buffer[BUFFERSIZE];
struct sockaddr_in client_addr;
int len;
int size;
len=sizeof(client_addr);
time_t now;
while(1){
memset(buffer,0,BUFFERSIZE);
size=recvfrom(s,buffer,BUFFERSIZE,0,(struct sockaddr*)&client_addr,&len);
if(size>0&&!strncmp(buffer,"TIME",4)){
memset(buffer,0,BUFFERSIZE);
now=time(NULL);
sprintf(buffer,"%24s\r\n",ctime(&now));
size=sendto(s,buffer,strlen(buffer),0,(struct sockaddr*)&client_addr,sizeof(client_addr));
if(size<=0){
perror("sendto error");
}
}
}
}
int main(int argc,char*argv[]){
int s;//套接字描述符
int ret;//返回值
int size;
int i;
pid_t pid[PIDNUM];
struct sockaddr_in server_addr;
//建立套接字
s=socket(AF_INET,SOCK_DGRAM,0);
if(s<0){
perror("socket error");
return -1;
}
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(PORT);
//将地址结构绑定到套接字描述符
ret=bind(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(ret==-1){
perror("bind error");
return -1;
}
//接收数据并处理,主函数建立两个进程,让进程并发的处理请求
for(i=0;i<PIDNUM;i++){
pid[i]=fork();
if(pid[i]==0){
handle(s);//让子进程去处理
}
}
while(1);//父进程等待
return 0;
}
客户端:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
/**
UDP端,客户端向服务器发送时间请求,服务器返回相应的时间
**/
#define PORT 8888
#define BUFFERSIZE 1024
int main(int argc,char*argv[]){
int s;//套接字描述符
int ret;//建立套接字的返回值
int size;
struct sockaddr_in server_addr;//地址结构
int len;
char buffer[BUFFERSIZE];
s=socket(AF_INET,SOCK_DGRAM,0);//建立流式套接字
if(s<0){
perror("socket error");
return -1;
}
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(PORT);
memset(buffer,0,BUFFERSIZE);
strcpy(buffer,"TIME");
//向服务器发送数据
size=sendto(s,buffer,strlen(buffer),0,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(size<0){
perror("sendto error");
return -1;
}
//从服务器接收数据
len=sizeof(server_addr);
size=recvfrom(s,buffer,BUFFERSIZE,0,(struct sockaddr*)&server_addr,&len);
if(size<0){
perror("recvfrom error");
return -1;
}
//write(1,buffer,size);
printf("%s\n",buffer);
close(s);
return 0;
}
总结:本文主要介绍了简单的TCP与UDP并发服务器,并给出了实例.这种简单的并发服务器很难适应动态变化的客户端请求,所以,接下来将介绍高级并发服务器.