要实现多客户端通信,必须要理清代码的框架
图可能有些潦草,但仔细研究两天就会懂了,除了这以外,还有定义一下客户端与客户端,客户端与服务器之间的通信协议,说白了就是定义一些结构体内容若如下
clientmsg.h
//CLIENTMSGbetween server and client
#ifndef_clientmsg
#define_clientmsg
//USER MSG EXITfor OP of CLIENTMSG
#define EXIT -1
#define USER 1
#define MSG 2
#define OK 3
#ifndef CMSGLEN
#define CMSGLEN100
#endif
structCLIENTMSG{
int OP;
char username[20];
char buf[CMSGLEN];
};
#endif
Servermsg.h
//SERVERMSG for communicateto translate
//MESSAGE fortranslate to communicate
#include<netinet/in.h>
#include"clientmsg.h"
#ifndef_servermsg
#define_servermsg
#ifndef CMSGLEN
#define CMSGLEN100
#endif
structSERVERMSG{
int OP;
char username[20];
char buf[CMSGLEN];
struct sockaddr_in client;
int qid;
};
struct MESSAGE{
long msgtype;
struct SERVERMSG msg;
};
#endif
写成头文件的形式,客户端,服务区的可以引用,可以避免重复打代码
服务器部分
Server.c
#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[], charclient_fifo[])
{
unlink(server_fifo);
unlink(client_fifo);
mkfifo(server_fifo,0666);
mkfifo(client_fifo,0666);
};
void write_to_client_fifo_fun(intfifo_write, struct CLIENTMSG C_recvmsg, struct sockaddr_in client,int qid)
{
structSERVERMSG 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()
{
structQUEUE queue[5];
intserverfd, clientfd;
structsockaddr_in server, client;
mkfifo_fun("SERVER","CLIENT");
intserver_fifo_read = open("SERVER", O_RDWR);
intserver_fifo_write = open("SERVER", O_RDWR);
intclient_fifo_read = open("CLIENT", O_RDWR);
intclient_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_tlen = sizeof(server);
serverfd= socket(AF_INET, SOCK_STREAM, 0);
bind(serverfd,(struct sockaddr *)&server, len);
listen(serverfd,6);
//createsemid
key_tkey;
key= ftok(".", 'a');
intsemid = CreateSem(key, 5);
//createfork()
intpid = fork();
if(pid < 0)
{
perror("mainfork error");
exit(1);
}
elseif (pid == 0) //转发子进程 从命名管道CLIENT中读取通信子进程发来的消息,消息类型为:用户名、退出及一般信息;
{
printf("####create5 msg quenence\n");
structSERVERMSG S_recvmsg;
inti = 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("readclient error\n"); exit(1);
}
structMESSAGE message;
inttmp;
switch(S_recvmsg.OP)
{
caseUSER: //若为用户名,依据消息队列在更新客户信息表,状态为可用;
message.msgtype= S_recvmsg.qid;
message.msg= S_recvmsg;
//printf("#####useris 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;
caseMSG: //若为一般信息,将信息转换后写入可用客户的消息队列,等待其他通信子进程读取;
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("errormsgsnd");
exit(1);
}
}
}
break;
caseEXIT: //若为退出,在客户信息表中状态设为不可用,执行信号量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("errormsgsnd");
exit(1);
}
}
if(queue[tmp].qid == message.msg.qid)
{
queue[tmp].stat= 0;
}
}
structQUEUE 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);
structCLIENTMSG 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));
intid = fork(); //创建 主进程/通信子进程
if(id < 0)
{
perror("iderror");
exit(1);
}
else
if(id == 0) //主进程/通信子进程
{
intqid;
structQUEUE recvqueue[5];
read(server_fifo_read,&recvqueue, sizeof(recvqueue));
inttep = 0;
for(tep; tep < 5; tep++)
{
if(recvqueue[tep].stat == 0){
qid= recvqueue[tep].qid;
break;
}
}
int recvid =fork(); //创建 主进程/通信子进程/接收进程
if(recvid < 0)
{
perror("recviderror"); exit(1);
}
elseif (recvid == 0) //主进程/通信子进程/子进程 -创建一个子进程负责从消息队列中读取消息,发送给客户
{
while(1)
{
structSERVERMSG S_msg;
structMESSAGE recvmsg;
if(-1 == (msgrcv(qid, &recvmsg, sizeof(recvmsg), 0, 0)))
{
perror("msgrcverror");
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;
}
elseif (S_msg.OP == USER)
{
C_sendmsg.OP= S_msg.OP;
}
elseif (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("errorread");
exit(1);
}
if(C_recvmsg.OP == -100)
{
C_recvmsg.OP= EXIT;
printf("####CLIENTBREAKDOWN 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("#####useris 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 == MSG) //若信息为用户名,附带消息队列、客户地址发送给转发子进程;
{
write_to_client_fifo_fun(client_fifo_write,C_recvmsg, client, qid);
}
C_recvmsg.OP= -100;
}
waitpid(recvid,NULL, WNOHANG);
close(clientfd);
}
}
else //主进程
{
close(clientfd);
waitpid(id,NULL, WNOHANG);
}
}
else //超过5个
{
C_sendmsg.OP= EXIT;
strcpy(C_sendmsg.username,"server");
strcpy(C_sendmsg.buf,"over client");
write(clientfd,&C_sendmsg, sizeof(C_sendmsg));
close(clientfd);
}
}
}
return0;
}
客户端部分
#include"string.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"sys/wait.h"
/*#define EXIT -1 #define USER 1 #define MSG 2
#define OK 3 #define CMSGLEN 100
struct CLIENTMSG{ int OP;char username[20];char buf[CMSGLEN];
*/
int main()
{
charname[100];
intserverfd;
structCLIENTMSG recvmsg, sendmsg;
structsockaddr_in server;
bzero(&server,sizeof(&server));
server.sin_addr.s_addr= inet_addr("127.0.0.1");
server.sin_port= htons(1234);
server.sin_family= AF_INET;
socklen_tlen = sizeof(server);
serverfd= socket(AF_INET, SOCK_STREAM, 0);
if(-1 == connect(serverfd, (struct sockaddr*)&server, len))
{
perror("connecterror");
exit(1);
}
printf("connectok\n");
read(serverfd,&recvmsg, sizeof(recvmsg));
if(recvmsg.OP == EXIT)
{
printf("####exitinformation: %s \n", recvmsg.buf);
return0;
}
printf("####inputyour name\n"); scanf("%s", &name);
sendmsg.OP= USER;
strcpy(sendmsg.username,name);
strcpy(sendmsg.buf,name);
write(serverfd,&sendmsg, sizeof(sendmsg));
intpid = fork();
if(pid == 0)
{
intbool = 1;
while(bool)
{
read(serverfd,&recvmsg, sizeof(recvmsg));
switch(recvmsg.OP)
{
caseEXIT:
printf("####exitinformation: %s \n",recvmsg.buf);
bool= 0;
break;
caseMSG:
printf("####from:%s:%s \n", recvmsg.username,recvmsg.buf);
break;
caseUSER:
printf("####usercoming:%s\n",recvmsg.username);
break;
default:
break;
}
}
}
elseif (pid > 0)
{
while(1)
{
charbuf[100];
scanf("%s", &buf);
if(!strcmp("bye", buf))
{
sendmsg.OP= EXIT;
strcpy(sendmsg.buf,buf);
write(serverfd,&sendmsg, sizeof(sendmsg));
break;
}
sendmsg.OP = MSG;
strcpy(sendmsg.buf,buf);
write(serverfd,&sendmsg, sizeof(sendmsg));
}
waitpid(-1,NULL, 0);
printf("###################EXIT!\n");
}
return0;
};
对信号量的操作
#ifndef _semaphore
#define _semaphore
#include"sys/sem.h"
union semun
{
int val;
struct semid_ds *buf ;
unsigned short *array;
};
int CreateSem(key_t key,int value)
{
union semun sem;
int semid;
sem.val=value;
if(-1==(semid=semget(key,1,IPC_CREAT)))
{
perror("semgeterror");
exit(1);
}
semctl(semid,0,SETVAL,sem);
return semid;
};
int Sem_P(int semid)
{
struct sembuf sops={0,+1,IPC_NOWAIT};
return (semop(semid,&sops,1));
};
int Sem_V(int semid)
{
struct sembuf sops={0,-1,IPC_NOWAIT};
return(semop(semid,&sops,1));
};
int GetvalueSem(int semid)
{
union semun sem;
return semctl(semid,0,GETVAL,sem);
}
void DestroySem(int semid)
{
unionsemun sem;
sem.val=0;
semctl(semid,0,IPC_RMID,sem);
}
#endif
源代码下载:http://download.csdn.net/detail/w1143408997/9243409