目的
通过命名管道实现简易聊天室功能,能通过一个服务器程序,可以实现多个客户端程序之间的通信。
思路
创建两个结构体成员message、list,分别表示消息包和已登录者信息
struct message{
unsigned int pid; //发送者的PID
char type; //消息类型
char send[8]; //发送者用户名
char receive[8]; //接收者用户名
char data[30]; //消息内容
};
message.type为‘0’表示服务器消息、‘1’表示用户消息、‘2’表示客户端退出登录
struct list{
int pid;
int fd;
char name[8];
struct list *next;
};
登录者信息采用链表存储,方便查看接收者是否在聊天室中,也方便频繁的添加和删除用户信息。
服务器端:
- 创建并打开公共FIFO
- 初始化链表list
- 无阻塞读取公共FIFO中的数据
- 判断读取到的message结构体,做出相应动作
- 无限循环接收消息
客户端:
- 创建并打开一个以用户名命名的专用FIFO
- 将标准输入设置为无阻塞
- 向公共FIFO发送一个登录消息,使得在服务器l的list链表上添加该用户的详细信息
- 无限循环处理消息
- 无阻塞读取标准输入
- 将输入的消息打包成message结构体发送给服务器
- 无阻塞读取并处理私有FIFO的数据
源代码
server.c:
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/mman.h>
#define TRUE 1
#define FALSE 0
typedef char BOOL;
void print_line(void){
printf("#####################\n");
}
struct message{
unsigned int pid;
char type;
char send[8];
char receive[8];
char data[30];
};
struct list{
int pid;
int fd;
char name[8];
struct list *next;
};
void del_node(struct list* root,int id){
struct list *p=malloc(sizeof(struct list));
struct list *q=p;
p->pid=id+10;
p->next=root;
while(p->next){
if(p->next->pid==id){
p=p->next->next;
break;
}else{
p=p->next;
}
}
root=q->next;
}
void add_node(struct list* root,int id,char* string){
struct list *p=root;
struct list *q=malloc(sizeof(struct list));
q->pid=id;
strcpy(q->name,string);
q->next=NULL;
while(p->next)
p=p->next;
p->next=q;
}
BOOL check_list(struct list* root,char* string){
struct list *p=root;
while(p){
if(strcmp(p->name,string)==0){
return TRUE;
}
p=p->next;
}
return FALSE;
}
int main(void){
int fd_public,fd_private,ret;
//创建服务器管道并打开
fd_public=mkfifo("server_fifo",0644);
if(fd_public==-1){
perror("mkfifo(server_fifo)");
exit(1);
}
fd_public=open("server_fifo",O_RDWR);
if(fd_public==-1){
perror("open(server_fifo)");
exit(1);
}
struct message *tip=malloc(sizeof(struct message));
struct message *buff=malloc(sizeof(struct message));
struct list *root=malloc(sizeof(struct list));
//初始化登录信息链表
root->pid=getpid();
root->fd=0;
root->next=NULL;
strcpy(root->name,"SERVER");
tip->pid=getpid();
strcpy(tip->send,"SERVER");
const char tip1[30]={"Login successful\n"};
const char tip2[30]={"Friends not logged in\n"};
const char tip3[30]={"You have logged in\n"};
while(1){
memset(buff,0,sizeof(struct message));
print_line();
if((ret=read(fd_public,buff,sizeof(struct message)))==-1){
perror("read fifo error:");
exit(1);
}
if(ret<=0){
continue;
}
printf("pid:%d\ntype:%c\nsend:%s\nreceive:%s\ndata:%s\n",buff->pid,buff->type,buff->send,buff->receive,buff->data);
print_line();
if(buff->type=='0'){
tip->type='0';
strcpy(tip->receive,buff->send);
strcpy(tip->data,tip1);
printf("[SERVER]Login successfully\n");
add_node(root,buff->pid,buff->send);
//send message
if((fd_private=open(buff->send,O_RDWR))==-1){
perror("open(private_fifo)");
exit(1);
}
if((ret=write(fd_private,tip,sizeof(struct message)))==-1){
perror("write(private_fifo)");
exit(1);
}
}else if(buff->type=='1'){
//check receive status
if(check_list(root,buff->receive)){
printf("sending...\n");
strcpy(tip->data,buff->data);
strcpy(tip->send,buff->send);
tip->type='1';
//send message
if((fd_private=open(buff->receive,O_RDWR))==-1){
perror("open(private_fifo)");
exit(1);
}
if(write(fd_private,tip,sizeof(struct message))==-1){
perror("write(private_fifo)");
exit(1);
}
}else{
printf("receive don't login in\n");
strcpy(tip->data,tip2);
strcpy(tip->send,"SERVER");
tip->type='0';
//send message
if((fd_private=open(buff->send,O_RDWR))==-1){
perror("open(private_fifo)");
exit(1);
}
if(write(fd_private,tip,sizeof(struct message))==-1){
perror("write(private_fifo)");
exit(1);
}
}
}else if(buff->type=='2'){
printf("delete node...\n");
del_node(root,buff->pid);
}else{
//nothing
}
}
return 0;
}
client.c:
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/mman.h>
void print_line(void){
printf("##################\n");
}
struct message{
unsigned int pid;
char type;
char send[8];
char receive[8];
char data[30];
};
int main(int argc,char* argv[]){
if(argc<2 || strlen(argv[1])>8){
printf("Input error!\n");
exit(1);
}
int flags,fd_public,fd_private,ret,ret2;
char info[38];
struct message *buff=malloc(sizeof(struct message));
struct message *p=malloc(sizeof(struct message));
buff->pid=getpid();
buff->type='0';
strcpy(buff->send,argv[1]);
strcpy(buff->receive,"server");
strcpy(buff->data,"login");
if(mkfifo(buff->send,0644)==-1){
perror("mkfifo(private_fifo)");
exit(1);
}
fd_public=open("server_fifo",O_RDWR);
if(fd_public==-1){
perror("open(server_fifo)");
exit(1);
}
fd_private=open(buff->send,O_RDONLY|O_NONBLOCK);
if(fd_private==-1){
perror("open(private_fifo)");
exit(1);
}
if((flags = fcntl(STDIN_FILENO, F_GETFL)) < 0)
perror("fcntl");
flags |= O_NONBLOCK;
if((fcntl(STDIN_FILENO, F_SETFL, flags)) < 0)
perror("fcntl");
if(write(fd_public,buff,sizeof(struct message))==-1){
perror("write login(server_fifo)");
exit(1);
}
while(1){
memset(info,0,sizeof(info));
ret=read(STDIN_FILENO,&info,sizeof(info));
if(ret>0){
if(strcmp(info,"quit")==0){
printf("goodbye!\n");
buff->type='2';
//send my message
if(write(fd_public,buff,sizeof(struct message))==-1){
perror("write(server_fifo)");
exit(1);
}
exit(1);
}else{
int i=0;
while(i<8){
if(info[i]==':'){
break;
}
i++;
}
if(i==8){
printf("input receive name error!\n");
memset(info,0,sizeof(info));
continue;
}else{
memset(buff->receive,0,sizeof(buff->receive));
strncpy(buff->receive,info,i);
strcpy(buff->data,&info[i+1]);
//printf("buff->send:%s\nbuff->receive:%s\nbuff->data:%s\n",buff->send,buff->receive,buff->data);
}
buff->type='1';
buff->pid=getpid();
strcpy(buff->send,argv[1]);
}
//send my message
if(write(fd_public,buff,sizeof(struct message))==-1){
perror("write(server_fifo)");
exit(1);
}
}
//read!
if((ret2=read(fd_private,p,sizeof(struct message)))<0){
if(errno != EAGAIN)
perror("read(private_fifo)");
}else if(ret2>0){
if(p->type==48){
printf("[SERVER]:%s",p->data);
}else if(p->type==49){
printf("[%s]:%s",p->send,p->data);
}
}
//write(STDOUT_FILENO,p,sizeof(p));
}
return 0;
}
如有错误,请指正!