1、多线程的web服务器
如果程序员忘记使用pthread_join来收回线程,这些线程所占用的资源就无法被收回,类似于用malloc来分配的空间却没有用free释放掉一样。这里可以创建不需要返回的线程,称之为独立线程(detached threads)。当函数执行完毕之后,独立线程自动释放它所占用的所有的资源,它们自身也不允许等待其他的线程返回。通过传递一个特殊的属性参数给函数pthread_create来创建一个独立线程:
- pthread_t t;
- pthread_attr_t attr_detached;
- pthread_attr_init(&attr_detached);
- pthread_attr_setdetached(&attr_detached,PTHREAD_CREATE_DETACHED);
- pthread_cerate(&t,&attr_detached,func,arg);
2、多线程版本的bounce1d.c
信号处理者和定时器机制虽然可以完成工作,但线程机制更好地匹配了内部和外部的结构。在外部,用户可以看到两个独立的活动流程:动画和键盘控制;在内部,线程可以将控制动画代码和键盘输入代码分开。原来是通过发送信号控制,控制信号发送的时间调整快慢;现在使用多线程,控制动画和键盘输入分开了,直接控制字符显示时间调整快慢。
3、基于多线程机制的多重动画tanimate.c
tanimate可以最多同时有11个线程在运行,在线程运行过程中,如何防止线程间的共享冲突?
(1)数据冲突:互斥量的动态初始化
控制动画的函数可以使用或修改作为参数传递给它的结构体成员的值,它们包括位置、速度以及运动方向。当用户使用一条消息跳动之后,输入线程修改了结构体中的成员dir的值,共享变量需要互斥量来防止数据的冲突。解决方法:每一个结构体中建一个互斥量pthread_mutex_t lock,当控制动画的线程和用户输入线程读取或修改结构体中共享变量的时候,这个互斥量开始工作。
(2)屏幕冲突:临界区
修改屏幕和光标位置的各函数同样也被所有动画线程共享,可使用互斥量mx来防止对这些函数的同步访问冲突。
4、编写程序代码
twebserv.c
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<netdb.h>
#include<time.h>
#include<stdbool.h>
#include<sys/stat.h>
#include<string.h>
#include<pthread.h>
#include<dirent.h>
#define HOSTLEN 256
#define BACKLOG 1
#define oops(msg) {perror(msg); exit(1);}
time_t server_started;
int server_bytes_sent;
int server_requests;
int make_server_socket_q(int portnum,int backlog)
{
struct sockaddr_in saddr;
struct hostent *hp;
char hostname[HOSTLEN];
int sock_id;
sock_id=socket(PF_INET,SOCK_STREAM,0);
if(sock_id==-1)
oops("socket");
bzero((void*)&saddr,sizeof(saddr));
gethostname(hostname,HOSTLEN);
hp=gethostbyname(hostname);
bcopy((void*)hp->h_addr,(void*)&saddr.sin_addr,hp->h_length);
saddr.sin_port=htons(portnum);
saddr.sin_family=AF_INET;
if(bind(sock_id,(struct sockaddr*)&saddr,sizeof(saddr))!=0)
oops("bind");
if(listen(sock_id,backlog)!=0)
oops("listen");
return sock_id;
}
int make_server_socket(int portnum)
{
return make_server_socket_q(portnum,BACKLOG);
}
int http_reply(int fd,FILE **fpp,int code,char *msg,char *type,char *content)
{
FILE *fp=fdopen(fd,"w");
int bytes=0;
if(fp!=NULL)
{
bytes=fprintf(fp,"HTTP/1.0 %d %s\r\n",code,msg);
bytes+=fprintf(fp,"Content-type:%s\r\n",type);
if(content)
bytes+=fprintf(fp,"%s\r\n",content);
}
fflush(fp);
if(fpp)
*fpp=fp;
else
close(fp);
return bytes;
}
void cannot_do(int fd)
{
http_reply(fd,NULL,501,"Not Implemented","text/plain","That command is not yet implement");
}
bool not_exist(char *f)
{
struct stat info;
//stat把文件fname的信息复制到指针bufp所指的结构
return (stat(f,&info)==-1);
}
void do_404(char *item,int fd)
{
http_reply(fd,NULL,404,"Not Found","text/plain","The item you seek is not here");
}
bool isadir(char *f)
{
struct stat info;
//stat把文件fname的信息复制到指针bufp所指的结构
return (stat(f,&info)!=-1 && S_ISDIR(info.st_mode));
}
void do_ls(char *dir,int fd)
{
DIR *dirptr;
struct dirent *direntp;
int bytes=0;
FILE *fp;
bytes=http_reply(fd,&fp,200,"OK","text/plain",NULL);
bytes+=fprintf(fp,"Listing of Directory %s\n",dir);
if((dirptr=opendir(dir))!=NULL)
{
while(direntp=readdir(dirptr))
{
bytes+=fprintf(fp,"%s\n",direntp->d_name);
}
closedir(dirptr);
}
fclose(fp);
server_bytes_sent+=bytes;
}
//得到文件后缀名
char *file_type(char *f)
{
char *cp;
if((cp=strrchr(f,'.'))!=NULL)
return cp+1;
return "";
}
void do_cat(char *f,int fd)
{
char *extension=file_type(f);
char *content="text/plain";
FILE *fpsock,*fpfile;
char c;
int bytes=0;
if(strcmp(extension,"html")==0)
content="text/html";
else if(strcmp(extension,"gif")==0)
content="text/gif";
else if(strcmp(extension,"jpg")==0)
content="text/jpeg";
else if(strcmp(extension,"jpeg")==0)
content="text/jpeg";
fpsock=fdopen(fd,"w");
fpfile=fopen(f,"r");
if(fpsock!=NULL && fpfile!=NULL)
{
bytes=http_reply(fd,&fpsock,200,"OK",content,NULL);
while((c=getc(fpfile))!=EOF)
putc(c,fpsock);
fclose(fpfile);
fclose(fpsock);
}
server_bytes_sent+=bytes;
}
int built_in(char *arg,int fd)
{
FILE *fp;
if(strcmp(arg,"status")!=0)
return 0;
http_reply(fd,&fp,200,"OK","text/plain",NULL);
fprintf(fp,"Server started:%s",ctime(&server_started));
fprintf(fp,"Total requests:%d\n",server_requests);
fprintf(fp,"Bytes sent out:%d\n",server_bytes_sent);
fclose(fp);
return 1;
}
void process_rq(char *rq,int fd)
{
char cmd[BUFSIZ],arg[BUFSIZ];
if(sscanf(rq,"%s %s",cmd,arg)!=2)
return;
if(strcmp(cmd,"GET")!=0) //必须通过GET实现请求
cannot_do(fd);
else if(built_in(arg,fd)) //如果arg是status返回服务器状态
;
else if(not_exist(arg)) //arg必须是已存在的文件或目录
do_404(arg,fd);
else if(isadir(arg)) //如果arg是目录执行ls命令
do_ls(arg,fd);
else //如果是其他文件执行cat
do_cat(arg,fd);
}
void setup(pthread_attr_t *attrp)
{
pthread_attr_init(attrp);
pthread_attr_setdetachstate(attrp,PTHREAD_CREATE_DETACHED);
time(&server_started);
server_requests=0;
server_bytes_sent=0;
}
void handle_call(int *fdptr)
{
FILE *fpin;
char request[BUFSIZ];
int fd=*fdptr;
free(fdptr);
fpin=fdopen(fd,"r");
fgets(request,BUFSIZ,fpin);
printf("got a call on %d:request=%s",fd,request);
process_rq(request,fd);
fclose(fpin);
}
int main(int argc,char *argv[])
{
int sock,fd;
int *fdptr;
pthread_t worker;
pthread_attr_t attr;
if(argc==1)
oops("usage:twebserv portnum\n");
sock=make_server_socket(atoi(argv[1]));
if(sock==-1)
oops("sock");
setup(&attr);
while(1)
{
fd=accept(sock,NULL,NULL);
server_requests++;
fdptr=malloc(sizeof(int));
*fdptr=fd;
pthread_create(&worker,&attr,handle_call,fdptr);
}
return 0;
}
twebclnt.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<stdlib.h>
#include<stdlib.h>
#include<strings.h>
#define oops(msg) {perror(msg); exit(1);}
int connect_to_server(char *host,int portnum)
{
struct sockaddr_in servadd;
struct hostent *hp;
int sock_id;
sock_id=socket(AF_INET,SOCK_STREAM,0);
if(sock_id==-1)
oops("socket");
bzero(&servadd,sizeof(servadd));
hp=gethostbyname(host);
if(hp==NULL)
oops(host);
bcopy(hp->h_addr,(struct sockaddr*)&servadd.sin_addr,hp->h_length);
servadd.sin_port=htons(portnum);
servadd.sin_family=AF_INET;
if(connect(sock_id,(struct sockaddr*)&servadd,sizeof(servadd))!=0)
oops("connect");
return sock_id;
}
int main(int argc,char *argv[])
{
struct sockaddr_in servadd;
struct hostent *hp;
int sock_id,sock_fd;
char message[BUFSIZ];
int messlen,n_read;
sock_id=connect_to_server(argv[1],atoi(argv[2]));
read(0, message, BUFSIZ);
if(write(sock_id,message,BUFSIZ)==-1)
oops("write");
while((n_read=read(sock_id,message,BUFSIZ))>0)
if(write(1,message,n_read)==-1)
oops("write");
close(sock_id);
return 0;
}
tbounce1d.c
#include<stdio.h>
#include<curses.h>
#include<signal.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/time.h>
#define MESSAGE "hello"
#define BLANK " "
int row;
int col;
int dir;
int delay;
void move_msg(char *msg)
{
while(1)
{
usleep(delay*1000);
move(row,col);
addstr(BLANK);
col+=dir;
move(row,col);
addstr(MESSAGE);
refresh();
if(dir==-1&&col<=0)
dir=1;
else if(dir==1&&col+strlen(msg)>=COLS)
dir=-1;
}
}
int main()
{
int newdelay;
char c;
pthread_t msg_thread;
initscr();
crmode();
noecho();
clear();
row=10;
col=10;
dir=1;
delay=200; //200ms=0.2seconds
if(pthread_create(&msg_thread,NULL,move_msg,MESSAGE))
{
fprintf(stderr,"error creating thread");
endwin();
exit(0);
}
while(1)
{
newdelay=0;
c=getch();
if(c=='q') break;
if(c==' ') dir=-dir;
if(c=='f'&&delay>2) newdelay=delay/2;
if(c=='s') newdelay=delay*2;
if(newdelay>0)
delay=newdelay;
}
pthread_cancel(msg_thread);
endwin();
return 0;
}
tanimate.c
#include<stdio.h>
#include<curses.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#define MAXMSG 10
#define TUNIT 20000
struct propset{
char *str;
int row;
int delay;
int dir;
};
pthread_mutex_t mx=PTHREAD_MUTEX_INITIALIZER;
int setup(int nstrings,char *strings[],struct propset props[])
{
int num_msg=(nstrings>MAXMSG?MAXMSG:nstrings);
srand(getpid());
int i;
for(i=0;i<num_msg;i++)
{
props[i].str=strings[i];
props[i].row=i;
props[i].delay=1+(rand()%15); //1-15
props[i].dir=((rand()%2?1:-1)); //1 or -1
}
initscr();
crmode();
noecho();
clear();
mvprintw(LINES-1,0,"q to quit,0..%d to bounce",num_msg-1);
return num_msg;
}
void animate(struct propset *arg)
{
struct propset *info=arg;
int len=strlen(info->str)+2;
int col=rand()%(COLS-len-3);
while(1)
{
usleep(info->delay*TUNIT);
pthread_mutex_lock(&mx);
move(info->row,col);
addch(' ');
addstr(info->str);
addch(' ');
move(LINES-1,COLS-1);
refresh();
pthread_mutex_unlock(&mx);
col+=info->dir;
if(info->dir==-1&&col<=0)
info->dir=1;
else if(info->dir==1&&col+len>=COLS)
info->dir=-1;
}
}
int main(int argc,char *argv[])
{
char c;
pthread_t thrds[MAXMSG];
struct propset props[MAXMSG];
int num_msg;
int i;
if(argc==1)
{
printf("usage:tanimate string1 string2...\n");
exit(1);
}
num_msg=setup(argc-1,argv+1,props);
for(i=0;i<num_msg;i++)
{
if(pthread_create(&thrds[i],NULL,animate,&props[i]))
{
fprintf(stderr,"error creating thread");
endwin();
exit(0);
}
}
while(1)
{
c=getch();
if(c=='q') break;
if(c==' ')
{
for(i=0;i<num_msg;i++)
props[i].dir=-props[i].dir;
}
if(c>='0'&&c<='9')
{
i=c-'0';
if(i<num_msg)
props[i].dir=-props[i].dir;
}
}
pthread_mutex_lock(&mx);
for(i=0;i<num_msg;i++)
pthread_cancel(&thrds[i]);
endwin();
return 0;
}