若对服务器进行分类,如果按照对于事件处理机制来分,则可以划分为迭代服务器和并发服务器。
所谓的迭代服务器是指在完成对当前客户的服务之前无法处理已等待服务的新客户,即是串行为客户服务的。而并发服务器则是并发地同时地为多个客户提供服务。
本文实现两个最初级版本的两种服务器。
首先介绍客户端
/*
Author:<song0071000#126.com>
time:2014/02/24
func:implements the client
*/
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<unistd.h>
#include<stdio.h>
int main(int argc,char* argv[])
{
struct sockaddr_in ser_addr;
bzero(&ser_addr,sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(8888);
if(inet_pton(AF_INET,"127.0.0.1",(void*)&(ser_addr.sin_addr))!=1)
{
printf("inet_pton error!\n");
return -1;
}
int sfd = socket(AF_INET,SOCK_STREAM,0);
connect(sfd,(struct sockaddr*)&ser_addr,sizeof(ser_addr));
char buf[255];
memset(buf,0,255);
read(sfd,buf,255);
printf("Now time is %s\n",buf);
return 0;
}
该客户端非常简单。只是连接到服务端,并打印服务端服务端发来的数据。这里并没有设置客户端的ip地址以及端口号,让内核自动选择端口和出口ip。
而迭代服务器:
/*
Author:<song0071000#126.com>
time:2014/02/24
func:implements iterative server
*/
#include<stdlib.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdio.h>
#include<unistd.h>
#include<time.h>
#include<sys/wait.h>
#include<signal.h>
int main(int argc,char* argv[])
{
struct sockaddr_in address;
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(8888);
if(inet_pton(AF_INET,"127.0.0.1",(void*)&(address.sin_addr))!=1)
{
printf("inet_pton error!\n");
perror("inet_pton");
return -1;
}
int sockfd = socket(AF_INET,SOCK_STREAM,0);
bind(sockfd,(struct sockaddr*)&address,sizeof(address));
listen(sockfd,6);
int connfd;
while(1)
{
connfd = accept(sockfd,NULL,NULL);
time_t t;
time(&t);
char time_str[255]={0};
struct tm* tp= localtime(&t);
strftime(time_str,100,"%Y-%m-%d-%H:%M:%S",tp);
printf("==>>server time is %s\n",time_str);
write(connfd,time_str,255);
/* simulate process data 10 seconds */
sleep(10);
close(connfd);
}
return 0;
}
这里服务器端使用while循环来,串行处理客户到来的连接请求。考虑两个客户端同时向服务器发送请求时。若服务器接收了第一个客户的连接请求,那么第二个客户的请求处理时间则,将会推迟10s。因为在处理第一个客户的请求时,服务器是不能同时accept第二个客户的请求的。
具体如下图所示:
在开启服务器之后,同时运行两个客户。可以看到有一个客户几乎是即时响应的,而另一个则等了将近10s。
和迭代服务器相对应的并发服务器如下:
/*
Author:<song0071000#126.com>
time:2014/02/24
func:implements concurrent server
*/
#include<stdlib.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdio.h>
#include<unistd.h>
#include<time.h>
#include<sys/wait.h>
#include<signal.h>
int main(int argc,char* argv[])
{
struct sockaddr_in address;
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(8888);
if(inet_pton(AF_INET,"127.0.0.1",(void*)&(address.sin_addr))!=1)
{
printf("inet_pton error!\n");
perror("inet_pton");
return -1;
}
int sockfd = socket(AF_INET,SOCK_STREAM,0);
bind(sockfd,(struct sockaddr*)&address,sizeof(address));
listen(sockfd,6);
int connfd;
while(1)
{
connfd = accept(sockfd,NULL,NULL);
/* use fork to reduce the respond time. */
int cpid = fork();
if(cpid == 0)
{
close(sockfd);
/*child process*/
time_t t;
time(&t);
char time_str[255]={0};
struct tm* tp= localtime(&t);
strftime(time_str,100,"%Y-%m-%d-%H:%M:%S",tp);
printf("==>>server time is %s\n",time_str);
write(connfd,time_str,255);
/* simulate process data 10 seconds */
sleep(10);
close(connfd);
/* child process task complete */
exit(0);
}
else
{
/*father process*/
//do nothing
close(connfd);
signal(SIGCHLD,SIG_IGN);
}
}
return 0;
}
该并发服务器使用fork操作来解放服务器,服务器只负责接收请求。具体的请求处理由其子进程来进行。这里父进程也就是服务器在fork之后关闭了连接套接字,而子进程则关闭监听套接字。还有一点就是避免僵尸进程的出现。考察到这里使用wait/waitpid并不适合这里的情况,我们选择了忽略子进程关闭时发送时SIGCHLD信号的方式来防止子进程成为僵尸进程(当然还有一种方式就是fork之后在子进程中再fork一次,在孙进程中执行请求处理。父进程使用wai/waitpid来回收子进程,子进程在孙进程退出之前退出)。
实验结果如图所示:
可以看到同时发出请求的两个客户,几乎同时得到了响应。
本人享有博客文章的版权,转载请标明出处http://blog.csdn.net/baidu20008