本文使用基本的函数编写一个完整的TCP客户/服务器程序示例。这个简单的例子是执行如下步骤的一个回射服务器:
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_port = htons(SERV_PORT);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
listen(listenfd,LISTENQ);
for( ; ; ){
clilen = sizeof(cliaddr);
connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&clilen);
if((childpid = fork())==0){
close(listenfd);
str_echo(connfd);
exit(0);
}
close(connfd);
}
return 0;
}
其中,INADDR_ANY表示的是通配地址,SERV_PORT在头文件unp.h中定义为9877
str_echo函数执行处理每个客户的服务:从客户读入数据,并把它们回射给客户。
void str_echo(int sockfd){
ssize_t n;
char buf[MAXSIZE];
again:
while((n = read(sockfd,buf,MAXLINE))>0)
writen(sockfd,buf,n);
if(n<0 && errno == EINTR)
goto again;
else if(n<0)
err_sys("str_echo:read error");
}
int main(int argc,char **argv){
int sockfd,
struct sockaddr_in servaddr;
if(argc!=2)
err_quit("usage:tcpcli");
sockid = 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,(struct sockaddr*)&servaddr,sizeof(servaddr));
str_cli(stdin,sockfd);
exit(0);
}
TCP回射客户程序:str_cli函数
str_cli函数完成客户处理循环:从标准输入读入一行文本,写到服务器上,都会服务器对该行的回射,并把回射行写到标准输出上。
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);
}
}
信号(signal)就是告知某个进程发生了某个时间的通知,有时也称为软件中断。信号通常是异步发生的,也就是说进程预先不知道信号的准确发生时刻。
信号可以:
每个信号都有一个与之关联的处置(disposition),也称为行为(action)。我们通过调用sigaction函数来设定一个信号的设置,并有三种选择:
建立信号处置的POSIX方法就是调用signaction函数。不过这有点复杂,所以一般简单些的方法就是调用signal函数,其第一个参数是信号名,第二个参数是指向函数的指针,或为常值SIG_IGN或SIG_DFL。
void (*signal(int signo,void (*func)(int)))(int)
僵死进程占用内核中的空间,最终可能导致耗尽进程资源。无论何时我们fork子进程都得wait它们,以防它们变成僵死进程。
void sig_chld(int signo){
pid_t pid;
int stat;
pid = wait(&stat);
printf("child %d terminated\n",pid);
return;
}
#include
//均返回:若成果返回进程ID,若出错返回0或-1
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid,int *statloc,int options);
函数wait和waitpid均返回两个值:已终止子进程的进程ID号,以及通过statloc指针返回的子进程终止状态(一个整数)。