用fifo管道实现多人聊天

用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"
};
//创建文件存储服务器进程id
void 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
    }
}




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值