2, 一客户一进程模型
该模型在服务流程不太复杂和系统负担不太重的情况下使用。该模型为每一个客户请求生成一个子进程,由新生成的子进程为客户端服务,我们知道子进程拥有自己的进程空间,这样就可以避免各个子进程间相互影响。
而且每个客户请求到来时,就新生成一个子进程,这样每个客户请求都可以及时的得到响应而不用等待。
其流程如下:
(1) 服务器端开启监听socket
(2) 有客户端请求到来,调到(3)
(3) accept后,生成新的子进程,在子进程中关闭listen socket
(4) 在子进程中为客户请求服务
(5) 若还有客户请求到来,调到(2)
该模型要注意的地方:
1) 当子进程为一个客户请求服务完成后,需要回收子进程的资源。
3) 还有一个小地方要注意:accept可能被信号中断,这里需要处理一下
while (1) {
if (accept() < 0)
if (errno == EINTR)
continue;
}
下面是代码原型,其中的readline和writen来自unpv3的代码。
下面是服务器端的代码:
----------------------------------code----------------------------------------------
/*
* name : echo server.c
* comment: echo string to client
* this version is paralled server using.
*/
#include "echosvr.h"
#include "util.h"
void str_echo(int sockfd);
void sig_chld(int signo);
void sig_chld2(int signo);
int
main(void)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr, svraddr;
/* 用来处理死去的子进程,回收子进程资源 */
signal(SIGCHLD, sig_chld2);
if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
perror("socket()");
exit(1);
}
bzero(&svraddr, sizeof(svraddr));
svraddr.sin_family = AF_INET;
svraddr.sin_addr.s_addr = htonl(INADDR_ANY); /* 可以连接服务器的任何网络接口 */
svraddr.sin_port = htons(SVR_PORT); /* 但必须是固定的服务端口 */
if(bind(listenfd, (struct sockaddr *)&svraddr, sizeof(svraddr)) < 0){
perror("bind()");
exit(1);
}
if(listen(listenfd, LISTENQ) < 0){
perror("listen()");
exit(1);
}
for( ; ; ){
clilen = sizeof(cliaddr);
if((connfd = accept(listenfd,
(struct sockaddr *)&cliaddr,
&clilen)) < 0){
/* 注意处理中断型号,让它重启 */
if(errno == EINTR)
continue;
else{
perror("accept()");
exit(1);
}
}
/* 当有连接到来时,生成一个新的子进程来处理客户请求 */
if((childpid = fork()) == 0){ /* child */
close(listenfd);
str_echo(connfd);
exit(0);
}
close(connfd);
}
}
/* do all things for server */
void
str_echo(int sockfd)
{
size_t n;
char line[MAXLINE];
for( ; ; ){
if((n = readline(sockfd, line, MAXLINE)) == 0)
return ;
writen(sockfd, line, n);
}
}
/* 回收子进程资源 */
void sig_chld2(int signo)
{
pid_t pid;
int stat;
while((pid = waitpid(-1, NULL, WNOHANG)) > 0)
printf("child %d terminated\n", pid);
return ;
}
----------------------------------edoc----------------------------------------------
下面是客户端的代码
----------------------------------code----------------------------------------------
/*
* echo client .c
* echoclt version 3
*/
#include "echoclt.h"
#include "util.h"
void str_clt(FILE *fp, int sockfd);
int
main(int argc, char *argv[])
{
int sockfd;
struct sockaddr_in svraddr;
int i;
if(argc != 2){
fprintf(stderr, "usage: %s \n", argv[0]);
exit(1);
}
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
perror("socket()");
exit(1);
}
bzero(&svraddr, sizeof(svraddr));
svraddr.sin_family = AF_INET;
svraddr.sin_port = htons(SVR_PORT);
inet_pton(AF_INET, argv[1], &svraddr.sin_addr);
if(connect(sockfd,
(struct sockaddr *)&svraddr,
sizeof(svraddr)) < 0){
perror("connect()");
exit(1);
}
str_clt(stdin, sockfd);
exit(0);
}
/* 通过使用select我们可以探测到多个不同类型的描述符的读写情况 */
void
str_clt(FILE *fp, int sockfd)
{
int maxfd;
fd_set rset;
char sendline[MAXLINE], rcvline[MAXLINE];
FD_ZERO(&rset);
for( ; ; ){
FD_SET(fileno(fp), &rset); /* set two fd */
FD_SET(sockfd, &rset);
maxfd = max(fileno(fp), sockfd) + 1;
if(select(maxfd, &rset, NULL, NULL, NULL) < 0){
fprintf(stderr, "select(): %s\n", strerror(errno));
return;
}
/* 通过这一行,我们可以实时的得到消息:到服务器程序是否崩溃 */
if(FD_ISSET(sockfd, &rset)){ /* socket is readable */
if(readline(sockfd, rcvline, MAXLINE) == 0) {
fprintf(stderr, " server terminated prematurely\n");
return ;
}
fputs(rcvline, stdout);
}
if(FD_ISSET(fileno(fp), &rset)){ /* input is readable */
if(fgets(sendline, MAXLINE, fp) == NULL)
return;
writen(sockfd, sendline, strlen(sendline));
}
}
}
----------------------------------edoc----------------------------------------------
以上例子来自unpv3。
参考书籍<>
阅读(2481) | 评论(0) | 转发(1) |