建立三个文件 分别bin include src 还有一个makefile
在这三个文件的目录下
1.make
2.进入bin文件,gcc -client.c -o client
3.在bin文件下mkfifo fifo
4.在bin文件下./main fifo 3(开三个线程)
5.再开启一个终端,在bin下 ./client fifo
在客户端发送字符串,服务器受到会返回这个字符串,可以见执行任务的函数修改,来执行其他任务而不是简单的返回字符串,通过修改src文件下的task.c中的excute_task函数
makefile
SRC_DIR := ./src INC_DIR := ./include EXE_DIR := ./bin CC := gcc CFLAGS := -g -o SRC_OBJECTS := $(wildcard $(SRC_DIR)/*.c) INC_OBJECTS := $(wildcard $(INC_DIR)/*.h) $(EXE_DIR)/main : $(SRC_OBJECTS) $(INC_OBJECTS) $(CC) $(CFLAGS) $@ $(SRC_OBJECTS) -I$(INC_DIR) -lpthreadinclude文件中的server.h
/************************************************************************* > File Name: server.h > Author: yang > Mail:[email protected] > Created Time: 2014年08月27日 星期三 10:01:27 ************************************************************************/ #ifndef __SERVER_H__ #define __SERVER_H__ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <pthread.h> #include <sys/time.h> #include <sys/select.h> #include <signal.h> #include<pthread.h> #define MSG_LEN 1024 #define TASK_CNT 1024 extern pthread_mutex_t mutex; extern pthread_cond_t cond_master; extern pthread_cond_t cond_slave; typedef struct tag_fds{ int s_rfd; int s_wfd; struct tag_fds *s_next; }FD_PAIR,*pFD_PAIR;//建立一个链表,放输入标识符,输出标识符 typedef struct tag_task{ char s_msg[1024]; int s_fd; }TASK,*pTASK;//将任务定义成结构体,存放着写描述符和信息 typedef struct tag_que{ TASK s_arr[TASK_CNT + 1]; int s_front; int s_tail; }QUEUE,*pQUEUE;//一个人物队列,线程来处理(添加任务,执行任务) void fds_link_init(pFD_PAIR* phread);//对描述符链表进行操作 void fds_insert(pFD_PAIR* phead,int fd_r,int fd_w); int fds_find_wfd(pFD_PAIR phead,int fd_r); void fds_link_delete(pFD_PAIR* phead,int fd_r); void add_task(pQUEUE pq,pTASK pt);//在set集合中寻找那个client发信息了,然后添加任务 void get_task(pQUEUE pq ,pTASK pt);//建立线程的时候用,直接执行任务 void excute_task(pTASK pt); #endifsrc文件中的 fds_link.c
/************************************************************************* > File Name: fds_link.c > Author: yang > Mail:[email protected] > Created Time: 2014年08月27日 星期三 10:19:10 ************************************************************************/ #include<server.h> void fds_link_init(pFD_PAIR* phead){ *phead=NULL; } void fds_insert(pFD_PAIR* phead,int fd_r,int fd_w){//向描述符链表插入 pFD_PAIR pCur = (pFD_PAIR)calloc(1,sizeof(FD_PAIR)); pCur ->s_rfd = fd_r; pCur ->s_wfd = fd_w; pCur ->s_next = *phead; *phead=pCur; } int fds_find_wfd(pFD_PAIR phead,int fd_r){//根据读描述符找到写描述符,因为server的set里放的是读描述符,server先看那个客户端给他发信息了,在找相应的读描述符,给给客户端写回结果 while(phead){ if(phead->s_rfd==fd_r) break; else phead=phead->s_next; } if(phead == NULL) return -1; else return phead ->s_wfd; } void fds_link_delete(pFD_PAIR *phead,int fd_r){//将该描述符链表中删掉 pFD_PAIR pPre,pCur; pPre = NULL; pCur = *phead; while(pCur){ if(pCur ->s_rfd ==fd_r) break; else{ pPre=pCur; pCur=pCur->s_next; } } if(pPre==NULL){//如果链表只有一个元素的情况 pPre ->s_next=pCur ->s_next; free(pCur); pCur=NULL; } }src 中 task.c
/************************************************************************* > File Name: task.c > Author: yang > Mail:[email protected] > Created Time: 2014年08月27日 星期三 10:31:16 ************************************************************************/ #include "server.h" static int que_empty(pQUEUE pq)//队列是否空 { return pq -> s_front == pq -> s_tail ; } static int que_full(pQUEUE pq)//队列是否满 { return (pq -> s_tail + 1)%(TASK_CNT + 1) == pq -> s_front ; } static int que_cnt(pQUEUE pq)//队列中元素的数量 { return (pq ->s_tail - pq ->s_front + TASK_CNT + 1)%(TASK_CNT + 1) ; } void add_task(pQUEUE pq,pTASK pt){ pthread_mutex_lock(&mutex); while(que_full(pq))//如果队列为满 pthread_cond_wait(&cond_master,&mutex);//让所有server都等着,因为就server往里添加任务 pq ->s_arr[pq->s_tail]=*pt;//队列操作 pq ->s_tail =(pq->s_tail+1)%(TASK_CNT + 1); { pthread_cond_broadcast(&cond_slave);//通知所有线程多来抢任务啊 } pthread_mutex_unlock(&mutex); sleep(1); } void get_task(pQUEUE pq,pTASK pt){//执行任务 pthread_mutex_lock(&mutex); while(que_empty(pq))//让任务队列为空,所有线程都等着,因为没有任务啦 pthread_cond_wait(&cond_slave,&mutex); *pt=(pq ->s_arr)[pq->s_front]; pq->s_front=(pq->s_front+1)%(TASK_CNT+1); { pthread_cond_broadcast(&cond_master);//通知server赶紧添加任务 } pthread_mutex_unlock(&mutex); sleep(1); } void excute_task(pTASK pt){ printf("excute_task:%d,%s\n",pt->s_fd,pt->s_msg);//执行任务啦 write(pt->s_fd,pt->s_msg,strlen(pt->s_msg)); }src文件中 main.c
/************************************************************************* > File Name: main.c > Author: yang > Mail:[email protected] > Created Time: 2014年08月27日 星期三 10:44:35 ************************************************************************/ #include<server.h> pthread_mutex_t mutex; pthread_cond_t cond_master; pthread_cond_t cond_slave; void* slave_handler(void *arg){ pthread_detach(pthread_self()); pQUEUE pq=(pQUEUE)arg;//任务队列 TASK my_task; while(1){ get_task(pq,&my_task);//每个线程都保存应该想哪个管道里发的描述符wfd excute_task(&my_task);//使用excute单独执行任务,因为这样执行什么任务我们可以自己定义 sleep(1); } } int main(int argc,char *argv[]){ if(argc!=3){//当输入的参数不是3的时候提示 printf("wrong!"); exit(1); } signal(SIGPIPE,SIG_IGN);//忽略SIGPIPE信号,当客户端关闭,服务器还通过管道给客户端发信号,系统就会发送SIGPIPE信号,把程序挂掉 signal(SIGQUIT,SIG_IGN);//忽略ctrl + \ 信号 int fd_server;//服务器有一个管道来接受客户上线信息 QUEUE my_que;//任务队列,线程在这里面找执行任务 pFD_PAIR my_list;//输出描述符,输入描述符链表,server通过输入描述符找到客户端输出描述符,然后发送信息 fd_set read_set,ready_set;//定义两个集合,server轮回检查谁发信息 struct timeval tm;//轮回时间 int select_ret; memset(&my_que,0,sizeof(QUEUE)); fds_link_init(&my_list); int slave_cnt = atoi(argv[2]);//线程的个数 pthread_t *arr=(pthread_t*)calloc(slave_cnt,sizeof(pthread_t));//用来初始化的线程的描述符数组,动态分配 pthread_mutex_init(&mutex,NULL);//初始化锁 pthread_cond_init(&cond_master,NULL);//初始化等待函数 pthread_cond_init(&cond_slave,NULL); int index=0; while(slave_cnt>0){ pthread_create(arr+index,NULL,slave_handler,(void*)&my_que);//建立线程,来执行任务 slave_cnt--; index++; } fd_server = open(argv[1],O_RDONLY);//从server的那个管道读 出客户端上线信息 if(fd_server == -1){ perror("open"); exit(-1); } FD_ZERO(&read_set);//集合清零 FD_SET(fd_server,&read_set);//将fd_server这个描述符添加到集合中 while(1){ tm.tv_sec=0; tm.tv_usec=1000; ready_set=read_set;//每次循环重新得到集合,因为ready_set每次都变 select_ret=select(1024,&ready_set,NULL,NULL,&tm);//集合中有几个描述符 if(select_ret==0){ continue; } //判断客户端上线 else if(select_ret>0){ if(FD_ISSET(fd_server,&ready_set)){//fd_server描述符是否有请求,这个函数如果发现某描述符没法请求,就会将该描述符删掉,所以这里一定要用ready_set这个临时的集合 char buf[32]; memset(buf,0,32); if(read(fd_server,buf,32)==0){//从fd_server描述符读取客户端上线信息==0说明没有上线 continue; } else{ printf("a client on!\n");//上线了 char pipe_name[32]; memset(pipe_name,0,32); buf[strlen(buf)-1]='\0';//客户端发送的是pid,通过这个pid客户端建立两个管道,r.getpid(),w.getpid() sprintf(pipe_name,"r.%s",buf);//client read int wfd,rfd; wfd = open(pipe_name,O_WRONLY);//客户端读,那么server就是写了,得到写描述符wfd memset(pipe_name,0,32); sprintf(pipe_name,"w.%s",buf);//client write rfd=open(pipe_name,O_RDONLY);//得到读描述符,每个客户端有两个管道 fds_insert(&my_list,rfd,wfd);//将这对描述符添加到描述符队列里面 FD_SET(rfd,&read_set);//将读描述符添加到集合 } } //遍历read_set集合,看那个读描述符给server发信息了,发信息就就开始执行任务 pFD_PAIR pCur = my_list; while(pCur){ printf("start to find!\n"); if(FD_ISSET(pCur->s_rfd,&read_set))//client request判断客户在不在set集合, { char buf[1024]; memset(buf,0,sizeof(buf)); if(read(pCur->s_rfd,buf,1024)==0)//判断客户有没有请求,==0时候就是不请求了 { FD_CLR(pCur->s_rfd,&read_set);//相当于客户ctrl+c了,退出了,所以就把该描述符从集合中删掉,这个集合是read_set备份集合 int fd_r=pCur->s_rfd; pCur = pCur->s_next; fds_link_delete(&my_list,fd_r);//吧描述符链表对应的位置也删掉 } else{ TASK tk; memset(&tk,0,sizeof(tk)); tk.s_fd=pCur->s_wfd; strcpy(tk.s_msg,buf);//如果没有退出,就将这个任务添加到任务队列,交给线程执行 add_task(&my_que,&tk); pCur = pCur->s_next; } } else{ pCur = pCur ->s_next; } } } } pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond_master); pthread_cond_destroy(&cond_slave); return 0; }bin 文件中的 client.c
/************************************************************************* > File Name: client.c > Author: yang > Mail:[email protected] > Created Time: 2014年08月27日 星期三 11:27:19 ************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> int main(int argc,char *argv[]){ int fd_server,fd_send,fd_recv; char rname[32],wname[32]; fd_server = open(argv[1],O_WRONLY);//这是描述符,之前建立好的管道,通过argv传过来,client想server发送登陆信息 memset(rname,0,32); memset(wname,0,32); sprintf(rname,"r.%d",getpid()); sprintf(wname,"w.%d",getpid()); //这两个管道用来client与server通信的 mkfifo(rname,0666);//生成两个管道,一个用来client读,server写 mkfifo(wname,0666);//用来client写,server读 char msg[1024]; sprintf(msg,"%d\n",getpid()); write(fd_server,msg,strlen(msg));//发送的登陆信息为进程的pid,方便server那面生成管道名(r.getpid(),w.getpid())来读写管道 fd_recv = open(rname,O_RDONLY);//得到两个管道的描述符 fd_send = open(wname,O_WRONLY); while(memset(msg,0,1024),fgets(msg,1024,stdin)!=NULL){//想管道(server)写信息 write(fd_send,msg,strlen(msg)); memset(msg,0,1024); read(fd_recv,msg,1024);//从管道(server)接信息 write(1,msg,strlen(msg));//打印到屏幕 } close(fd_server); close(fd_send); close(fd_recv); return 0; }