Linux下Socket 多客户端通信

要实现多客户端通信,必须要理清代码的框架






图可能有些潦草,但仔细研究两天就会懂了,除了这以外,还有定义一下客户端与客户端,客户端与服务器之间的通信协议,说白了就是定义一些结构体内容若如下

 

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值