网络编程第五章:TCP客户服务器程序一 和 signal函数

服务器:
#include    "unp.h"

void str_echo(int sockfd){
	ssize_t n;
	char buf[MAXLINE];
again:
	while((n=read(sockfd,buf,MAXLINE))>0)
		write(sockfd,buf,n);
	if(n<0&&errno == EINTR)
		goto again;
	else if(n<0)
		printf("read error");

	
}

int
main(int argc, char **argv)
{
    int                 listenfd, connfd;
    pid_t               childpid;
    socklen_t           clilen;
    struct sockaddr_in  cliaddr, 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(SERV_PORT);
 
    bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
 
    listen(listenfd, LISTENQ);
 
    for ( ; ; ) {
        clilen = sizeof(cliaddr);
        connfd = accept(listenfd, (SA *) &cliaddr, &clilen);
 
        if ( (childpid = fork()) == 0) {    /* child process */
            close(listenfd);    /* close listening socket */
            str_echo(connfd);   /* process the request */
            exit(0);
        }
        close(connfd);          /* parent closes connected socket */
    }
}

客户端:
#include	"unp.h"


void
str_cli(FILE *fp, int sockfd)
{
	char	sendline[MAXLINE], recvline[MAXLINE];

	while (Fgets(sendline, MAXLINE, fp) != NULL) {

		Writen(sockfd, sendline, strlen(sendline));

		if (Readline(sockfd, recvline, MAXLINE) == 0)
			err_quit("str_cli: server terminated prematurely");

		Fputs(recvline, stdout);
	}
}

int
main(int argc, char **argv)
{
	int					sockfd;
	struct sockaddr_in	servaddr;

	if (argc != 2)
		err_quit("usage: tcpcli <IPaddress>");

	sockfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(SERV_PORT);
	Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

	Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));

	str_cli(stdin, sockfd);		/* do it all */

	exit(0);
}

建立信号处理的方法就是调用sigaction函数,但是调用这个函数比较复杂.

简单些的方法是调用signal函数,

Sigfunc * signal(int signo,Sigfunc *  func)



signal(信号名,函数或者SIG_IGN或者SIGDFL).第二个参数和返回值都是指向信号处理函数的指针.


/* include signal */
#include	"unp.h"

Sigfunc *
signal(int signo, Sigfunc *func)
{
	struct sigaction	act, oact;

	act.sa_handler = func;//指向信号处理函数
	sigemptyset(&act.sa_mask);//这个信号设置成空集,表示不阻塞任何信号,posix允许指定的信号,在函数调用期间,任何被阻塞的信号都不能传递给进程,就被阻塞
	act.sa_flags = 0;
	if (signo == SIGALRM) {
#ifdef	SA_INTERRUPT
		act.sa_flags |= SA_INTERRUPT;	/* SunOS 4.x */
#endif
	} else {  
#ifdef	SA_RESTART
		act.sa_flags |= SA_RESTART;		/* SVR4, 44BSD */
#endif
	}//sa_restart标志是可选的,如果设置,由相应信号中断的系统调用讲由内核自动重启
	if (sigaction(signo, &act, &oact) < 0)//调用sigaction函数
		return(SIG_ERR);
	return(oact.sa_handler);
}
/* end signal */

在本文开头,那个服务器客户端程序,服务器父进程创建子进程,子进程销毁的时候向服务器发送一个sigchle信号,然后处于僵死状态.

僵死状态存在的理由是:为了方便以后父进程或者这个已经僵死的进程的信息.

如果父进程死亡,那么僵死进程的父进程将会重置为1,也就是init进程,init进程将会清理这些僵死进程.

在fork的时候都wait子进程,以防子进程变为僵死进程.

处理sigchld的函数如下:

#include	"unp.h"

void sig_chld(int signo){
	pid_t	pid;
	int		stat;
	pid = wait(&stat);
	printf("child %d terminated\n", pid);
	return;
}

修改服务器程序:

#include	"unp.h"

void str_echo(int sockfd){
	ssize_t n;
	char buf[MAXLINE];
again:
	while((n=read(sockfd,buf,MAXLINE))>0)
		write(sockfd,buf,n);
	if(n<0&&errno == EINTR)
		goto again;
	else if(n<0)
		printf("read error");

	

}

void sig_chld(int signo){
	pid_t	pid;
	int		stat;
	pid = wait(&stat);
	printf("child %d terminated\n", pid);
	return;
}
int
main(int argc, char **argv)
{
	int					listenfd, connfd;
	pid_t				childpid;
	socklen_t			clilen;
	struct sockaddr_in	cliaddr, servaddr;
	void				sig_chld(int);

	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(SERV_PORT);

	Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

	Listen(listenfd, LISTENQ);

	Signal(SIGCHLD, sig_chld);//在listen之后调用这个信号处理函数

	for ( ; ; ) {
		clilen = sizeof(cliaddr);
		connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);

		if ( (childpid = Fork()) == 0) {	/* child process */
			Close(listenfd);	/* close listening socket */
			str_echo(connfd);	/* process the request */
			exit(0);
		}
		Close(connfd);			/* parent closes connected socket */
	}
}

对于accept来讲,是一个慢系统调用,也就是说,也就是系统调用这个函数有可能永远都不会返回.
在上述代码中,sigchld信号在accept调用被父进程捕获,accept返回一个enter错误,父进程不处理该错误.
有些内核会重启enter错误导致的系统调用中断,但是有些不会,(这里不是太懂,)

wait和waitpid的区别
pid_t wait(int *slatloc){}
pid_t waitpid(pid_t pid,int *statloc,int options)

如果成功返回ID,如果出错返回0或者-1
wait信号处理函数只执行一次,然而Unix信号是不排队的,所以同时多个进程僵死,只能处理一个僵死进程,但是waitpid可以,如下:

#include "unp.h"
void sig_child(int signo){
	pid_t pid;
	int stat;
	while((pid = waitpid(-1,stat,WNOHANG))>0){//使用WNOHANG在有尚未终止的进程的时候不能阻塞,不阻塞就会有循环,有循环就能一直调用waitpid从而处理所有的僵死进程
		printf("子进程已经终止");
	}
	return ;
}

网络编程中可能遇到的三个情况:
1 : fork子进程的时候必须捕获sigchld信号,
2 :捕获信号的时候,必须处理被中断的系统调用
3 :sigchld捕获函数必须正确编写(waitpid)

关于被中断的系统调用:
我们的服务器函数必须从for循环开始改起.

	for(;;){
		clilen = sizeof(cliaddr);
		if((connfd=accept(listenfd,(SA*)&cliaddr,&clilen)<0)){
			if(errno == EINTR)continue;
			else err_sys("accept error");
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值