首页 > 学院 > 开发设计 > 正文

用fifo管道实现多人聊天

2019-11-08 02:51:20
字体:
来源:转载
供稿:网友

用fifo管道实现多人聊天

最近学习了多路IO,多线程,信号注册函数,管道通信等内容。于是通过以上技术,写了一个通过管道通信的多人聊天小程序。程序还存在很多不足,但是勉强能实现需要的功能。先贴上,日后完善。 先介绍程序大致功能,再贴上代码: 1.启动服务器程序,用epoll进行监听客户端事件发生,同时创建一个文件写入服务器端的pid,用于客户端信号发送到服务器. 2.再启动客户端程序,创建读写管道。同时发送信号到服务器,以便让服务器连接管道。 3.连接成功后进行通信 setname xxx 用于创建用户名 to xxx:buf 给指定xxx用户发送buf消息 to all:buf 给所有用户发送buf消息

服务端程序:

#include<stdio.h>#include<errno.h>#include<signal.h>#include<sys/epoll.h>#include<fcntl.h>#include<sys/types.h>#include<sys/stat.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#include<string>#include<map>#include<iostream>using namespace std;typedef struct chat_user_t{//客户信息结构体 string name; int fd_read; int fd_write;}chat_user_t;map<string,chat_user_t*> users;//保存客户信息int epollfd;const char* errmsg[] = {//出错信息 "ok", "user not exit", "unknown command"};//创建文件存储服务器进程idvoid create_pid_file(char* filename){ char buf[1024]; sPRintf(buf,"%s.run",filename); int fd=open(buf,O_CREAT|O_RDWR,0777); if(fd<0){ perror("create_pid open"); return; } sprintf(buf,"%d",(int)getpid()); write(fd,buf,strlen(buf)); close(fd);}//设置文件非阻塞void setNonblock(int fd){ int flags=fcntl(fd,F_GETFL); flags|=O_NONBLOCK; fcntl(fd,F_SETFL,flags);}//信号处理函数void signal_handle(int signum,siginfo_t* info,void *p){ char filepath1[64]; char filepath2[64]; sprintf(filepath1,"%d-1",(int)info->si_pid); sprintf(filepath2,"%d-2",(int)info->si_pid); int fd_read=open(filepath1,O_RDONLY);//打开管道1读客户端 if(fd_read<0){ return; } int fd_write=open(filepath2,O_WRONLY);//打开管道2写客户端 if(fd_write<0){ close(fd_read); return; } setNonblock(fd_read);//设置读管道非阻塞 chat_user_t *user=new chat_user_t; user->fd_read=fd_read; user->fd_write=fd_write; struct epoll_event ev; ev.events=EPOLLIN; ev.data.ptr=user; epoll_ctl(epollfd,EPOLL_CTL_ADD,fd_read,&ev);//将新加入的客户端放入监听 printf("some is coming/n");}//注册信号处理函数void register_signal(){ struct sigaction act; act.sa_handler=NULL; act.sa_sigaction=signal_handle; sigemptyset(&act.sa_mask); act.sa_flags=SA_SIGINFO; act.sa_restorer=NULL; sigaction(SIGRTMIN,&act,NULL); printf("register_signale/n");}////setname xxx//to xxx://to all:void handle_cmd(chat_user_t *user,char *cmd){ if(strncmp(cmd,"setname",7)==0){ strtok(cmd," "); char *username=strtok(NULL," "); user->name=username; users[username]=user; printf("%s setname/n",username); } //to xxx:aaaaa||to all:aaaaa else if(strncmp(cmd,"to",2)==0){ char* head=strtok(cmd,":");//to xxx char* content=strtok(NULL,"/0");//aaaaa strtok(head," "); char* tousername=strtok(NULL," ");//xxx //to all:aaaa; if(strcmp(tousername,"all")==0){//给所有人发消息 char buf[1024]; sprintf(buf,"%s:%s",tousername,content); map<string,chat_user_t*>::iterator it=users.begin(); for(;it!=users.end();it++){ if(it->first==user->name){ continue; } write(it->second->fd_write,buf,strlen(buf)+1); } return; } map<string,chat_user_t*>::iterator it=users.find(tousername); if(it==users.end()){//not user //printf("==========/n"); write(user->fd_write,errmsg[1],strlen(errmsg[1])+1); }else{//给特定的人发消息 //yyy:buf char buf[1024]; sprintf(buf,"%s:%s",tousername,content); write(users[tousername]->fd_write,buf,strlen(buf)+1); } }else{//wrong msg printf("=============cmd:%s/n",cmd); write(user->fd_write,errmsg[2],strlen(errmsg[2])+1); }}int main(int argc,char *argv[]){ //创建文件,服务器进程id create_pid_file(argv[0]); //注册客户端信号 register_signal(); struct epoll_event ev[10]; epollfd=epoll_create(1024); while(1){ int nready=epoll_wait(epollfd,ev,10,2000); if(nready>0){//有人发消息 for(int i=0;i<nready;i++){ char buf[1024]; chat_user_t *tempuser=(chat_user_t*)ev[i].data.ptr; int n=read(tempuser->fd_read,buf,sizeof(buf)); if(n<=0){//读取有异常 if(n==-1&&errno==EINTR){ continue; } printf("%s is exit/n",tempuser->name.c_str()); users.erase(tempuser->name); close(tempuser->fd_read); close(tempuser->fd_write); delete(tempuser); } else{ //处理格式 //setname xxx //to yy:hello //to all:hello all handle_cmd(tempuser,buf); } } } else if(nready<0){//监听发生异常 if(errno==EINTR){//被信号打断继续监听 continue; } break;//其他异常 } } return 0;}

客户端程序

#include <stdio.h>#include <signal.h>#include <pthread.h>#include <fcntl.h>#include <sys/stat.h>#include <sys/types.h>#include <errno.h>#include <unistd.h>#include <string.h>#include <unistd.h>#include <stdlib.h>pid_t get_server_pid(){ int fd = open("myserver.run", O_RDWR); char buf[1024]; read(fd, buf, sizeof(buf)); close(fd); return (pid_t)atoi(buf);}// 等待服务器发送数据void* thread_recv(void* ptr){ int fd_read = (int)(intptr_t)ptr; char buf[4096]; while(1) { int ret = read(fd_read, buf, sizeof(buf)); if(ret == 0) // 写端已经被关闭了 { exit(0); // 整个进程退出 } if(ret < 0) { if(errno == EINTR) // 读文件失败 continue; exit(0); // 读文件错误 } printf("%s/n", buf); // 要求buf不带 }}int main(){ // 创建两个管道文件 pid_t pid = getpid(); char buf1[4096]; sprintf(buf1, "%d-1", (int)pid); mkfifo(buf1, 0777); char buf2[4096]; sprintf(buf2, "%d-2", (int)pid); mkfifo(buf2, 0777); // 发送信号给服务器,我来了 pid = get_server_pid();// printf("%d/n",(int)pid); // 发送信号告诉服务器,新的客户端加入 union sigval v; sigqueue(pid, SIGRTMIN, v); // 打开管道文件,一定在发送信号之后 // 让客户端和服务器一起打开管道,否则会阻塞 int fd_write = open(buf1, O_WRONLY); int fd_read = open(buf2, O_RDONLY); // 创建一个线程,负责信息的接收 pthread_t tid; pthread_create(&tid, NULL, thread_recv, (void*)(intptr_t)fd_read); // 等待用户输入 while(1) { char buf[4096]; fgets(buf, sizeof(buf), stdin); buf[strlen(buf)-1] = 0; if(strlen(buf) == 0) // 空敲回车的处理 continue; // setname xue // to yy: hello yy write(fd_write, buf, strlen(buf)+1); // 带上/0 }}
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表