#include"string.h"
#include"fcntl.h"
#include"sys/wait.h"
#include"stdio.h"
#include"stdlib.h"
#include"sys/types.h"
#include"unistd.h"
#include"sys/stat.h"
#include"netinet/in.h"
#include"clientmsg.h"
#include"servermsg.h"
#include"semaphore.h"
struct QUEUE{ int qid; int stat; };
void mkfifo_fun(char server_fifo[], char client_fifo[])
{
unlink(server_fifo);
unlink(client_fifo);
mkfifo(server_fifo, 0666);
mkfifo(client_fifo, 0666);
};
void write_to_client_fifo_fun(int fifo_write, struct CLIENTMSG C_recvmsg, struct sockaddr_in client,int qid)
{
struct SERVERMSG S_sendmsg;
S_sendmsg.OP = C_recvmsg.OP;
strcpy(S_sendmsg.username, C_recvmsg.username);
strcpy(S_sendmsg.buf, C_recvmsg.buf);
S_sendmsg.client = client;
S_sendmsg.qid = qid;
write(fifo_write, &S_sendmsg, sizeof(S_sendmsg));
}
int main()
{
struct QUEUE queue[5];
int serverfd, clientfd;
struct sockaddr_in server, client;
mkfifo_fun("SERVER", "CLIENT");
int server_fifo_read = open("SERVER", O_RDWR);
int server_fifo_write = open("SERVER", O_RDWR);
int client_fifo_read = open("CLIENT", O_RDWR);
int client_fifo_write = open("CLIENT", O_RDWR);
bzero(&server, sizeof(&server));
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(1234);
server.sin_family = AF_INET;
socklen_t len = sizeof(server);
serverfd = socket(AF_INET, SOCK_STREAM, 0);
bind(serverfd, (struct sockaddr *)&server, len);
listen(serverfd, 6);
//create semid
key_t key;
key = ftok(".", 'a');
int semid = CreateSem(key, 5);
//create fork()
int pid = fork();
if (pid < 0)
{
perror("main fork error");
exit(1);
}
else if (pid == 0) //转发子进程 从命名管道CLIENT中读取通信子进程发来的消息,消息类型为:用户名、退出及一般信息;
{
printf("####create 5 msg quenence\n");
struct SERVERMSG S_recvmsg;
int i = 0;
printf("####"); fflush(NULL);
for (i; i < 5; i++)
{
key_t key = ftok(".",i);
if (-1 == (queue[i].qid = msgget(key, IPC_CREAT|IPC_EXCL | 0666)))
{
msgctl(msgget(key,IPC_CREAT|0666),IPC_RMID,NULL);
queue[i].qid = msgget(key, IPC_CREAT|IPC_EXCL | 0666);
}
queue[i].stat = 0;
printf(" %d", queue[i].qid); fflush(NULL);
}
printf("\n");
write(server_fifo_write, queue, sizeof(queue));
while (1)
{
if (-1 == read(client_fifo_read, &S_recvmsg, sizeof(S_recvmsg)))
{
perror("read client error\n"); exit(1);
}
struct MESSAGE message;
int tmp;
switch (S_recvmsg.OP)
{
case USER: //若为用户名,依据消息队列在更新客户信息表,状态为可用;
message.msgtype = S_recvmsg.qid;
message.msg = S_recvmsg;
//printf("#####user is coming:%s, clietn left %d \n", message.msg.username, GetvalueSem(semid));
//依据消息队列在更新客户信息表,状态为可用
for (tmp = 0; tmp < 5; tmp++)
{
if (queue[tmp].qid == message.msg.qid)
{
queue[tmp].stat = 1;
}
else
if (queue[tmp].stat == 1)
{
msgsnd(queue[tmp].qid, &message, sizeof(message), 0);
}
}
write(server_fifo_write, queue, sizeof(queue));
break;
case MSG: //若为一般信息,将信息转换后写入可用客户的消息队列,等待其他通信子进程读取;
message.msgtype = S_recvmsg.qid;
message.msg = S_recvmsg;
for (tmp = 0; tmp < 5; tmp++)
{
if (queue[tmp].qid != message.msg.qid&&queue[tmp].stat == 1)
{
if (-1 == (msgsnd(queue[tmp].qid, &message, sizeof(message), 0)))
{
perror("error msgsnd");
exit(1);
}
}
}
break;
case EXIT: //若为退出,在客户信息表中状态设为不可用,执行信号量V操作,并将可用客户的消息队列标识符写入到命名管道SERVER;
message.msgtype = S_recvmsg.qid;
message.msg = S_recvmsg;
for (tmp = 0; tmp < 5; tmp++)
{
if (queue[tmp].stat == 1)
{
if (-1 == (msgsnd(queue[tmp].qid, &message, sizeof(message), 0)))
{
perror("error msgsnd");
exit(1);
}
}
if (queue[tmp].qid == message.msg.qid)
{
queue[tmp].stat = 0;
}
}
struct QUEUE clear[5];
read(server_fifo_read, &clear, sizeof(clear));
write(server_fifo_write, queue, sizeof(queue));
break;
default:
break;
}
S_recvmsg.OP = 100;
}
}
else //主进程
{
while (1)
{
clientfd = accept(serverfd, (struct sockaddr*)&client, &len);
struct CLIENTMSG C_recvmsg, C_sendmsg;
if (0 == Sem_V(semid))
{
printf("#### new client PORT: %d IP:%s \n", ntohs(client.sin_port), inet_ntoa(client.sin_addr.s_addr));
C_sendmsg.OP = OK;
strcpy(C_sendmsg.username, "OK");
strcpy(C_sendmsg.buf, "OK");
write(clientfd, &C_sendmsg, sizeof(C_sendmsg));
int id = fork(); //创建 主进程/通信子进程
if (id < 0)
{
perror("id error");
exit(1);
}
else
if (id == 0) //主进程/通信子进程
{
int qid;
struct QUEUE recvqueue[5];
read(server_fifo_read, &recvqueue, sizeof(recvqueue));
int tep = 0;
for (tep; tep < 5; tep++)
{
if (recvqueue[tep].stat == 0){
qid = recvqueue[tep].qid;
break;
}
}
int recvid = fork(); //创建 主进程/通信子进程/接收进程
if (recvid < 0)
{
perror("recvid error"); exit(1);
}
else if (recvid == 0) //主进程/通信子进程/子进程 -创建一个子进程负责从消息队列中读取消息,发送给客户
{
while (1)
{
struct SERVERMSG S_msg;
struct MESSAGE recvmsg;
if (-1 == (msgrcv(qid, &recvmsg, sizeof(recvmsg), 0, 0)))
{
perror("msgrcv error");
exit(1);
}
S_msg = recvmsg.msg;
strcpy(C_sendmsg.username, S_msg.username);
strcpy(C_sendmsg.buf, S_msg.buf);
if (S_msg.OP == EXIT &&S_msg.qid == qid)
{
Sem_P(semid);
printf("#### this name:%s pid :%d exit client left :%d !!\n", S_msg.username, qid, GetvalueSem(semid));
C_sendmsg.OP = S_msg.OP;
write(clientfd, &C_sendmsg, sizeof(C_sendmsg));
break;
}
else if (S_msg.OP == USER)
{
C_sendmsg.OP = S_msg.OP;
}
else if (S_msg.OP == MSG || S_msg.OP == EXIT)
{
C_sendmsg.OP = MSG;
}
write(clientfd, &C_sendmsg, sizeof(C_sendmsg));
}
}
else //主进程/通信子进程// 接收客户端数据 -通过CLIENT发送给转发子进程
{
while (1)
{
if (-1 == read(clientfd, &C_recvmsg, sizeof(C_recvmsg))) //若信息为退出,终止子进程,程序结束
{
perror("error read");
exit(1);
}
if (C_recvmsg.OP == -100)
{
C_recvmsg.OP = EXIT;
printf("####CLIENT BREAKDOWN WHEN COMMUNICATION -->%d !!!!\n", C_recvmsg.OP);
write_to_client_fifo_fun(client_fifo_write, C_recvmsg, client, qid);
}
if (C_recvmsg.OP == EXIT)
{
write_to_client_fifo_fun(client_fifo_write, C_recvmsg, client, qid);
break;
}
else
if (C_recvmsg.OP == USER) //若信息为用户名,附带消息队列、客户地址发送给转发子进程;
{
printf("#####user is coming:%s, clietn left %d \n", C_recvmsg.username, GetvalueSem(semid));
write_to_client_fifo_fun(client_fifo_write, C_recvmsg, client, qid);
}
else
if (C_recvmsg.OP == M