预先创建子进程的方式
预先创建子进程的方式比为每个客户端创建一个进程的方式优越的地方在于当服务器启动时就将子进程创建好,当一个客户端连接至服务器时选择一个可用的子进程处理,这样可以节省到创建子进程的消耗。有一个弊端是需要事先确定子进程的个数,当客户端多于子进程的个数时,基于的客户端只能等待可用的子进程。有一种解决方法就是动态的分配子进程的个数,当可用的子进程小于事先设定的阀值时,增加一定数量的子进程,相持当可用子进程多于事先设定的阀值时,减少一定数量的子进程。
这种方式的实现是利用了UNIX内核对文件描述符的实现,让所有的子进程全部监听处于Listen状态的描述符(即调用accept函数),当一个客户端连接进来时所有的子进程被激活,但是只有一个进程可以与客户端进行连接,其它的进程继续进入阻塞状态。为了防止这种竞争发生错误,可以加入锁机制,只有得到锁的进程才能与客户端进行连接,可以用文件锁或线程锁等机制实现。
例子:回射服务器,即服务器端接收客户端来的数据,并将数据原样返回给客户端,代码在CentOS5.0测试通过
服务器端代码:
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/select.h>
#include <strings.h>
#include <errno.h>
#include <stdlib.h>
#define SERV_PORT 2003
#define MAXLINE 1024
#define LISTENQ 4
int makeListenByPort(short port)
{
int listenfd;
struct sockaddr_in servaddr;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd, LISTENQ);
return listenfd;
}
/* 客户请求处理函数 */
void requestProcessFunc(int connfd)
{
char buf[MAXLINE];
int n;
for(;;) {
if((n = read(connfd, buf, MAXLINE)) == 0) {
printf("client is quit\n");
break;
}else{
write(connfd, buf, n);
}
}
}
int main(int argc, char **argv)
{
int listenfd, connfd, maxfd, sockfd;
char buf[MAXLINE];
struct sockaddr_in cliaddr;
int i;
int clilen, adrlen;
pid_t child;
/*根据端号建立监听 */
listenfd= makeListenByPort(SERV_PORT);
adrlen = sizeof(struct sockaddr);
/*创建5个子进程 */
for(i = 0; i < 5; i++) {
if((child = fork()) == 0) {
/* 每个子进程都循环的监听 */
for(;;) {
clilen = adrlen;
if((connfd = accept(listenfd,(struct sockaddr *)&cliaddr, &clilen)) < 0){
if(errno == EINTR)
continue;
else{
printf("accepterror!");
exit(0);
}
}
requestProcessFunc(connfd);
close(connfd);
}
}
}
/*父进程不做任何操作 */
for(;;) pause();
}
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#define MAXLINE 1024
#define SERV_PORT 2003
void cli_str(FILE *fp, int sockfd)
{
char sendline[MAXLINE];
char recvline[MAXLINE];
memset(recvline, 0x00, sizeof(recvline));
memset(sendline, 0x00, sizeof(sendline));
printf("cli#");
while((fgets(sendline, MAXLINE, fp)) != NULL) {
write(sockfd, sendline, strlen(sendline));
if(read(sockfd, recvline, MAXLINE) == 0) {
printf("str_cli:serverterminated prematurely\n");
exit(0);
}
fputs(recvline, stdout);
fflush(stdout);
memset(recvline, 0x00, sizeof(recvline));
printf("cli#");
}
}
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("socket error\n");
exit(0);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, argv[1],&servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
if((connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)))== -1) {
printf("connect error\n");
exit(0);
}
cli_str(stdin, sockfd);
exit(0);
}