练习:在线英英词典

值得学习的内容:
1.网络通信TCP编程
2.多进程并发服务器
3.sqlite3轻量级数据库
4.IO多路复用编程,使用epoll族

client.c

#include "MYHEAD.h"
#define		BACKLOG		5		//允许同时请求的客户端个数
#define		PORT		5001	//端口号
#define     TRANS       4   //单词解释
#define     HIST        5   //历史记录
#define     LINE_INFO   printf("ERR:%s,%s,%d\n",__FILE__,__FUNCTION__,__LINE__)
#define     LOGIN       1   //登录
#define     REGISTER    2   //注册
#define     QUIT        3   //退出

#define		DB_PATH		"./mydictory.db"
#define		DICT_PATH	"./dict.txt"
typedef struct MSG{
    int type;           //消息类型
    char title[10];     //标题
    char data[200];     //内容
}MSG;


/* 单词解释 */
int do_translate(int fd, MSG *msg, sqlite3 *db);
/* 查询历史记录 */
int do_history(int fd, MSG *msg, sqlite3 *db);
/* 登录 */
int do_check_login(int fd,MSG *msg,sqlite3 *db);
/* 注册 */
int do_register(int fd,MSG *msg,sqlite3 *db);
/* 子进程 */
void child_process(int fd,struct sockaddr_in client,sqlite3 *db);
/* 响应函数 */
void handler(int signal);
/* 服务器函数 */
int main(char argc,char **argv)
{

	int fd,newfd;			//文件描述符
	pid_t pid;				//进程id
	char *errmsg;			//sql库函数存放错误信息
	sqlite3 *db;			//数据库
	char sql[250];			//sql语句
	/* 打开数据库 */
	if(sqlite3_open(DB_PATH,&db)!=SQLITE_OK){
		LINE_INFO;
	}
	/* 创建用户ID密码表 */
	sprintf(sql,"create table user(id int primary key,password text)");
	if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK){
		LINE_INFO;
		printf("%s\n",errmsg);
	}
	/* 创建用户查询记录表 */
	sprintf(sql,"create table record(id int, word text, datetime text)");
	if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK){
		LINE_INFO;
		printf("%s\n",errmsg);
	}
	/* 绑定:信号--响应函数。子进程退出时,内核会发送SIGCHLD通知父进程 */
	signal(SIGCHLD,handler);
	/* 创建socket */
	if((fd=socket(AF_INET,SOCK_STREAM,0))<0){
		perror("socket");
		exit(-1);
	}
	/* 允许绑定地址快速重用 */
	int b_reuse = 1;
	setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));
	
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port=htons(PORT);					//字节序转换

	addr.sin_addr.s_addr = htonl(INADDR_ANY);	//(INADDR_ANY)表示本机任意网卡地址
	/* 绑定服务器的ip,port给刚创建的socket */
	if(bind(fd,(struct sockaddr *)&addr,sizeof(addr)) < 0)
	{
		perror("bind");
		exit(-1);
	}
	/* 监听,socket变成被动的 */
	if(listen(fd,BACKLOG) < 0)		//BACKLOG=同时请求的客户端个数
	{
		perror("listen");
		exit(-1);
	}
	socklen_t client_len;			//容器
	struct sockaddr_in client;		//容器,存放客户端的地址信息
	printf("lstening...\n");

	while(1){
		/* 阻塞等待建立链接accept,fd属于监听类socket(被动),newfd属于连接类socket(主动) */
		if((newfd = accept(fd,(struct sockaddr *)&client,&client_len))<0)
		{
			if(errno != EINTR){				//accept属于阻塞函数,要考虑被中断信号打断的情况。
				perror("accept");
				exit(-1);
			}
		}
		printf("newfd:%d,fd:%d\n",newfd,fd);	//每个进程都有一个独立的表,因此每个进程打印出来的fd数值可以相同
		if((pid=fork())<0){
			perror("fork");
			exit(-1);
		}
		if(0 == pid){				//子进程
			close(fd);				//关闭监听socket,对子进程来说不需要监听
			child_process(newfd,client,db);
		}else{						//父进程,只负责accept
			close(newfd);			//关闭连接socket,对父进程来说不要连接
		}
	}
	close(fd);						//父进程关闭监听socket
	return 0;
}
/* 子进程函数 */
void child_process(int fd,struct sockaddr_in client,sqlite3 *db){
	MSG msg;
	int n;

	char ip[16];
	char port[6];
	while(1){
		if((n=read(fd,(void *)&msg,sizeof(MSG)))<0){			//阻塞等待
			if(n!=EINTR)					//阻塞可能会被中断信号打断,并返回-1
			{								//任何长等待机制都要考虑被中断的处理方式,用来区分异常错误
				perror("read");
				exit(-1);
			}
		}
		inet_ntop(AF_INET,&client.sin_addr.s_addr,ip,16);
		inet_ntop(AF_INET,&client.sin_port,port,6);

		if(0==n){
			printf("quit!\n%s:%s:\n",ip,port);
			close(fd);
			exit(0);
		}
		if(n>0){
			/* 收到客户端的用户登录信息 */
			switch(msg.type){
				case LOGIN:
					do_check_login(fd,&msg,db);
					break;
				case TRANS:
					do_translate(fd,&msg,db);
					break;
				case HIST:
					do_history(fd,&msg,db);
					break;
				case REGISTER:
					do_register(fd,&msg,db);
					break;
			}
		}
	}
}
/* 注册 */
int do_register(int fd,MSG *msg,sqlite3 *db){
	char sql[250];
	char *errmsg,**resultp;
	int nrow,ncolumn;
	sprintf(sql,"insert into user values(%s,'%s')",msg->title,msg->data);
	/* 插入记录 */
	if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK){
		LINE_INFO;
		printf("%s\n",errmsg);
		strcpy(msg->data,errmsg);
	}
	else strcpy(msg->data,"ok");
	if(send(fd,(void *)msg,sizeof(MSG),0)<0){
		LINE_INFO;
		perror("send");
		return -1;
	}
	return 0;
}
/* 登录 */
int do_check_login(int fd,MSG *msg,sqlite3 *db){
	char sql[250];
	char *errmsg,**resultp;
	int nrow,ncolumn;
	int ret;
	sprintf(sql,"select * from user where id=%s",msg->title);
	/* 先查询id号是否存在 */
	if(sqlite3_get_table(db,sql,&resultp,&nrow,&ncolumn,&errmsg)!=SQLITE_OK){
		LINE_INFO;
		printf("%s\n",errmsg);
		strcpy(msg->data,errmsg);
	}
	/* 表格内容实际以一维数组存放,包括column名,从左到右,从上到下 */
	/* nrow=行数,ncolumn=列数。如果ncolumn=0,说明数组内没有内容 */

	else if(0==ncolumn){
		/* id不存在 */
		ret =0 ;
		strcpy(msg->data,"id don't exist!\n");
	}
	else{
		/* 密码正确 */
		if(0==strcmp(msg->data,resultp[ncolumn*2-1]))
		{
			ret = 1;
			strcpy(msg->data,"ok");
		}
		/* 密码错误 */
		else{ 
			ret = 0;
			strcpy(msg->data,"password error!\n");
	
		}
	}
	/* 返回信息 */
	if(send(fd,(void *)msg,sizeof(MSG),0)<0){
		LINE_INFO;
		perror("send");
		return -1;
	}
	printf("send!\n");
	return ret;
	
}
/* 单词解释 */
int do_translate(int fd, MSG *msg, sqlite3 *db){
	char sql[250];			//存放sql语句
	char buf[512];			//存放从字典取出来的字符串
	char word[20];			//存放用户查询的单词
	char *errmsg;
	FILE *dict;
	int ret,length;				
	if((dict = fopen(DICT_PATH, "r+")) == NULL){
		LINE_INFO;
		perror("fopen");
		return -1;			//打开字典失败
	}
	printf("recv:%d:%s:%s\n",msg->type, msg->title, msg->data);
	strcpy(word,msg->data);
	length = strlen(word);					//单词长度
	/* 循环fgets,直到文件末尾 */
	while(!feof(dict)){	
		/* 字典在txt文件中以行为单位,并且按照字母a-z排序 */
		if(fgets(buf, 512, dict) == NULL){
			LINE_INFO;
			perror("fgets");
		}
		/* 用data的字串减去buf的字串 */
		ret = strncmp(msg->data, buf, length);		//匹配
		printf("ret=%d,length=%d,word=%s\n",ret,length,word);
		printf("%s\n",buf);
		if(ret > 0){
			/* 继续跟下一行比较,dog 与 a */
			continue;
		}
		else if(ret < 0){
			printf("/* 按照字母表顺序,往下已经没有能够匹配的了 */\n");
			strcpy(msg->data, "not found!");
			break;
		}
		else if(buf[length] != ' '){
			printf("/* 考虑 a与abc的情况。前面匹配,后面却不是空格 */\n");
			strcpy(msg->data, "not found!");
			break;
		}

		/* 最后剩下匹配的情况 */
		while(buf[length] == ' ')
			length++;				//跳过空格
		strcpy(word,msg->data);				
		strcpy(msg->data, &buf[length]);
		/* 记录保存到数据库内 */
		time_t ti = time(NULL);				
		struct tm* tm = localtime(&ti);
		sprintf(buf, "%d-%d-%d,%d:%d:%d", \
				tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, \
				tm->tm_hour, tm->tm_min, tm->tm_sec);
		sprintf(sql,"insert into record values(%s,'%s','%s')",msg->title,word,buf);
		if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK){
			LINE_INFO;
			printf("%s\n",errmsg);
		}
		break;
	}
	fclose(dict);
	if(send(fd,(void *)msg,sizeof(MSG),0)<0){
		LINE_INFO;
		perror("send");
		return -1;
	}	
	return 0;
}
/* 查询历史记录 */
int do_history(int fd, MSG *msg, sqlite3 *db){
	char sql[250];								//存放sql语句
	char *errmsg, **resultp;
	int nrow, ncolumn, r; 
	msg->data[0] = '\0';							//写入空字符,代表字串长度=0
	sprintf(sql,"select * from record where id=%s",msg->title);
	if(sqlite3_get_table(db,sql,&resultp,&nrow,&ncolumn,&errmsg) < 0){
		LINE_INFO;
		printf("%s",errmsg);
		return -1;
	}
	printf("recv:%d:%s:%s",msg->type, msg->title, msg->data);
	/* 如果查不到记录,nrow=0,column=0 */
	if(0 == nrow){
		strcpy(msg->data, "not found record!\n");
		if(send(fd,msg,sizeof(MSG),0)<0){
			LINE_INFO;
			perror("send");
			return -1;
		}
		/* 查不到记录 */
		return 1;
	}

	printf("/* 如果查到记录,nrow >= 2 column >= 1 */\n");
	/* 总共nrow行,第1行是column名,查询历史记录只打印最后2条即可 */
	if(nrow<3) r=1;				//打印1条记录
	else r=2;					//打印2条记录
	
	while(r--){
		/* 打印倒数1或2条记录 */
		sprintf(sql,"id=%s,word='%s',time:%s\n", \
				resultp[(nrow-r)*ncolumn],resultp[(nrow-r)*ncolumn+1],resultp[(nrow-r)*ncolumn+2]);
		strcat(msg->data,sql);
	}
	if(send(fd,msg,sizeof(MSG),0)<0){
		LINE_INFO;
		perror("send");
		return -1;
	}
	return 1;
}

	
/* 信号响应函数 */
void handler(int signal){						
	if(SIGCHLD == signal){
		waitpid(-1,NULL,WNOHANG);			//WNOHANG代表非阻塞。
	}
}

server.c

#include "MYHEAD.h"
//使用示例:./client 192.168.1.100 5001
#define		TRANS		4	//单词解释
#define		HIST		5	//历史记录
#define		LINE_INFO	printf("%s,%s,%d\n",__FILE__,__FUNCTION__,__LINE__)
#define		LOGIN		1	//登录
#define		REGISTER	2	//注册
#define		QUIT		3	//退出
typedef struct MSG{
	int type;			//消息类型
	char title[10];		//标题
	char data[255];		//内容
}MSG;
/* 单词解释 */
int do_translate(int sockfd, MSG *msg);
/* 历史记录 */
int do_history(int sockfd, MSG *msg);
/* 注册模块 */
int do_register(int sockfd, MSG *msg);
/* 打印主菜单 */
int mainmenu(void);
/* 打印用户菜单 */
int usermenu(void);
/* 登录 */
int do_login(int sockfd, MSG *msg);
/******************************************************************/
/* 客户端主程序 */
int main(char argc,char **argv){
	/* 判断输入参数个数 */	
	if(argc<3){
		printf("Usage:%s <ip> <port>\n",argv[0]);
		exit(-1);
	}
	MSG msg;
	int fd, cmd;
	/* 创建socket */
	if((fd=socket(AF_INET,SOCK_STREAM,0))<0){
		perror("socket");
		exit(-1);
	}
	uint32_t ip;	//容器
	/* 192.xxx.xxx.xxx字符串形式,转换,32位网络字节序 */
	if(inet_pton(AF_INET,argv[1],(void *)&ip) != 1){
		perror("inet_pton");
		exit(-1);
	}
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = ip;
	addr.sin_port = htons(atoi(argv[2]));	//网络字节序
	/* 链接connect */
	if(connect(fd,(struct sockaddr *)&addr,sizeof(addr))<0){
		perror("connect");
		exit(-1);
	}
before_login:
	while(1){
		mainmenu();						//打印菜单
		printf("input:");
		/* 阻塞等待输入 */
		cmd=0;
		scanf("%d",&cmd);
		getchar();						//过滤回车键
		switch(cmd){
			case LOGIN:					//登录
				if(do_login(fd, &msg)>0)
					/* 登录成功,跳转 */
					goto after_login;
				break;
			case REGISTER:				//注册
				do_register(fd, &msg);
				break;
			case QUIT:					
				/* 退出 */
				close(fd);		//关闭sockfd
				printf("quit sucessed!\n");
				exit(0);
				break;
			default:
				printf("undefined cmd!\n");
		}
	}
	/* 用户登录成功后 */
after_login:
	while(1){
		usermenu();
		printf("input:");
		cmd=0;
		scanf("%d",&cmd);
		getchar();
		switch(cmd){
			case TRANS:
				/* 单词解释 */
				do_translate(fd, &msg);
				break;
			case HIST:
				/* 历史记录 */
				do_history(fd, &msg);
				break;
			case QUIT:
				/* 返回上级菜单 */
				goto before_login;
				break;
			default:
				printf("undefined cmd!\n");
		}
	}


	return 0;
}
/* 打印主菜单 */
int mainmenu(void){
	printf("**************************************************\n");
	printf("\t\t主菜单\n");
	printf("1:登录\t2:注册\t3:退出\n");
	printf("**************************************************\n");
	return 0;
}

/* 打印用户菜单 */
int usermenu(void){
	printf("**************************************************\n");
	printf("\t\t用户菜单\n");
	printf("4:单词解释\t5:历史记录\t3:退出\n");
	printf("**************************************************\n");
	return 0;
}	
/* 登录模块 */
int do_login(int sockfd,MSG *msg){
	pid_t pid;
	char buf[100];
	msg->type=LOGIN;				//登录类型
	printf("input id:");
	scanf("%s",msg->title);
	getchar();
	printf("input password:");
	scanf("%s",msg->data);
	getchar();
	printf("wait for login...\n");

	/* IO多路复用,同时监听socket与stdin */
	int ret;
	int epfd=epoll_create(2);
	struct epoll_event event;		//注册结构体
	struct epoll_event ready[2];	//就绪结构体

	event.events=EPOLLIN;		//监听可读信号
	event.data.fd=sockfd;		//socket的fd
	/* 注册sockfd */
	if(epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event)<0){
		LINE_INFO;
		perror("epoll_ctl");
	}
	event.data.fd=0;
	/* 注册stdin */
	if(epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event)<0){
		LINE_INFO;
		perror("epoll_ctl");
	}
	/* 发送数据给服务器 */
	if(send(sockfd,(void *)msg,sizeof(MSG),0)<0){
		LINE_INFO;				//输出位置信息
		perror("send");			//输出errno信息
		close(epfd);
		return -1;
	}
	printf("/* 开始监听 */\n");
	int n;
	while((ret=epoll_wait(epfd,ready,2,-1))>0){
		/* ret存放的是fd个数,需要配合遍历匹配 */
		while(ret--){                        
			if((0 == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){
				printf("/* 标准输入fd有可读信号 */\n");
				if(read(0,buf,100)<0){
					LINE_INFO;
					perror("read");
				}
				/* 比较1个字符 */
				if(strncmp("#",buf,1)==0){
					close(epfd);
					return 0;			//退出login模块
				}
			}
			if((sockfd == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){
				printf("/* sockfd有可读信号 */\n");
				if((n=read(sockfd,(void *)msg,sizeof(MSG))>0)){
					printf("recv:%s\n",msg->data);
					if(0==strcmp("ok",msg->data)){
						/* 登录成功 */
						printf("login successed!\n");
						printf("recv->type:%d,title:%s,data:%s\n",\
								msg->type,msg->title,msg->data);
						close(epfd);
						return 1;		//退出login模块
					}
					close(epfd);
					return -1;		//退出login模块
				}
			}
		}
	}

	/* 登录失败 */
	LINE_INFO;
	perror("epoll_wait");
	close(epfd);
	return 0;
}
/* 注册模块 */
int do_register(int sockfd, MSG *msg){
	pid_t pid;
	char buf[100];
	msg->type=REGISTER;				//注册类型
	printf("input id:");
	scanf("%s",msg->title);
	getchar();
	printf("input password:");
	scanf("%s",msg->data);
	getchar();
	printf("wait for register...\n");

	/* IO多路复用,同时监听socket与stdin */
	int ret;
	int epfd=epoll_create(2);
	struct epoll_event event;		//注册结构体
	struct epoll_event ready[2];	//就绪结构体

	event.events=EPOLLIN;		//监听可读信号
	event.data.fd=sockfd;		//socket的fd
	/* 注册sockfd */
	if(epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event)<0){
		LINE_INFO;
		perror("epoll_ctl");
	}
	event.data.fd=0;
	/* 注册stdin */
	if(epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event)<0){
		LINE_INFO;
		perror("epoll_ctl");
	}
	/* 发送数据给服务器 */
	if(send(sockfd,(void *)msg,sizeof(MSG),0)<0){
		LINE_INFO;				//输出位置信息
		perror("send");			//输出errno信息
		close(epfd);
		return -1;
	}
	printf("/* 开始监听 */\n");
	int n;
	while((ret=epoll_wait(epfd,ready,2,-1))>0){
		/* ret存放的是fd个数,需要配合遍历匹配 */
		while(ret--){                        
			if((0 == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){
				printf("/* 标准输入fd有可读信号 */\n");

				if((n=read(0,buf,100))<=2){
					/* 防止用户可能不止输入了1个字符 */
					/* buf[0]='#' buf[1]=回车 */
					if('#'==buf[0]){
						/* login过程中,用户想要返回主菜单 */
						close(epfd);	//关闭io句柄
						return 0;		//中止

					}
					printf("undefined cmd!\n");		//输如了#以外的命令
				}
			}
			if((sockfd == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){
				printf("/* sockfd有可读信号 */\n");
				if((n=read(sockfd,(void *)msg,sizeof(MSG))>0)){
					printf("recv:%s\n",msg->data);
					if(0==strcmp("ok",msg->data)){
						/* 注册成功 */
						printf("register successed!\n");
						close(epfd);
						return 1;
					}
					close(epfd);
					return -1;
				}
			}
		}
	}

	/* 注册失败 */
	LINE_INFO;
	perror("epoll_wait");
	return 0;
}
/* 单词解释*/
int do_translate(int sockfd, MSG *msg){

	char buf[100];
	int ret;						//存放返回值
	int epfd=epoll_create(2);		//监听集合
	struct epoll_event event;		//注册结构体
	struct epoll_event ready[2];	//就绪结构体
	
	/* 读取用户输入word */
	msg->type = TRANS;				//单词解释
	printf("input word:");
	scanf("%s",msg->data);
	getchar();
	
	/* IO多路复用,同时监听socket与stdin */
	event.events=EPOLLIN;			//监听可读信号
	event.data.fd=sockfd;			//socket
	/* 注册sockfd */
	if(epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event)<0){
		LINE_INFO;
		perror("epoll_ctl");
	}
	event.data.fd=0;
	/* 注册stdin */
	if(epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event)<0){
		LINE_INFO;
		perror("epoll_ctl");
	}
	/* 发送数据给服务器 */
	if(send(sockfd,(void *)msg,sizeof(MSG),0)<0){
		LINE_INFO;				//输出位置信息
		perror("send");			//输出errno信息
		close(epfd);
		return -1;
	}
	printf("send->type:%d,title:%s,data:%s\n",msg->type,msg->title,msg->data);
	printf("/* 开始监听 */\n");
	while((ret=epoll_wait(epfd,ready,2,-1))>0){
		/* ret存放的是fd个数,需要配合遍历匹配 */
		while(ret--){                        
			if((0 == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){
				printf("/* 标准输入fd有可读信号 */\n");
				if(read(0, buf, 100)<0){
					LINE_INFO;
					perror("read");
				}
				/* 识别用户的输入 */
				if(0 == strncmp("#",buf,1)){
					close(epfd);	//关闭监听集合
					return 0;		//退出单词解释模块
				}
			}
			if((sockfd == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){
				printf("/* sockfd有可读信号 */\n");
				if(read(sockfd,(void *)msg,sizeof(MSG))>0){
					printf("%s\n",msg->data);
					close(epfd);
					return 1;			//收到单词解释,退出
				}

			}
		}
	}


	close(epfd);
	return 0;
}

/* 历史记录 */
int do_history(int sockfd, MSG *msg){

	char buf[100];
	int ret;						//存放返回值
	int epfd;		//监听集合
	struct epoll_event event;		//注册结构体
	struct epoll_event ready[2];	//就绪结构体
	
	msg->type = HIST;				//查询历史记录

	if((epfd = epoll_create(2))<0){
		LINE_INFO;
		perror("epoll_create\n");
	}
	/* IO多路复用,同时监听socket与stdin */
	event.events=EPOLLIN;			//监听可读信号
	event.data.fd=sockfd;			//socket
	/* 注册sockfd */
	if(epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&event)<0){
		LINE_INFO;
		perror("epoll_ctl");
	}
	event.data.fd=0;
	/* 注册stdin */
	if(epoll_ctl(epfd,EPOLL_CTL_ADD,0,&event)<0){
		LINE_INFO;
		perror("epoll_ctl");
	}
	/* 发送数据给服务器 */
	if(send(sockfd,(void *)msg,sizeof(MSG),0)<0){
		LINE_INFO;				//输出位置信息
		perror("send");			//输出errno信息
		close(epfd);
		return -1;
	}
	printf("/* 开始监听 */\n");
	while((ret=epoll_wait(epfd,ready,2,-1))>0){
		/* ret存放的是fd个数,需要配合遍历匹配 */
		while(ret--){                        
			if((0 == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){
				printf("/* 标准输入fd有可读信号 */\n");
				if(read(0, buf, 100)<0){
					LINE_INFO;
					perror("read");
				}
				/* 识别用户的输入 */
				if(0 == strncmp("#",buf,1)){
					close(epfd);	//关闭监听集合
					return 0;		//退出模块
				}
			}
			if((sockfd == ready[ret].data.fd) && (EPOLLIN & ready[ret].events)){
				printf("/* sockfd有可读信号 */\n");
				if(read(sockfd,(void *)msg,sizeof(MSG))>0){
					printf("%s\n",msg->data);
					close(epfd);
					return 1;			//收到回复,退出
				}

			}
		}
	}


	close(epfd);
	return 0;
}





发布了15 篇原创文章 · 获赞 0 · 访问量 387

猜你喜欢

转载自blog.csdn.net/mynameisJW/article/details/104598862