程序简介:这是一个传统的多进程服务器模型。当一个客户端连接上服务器时,服务器就产生一个子进程来与客户端进行通信。这种通信机制不但效率低,而且最大连接数会受到系统的最大子进程数的限制。但是它的实现相对简单,所以在早期的UNIX系统中比较流行。
上代码:
#include "my_unp.h"
void str_echo(int sockfd)
{
ssize_t n;
char buf[MAXLINE];
again:
//从套接字中读取数据,写到buffer中去
//再将buffer中的数据写到套接字中去
while( (n=read(sockfd, buf, MAXLINE)) > 0 )
Writen(sockfd, buf, n);
//由于信号中断,没写或读成功任何数据
if( n<0 && errno==EINTR )
goto again;
else if( n < 0 )
error_quit("str_echo: read error");
}
//捕获并处理子进程的SIGCHLD信号
void sig_child(int signo)
{
pid_t pid;
int stat;
while( (pid=waitpid(-1, &stat, WNOHANG)) > 0 )
printf("child %d terminated\n", pid);
return;
}
int main(void)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
char buff[MAXLINE];
struct sockaddr_in cliaddr, servaddr;
//创建用于TCP协议的套接字
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
//把socket和socket地址结构联系起来
Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));
//开始监听LISTENQ端口
Listen(listenfd, LISTENQ);
//处理SIGCHLD信号,防止子进程变成僵死进程
Signal(SIGCHLD, sig_child);
while(1)
{
clilen = sizeof(cliaddr);
//接受一个来自客户端的连接
//如果没有连接请求,就使程序睡眠,直到有连接请求--这是accept函数的特性
//accept函数返回一个描述符,这个socket(connfd)用于同连接到的客户的通信
connfd = accept(listenfd, (SA*)&cliaddr, &clilen);
if( connfd < 0 )
{
//accetp()是慢系统调用,在信号产生时会中断其调用,
//并将errno变量设置为EINTR,此时应重新调用
if( errno == EINTR )
continue;
else
error_quit("accept error");
}
//产生一个子进程,让它处理与(某个客户端的)通信
childpid = Fork();
if( childpid == 0 )
{
Close(listenfd);
str_echo(connfd);
return 0;
}
//输出客户端的IP地址与端口号,还有处理(子)进程的PID
printf("connection from %s, port %d. process with clild %d\n",
Inet_ntop(AF_INET, (void*)&cliaddr.sin_addr, buff, sizeof(buff)),
ntohs(cliaddr.sin_port), childpid);
//与客户端的通信由子进程来处理,所以关闭此套接字
Close(connfd);
}
return 0;
}