搭建一个服务器框架,进程间利用管道通信,线程处理数据

建立三个文件 分别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) -lpthread
include文件中的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);
#endif
src文件中的 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;
}




























发布了214 篇原创文章 · 获赞 78 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/youngyangyang04/article/details/38884617
今日推荐