与循环服务器的串行处理不同,并发服务器对服务请求并发处理。循环服务器只能够一个一个的处理客户端的请求,显然效率很低。并发服务器通过建立多个子进程来实现对请求的并发处理。并发服务器的一个难点是如何确定子进程的数据,因为不清楚请求客户端的数目,因此实现中通常采用事先分配一定数目的子进程与动态增加子进程相结合的方法来实现并发服务器。
简单并发服务器模型
/* UDP简单并发服务器模型 */
/* 服务器主进程 */
socket(...);
bind(...);
fork(); //创建多个子进程进行请求处理
while();
/* 服务器子进程1 */
while()
{
recvfrom(....);
process(...);
sendto(.....);
}
close(...);
/* 服务器子进程2(同上) */
..................
..................
..................
/* 服务器子进程3(同上) */
..................
..................
..................
/* TCP简单并发服务器模型 */
/* 服务器主进程 */
socket(...);
bind(...);
fork();//创建多个子进程进行请求处理
while();
/* 服务器子进程1 */
socket(...);
bind(...);
listen(...);
while()
{
accept(...);
recv(....)
process(...);
send(.....);
}
close(....);
/* 服务器子进程2(同上) */
..................
..................
..................
/* 服务器子进程3(同上) */
..................
..................
..................
一个并发服务器的例子
下面给出一个简单的并发服务器样子,仍然模拟服务器对外提供时间服务器,等待客户端到来,并返回给客户端服务器的当前时间。
UDP循环服务器
/** UDP并发服务器--server端程序**/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#define BUFFLEN 1024
#define SERVER_PORT 12347
#define BACKLOG 5 static void handle_request(int s, struct sockaddr_in* from, char *buff)
{
time_t now;
int len = sizeof(*from);
memset(buff, , BUFFLEN);
now = time(NULL);
sprintf(buff, "%24s\r\n",ctime(&now));
sendto(s, buff, strlen(buff),, (struct sockaddr*)from, len);/*发送数据*/
} static void handle_connect(int s_s)
{
struct sockaddr_in from; /*客户端地址*/
int len = sizeof(from);
int n = ;
char buff[BUFFLEN]; /*主处理过程*/
while()
{
memset(buff, , BUFFLEN);
/*接收客户端连接*/
n = recvfrom(s_s, buff, BUFFLEN,,(struct sockaddr*)&from, &len);/*接收发送方数据*/
if(n > && !strncmp(buff, "TIME", ))/*判断是否合法接收数据*/
{
/*创建进程进行数据处理*/
if(!fork())
{
handle_request(s_s, &from, buff);/*处理连接请求*/
return ;
}
}
}
} int main(int argc, char *argv[])
{
int s_s; /*服务器套接字文件描述符*/
struct sockaddr_in local; /*本地地址*/ /*建立TCP套接字*/
s_s = socket(AF_INET, SOCK_STREAM, ); /*初始化地址接哦股*/
memset(&local, , sizeof(local));
local.sin_family = AF_INET;/*AF_INET协议族*/
local.sin_addr.s_addr = htonl(INADDR_ANY);/*任意本地地址*/
local.sin_port = htons(SERVER_PORT);/*服务器端口*/ /*将套接字文件描述符绑定到本地地址和端口*/
int err = bind(s_s, (struct sockaddr*)&local, sizeof(local)); /*处理客户端连接*/
handle_connect(s_s); close(s_s); return ;
}
TCP循环服务器
/** TCP并发服务器--server端程序**/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#define BUFFLEN 1024
#define SERVER_PORT 12347
#define BACKLOG 5 static void handle_request(int s_c)
{
time_t now;
char buff[BUFFLEN];
int n = ;
memset(buff, , BUFFLEN);
n = recv(s_c, buff, BUFFLEN,);/*接收发送方数据*/
if(n > && !strncmp(buff, "TIME", ))/*判断是否合法接收数据*/
{
memset(buff, , BUFFLEN);
now = time(NULL);
sprintf(buff, "%24s\r\n",ctime(&now));
send(s_c, buff, strlen(buff),);/*发送数据*/
}
/*关闭客户端*/
close(s_c);
} static void handle_connect(int s_s)
{
int s_c; /*客户端套接字文件描述符*/
struct sockaddr_in from; /*客户端地址*/
int len = sizeof(from); /*主处理过程*/
while()
{
/*接收客户端连接*/
s_c = accept(s_s, (struct sockaddr*)&from, &len);
if(s_c > )/*客户端成功连接*/
{
/*创建进程进行数据处理*/
if(fork() > )/*父进程*/
{
close(s_c);/*关闭父进程的客户端连接套接字*/
}
else
{
handle_request(s_c);/*处理连接请求*/
return ;
}
}
}
} int main(int argc, char *argv[])
{
int s_s; /*服务器套接字文件描述符*/
struct sockaddr_in local; /*本地地址*/ /*建立TCP套接字*/
s_s = socket(AF_INET, SOCK_STREAM, ); /*初始化地址*/
memset(&local, , sizeof(local));
local.sin_family = AF_INET;/*AF_INET协议族*/
local.sin_addr.s_addr = htonl(INADDR_ANY);/*任意本地地址*/
local.sin_port = htons(SERVER_PORT);/*服务器端口*/ /*将套接字文件描述符绑定到本地地址和端口*/
int err = bind(s_s, (struct sockaddr*)&local, sizeof(local));
err = listen(s_s, BACKLOG);/*侦听*/ /*处理客户端连接*/
handle_connect(s_s); close(s_s); return ;
}
注意,
1. 为了方便退出,server程序中对信号SIGINT信号进行了处理,此时所有的进程都会退出;
2. 并发服务器中的client程序与上一节介绍循环服务器时使用的client程序是一样的,这里不在贴出来,
请参考 Linux网络编程服务器模型选择之循环服务器 http://www.cnblogs.com/lizhenghn/p/3617608.html