Select服務器實現多人通信的原理:
用戶與服務器之間需要動態建立三條管道,第一條管道用於判斷用戶是否上線,如果用戶n上線則在server.fifo中寫入on以及用戶id,然後建立專門用來發信息和收信息的管道,並將用戶n加入用戶鏈表。用戶n通過發信息管道向服務器發信息,服務器收到信息後,將信息發送給目前在線的所有用戶。用戶個數即鏈表中結點個數。
代碼如下:
1、頭文件
#ifndef __SER_H__
#define __SER_H__
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/select.h>
#include<sys/time.h>
#define PIPE_PATH "/home/comst/pipe"
#define PIPE_NAME "server.fifo"
typedef struct tag
{
int m_id ;
int m_send;
int m_recv;
struct tag* m_next ;
}CLIENT, *pCLIENT;
void distpatch_msg(pCLIENT phead, char* msg);
#endif
2、服務器server.c
/*************************************************************************
> File Name: ./src/server.c
> Author: Comst
> Mail:[email protected]
> Created Time: Sun 01 Feb 2015 04:41:48 PM CST
************************************************************************/
#include"my_server.h"
int main(int argc, char* argv[])
{
int fd_listen ;
char path_name[128] = "" ;
char fifo_name[128] ;
char msg[1024];
char client_stat[5] = "";
int client_pid ;
sprintf(path_name, "%s/%s", PIPE_PATH, PIPE_NAME);
mkfifo(path_name, 0666);
printf("mkfifo over!\n");
fd_listen = open(path_name, O_RDONLY);
if(fd_listen == -1)
{
printf("open server_fifo fail!\n");
exit(1);
}
pCLIENT plist = NULL, pcur, pnew, ppre ;
fd_set rd_sets, bak_sets;
FD_ZERO(&rd_sets);
FD_ZERO(&bak_sets);
FD_SET(fd_listen, &rd_sets);
while(1)
{
bak_sets = rd_sets ;
printf("selecting...\n");
select(1024, &bak_sets, NULL, NULL, NULL);
if(FD_ISSET(fd_listen, &bak_sets))
{
memset(msg,0, 1024);
if( read(fd_listen, msg, 1024) == 0 )
{
printf("no clients!\n");
continue ;
}
//
memset(client_stat, 0, sizeof(client_stat));
sscanf(msg, "%d%s", &client_pid, client_stat);
if(strncmp("on", client_stat, 2) == 0)// client on "pid on\n"
{// pid_r.fifo(c_r - s_w) pid_w.fifo(c_w - s_r)
printf("client: %d on\n", client_pid);
memset(fifo_name, 0, 128) ;
sprintf(fifo_name, "%d_r.fifo", client_pid);
memset(path_name, 0, 128) ;
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name);
pnew = (pCLIENT)calloc(1, sizeof(CLIENT));
pnew ->m_id = client_pid ;
printf("pid_r.fifo: %s\n", path_name);
pnew ->m_send = open(path_name, O_WRONLY);
printf("send_fd: %d\n", pnew ->m_send);
memset(fifo_name, 0, 128) ;
sprintf(fifo_name, "%d_w.fifo", client_pid);
memset(path_name, 0, 128) ;
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name);
printf("pid_w.fifo: %s\n", path_name);
pnew ->m_recv = open(path_name, O_RDONLY);
printf("recv_fd: %d\n", pnew ->m_recv);
printf("open client fifo: %d, %d\n", pnew ->m_send, pnew ->m_recv);
FD_SET(pnew ->m_recv, &rd_sets);
pnew ->m_next = plist ;
plist = pnew ;
}else//client off "pid off\n"
{
printf("client: %d off\n", client_pid);
ppre = NULL ;
pcur = plist ;
while(pcur && pcur ->m_id != client_pid)
{
ppre = pcur ;
pcur = pcur ->m_next ;
}
if(pcur == NULL)
{
printf("not exist!\n");
continue ;
}else
{
if(ppre == NULL)
{
plist = pcur ->m_next ;
}else
{
ppre ->m_next = pcur ->m_next ;
}
close(pcur ->m_send) ;
close(pcur ->m_recv) ;
FD_CLR(pcur ->m_recv, &rd_sets);
free(pcur);
printf("clear ok !\n");
}
}
}
pcur = plist ;
while(pcur)
{
if(FD_ISSET(pcur ->m_recv, &bak_sets))// translate
{
memset(msg, 0, 1024);
read(pcur -> m_recv, msg, 1024);
dispatch_msg(plist, msg);
}
pcur = pcur ->m_next ;
}
}
return 0 ;
}
3、用戶client.c
/*************************************************************************
> File Name: client.c
> Author: Comst
> Mail:[email protected]
> Created Time: Sun 01 Feb 2015 06:04:36 PM CST
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/select.h>
#include<sys/time.h>
#define PIPE_PATH "/home/comst/pipe"
#define PIPE_NAME "server.fifo"
int main(int argc, char* argv[])
{
int fd_server ;
char path_name[128]="";
char fifo_name[128] ;
char msg[1024] ="" ;
int fd_recv, fd_send ;
sprintf(path_name, "%s/%s", PIPE_PATH, PIPE_NAME);
fd_server = open(path_name, O_WRONLY);
if(fd_server == -1)
{
printf("open fail!\n");
exit(1) ;
}
// pid_r.fifo pid_w.fifo
//
memset(fifo_name, 0, 128);
sprintf(fifo_name, "%u_r.fifo", getpid());
memset(path_name, 0, sizeof(path_name));
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name);
if(-1 == mkfifo(path_name, 0666) )
{
printf("mkfif fail: %s\n", path_name);
exit(1) ;
}
printf("%s open\n", path_name);
memset(fifo_name, 0, 128);
sprintf(fifo_name, "%u_w.fifo", getpid());
memset(path_name, 0, sizeof(path_name));
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name);
if(mkfifo(path_name, 0666) == -1 )
{
printf("mkfif fail: %s\n", path_name);
exit(1) ;
}
printf("%s open\n", path_name);
printf("mkfifo over!\n");
sprintf(msg, "%u on\n", getpid());
printf("msg: %s\n", msg);
write(fd_server, msg, strlen(msg));
memset(fifo_name, 0, 128);
sprintf(fifo_name, "%u_r.fifo", getpid());
memset(path_name, 0, sizeof(path_name));
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name);
fd_recv = open(path_name, O_RDONLY);
memset(fifo_name, 0, 128);
sprintf(fifo_name, "%u_w.fifo", getpid());
memset(path_name, 0, sizeof(path_name));
sprintf(path_name, "%s/%s", PIPE_PATH, fifo_name);
fd_send = open(path_name, O_WRONLY);
printf("fifo open %d %d\n", fd_send, fd_recv);
fd_set rd_sets ;
FD_ZERO(&rd_sets);
while(1)
{
FD_SET(0, &rd_sets);
FD_SET(fd_recv, &rd_sets);
select(1024, &rd_sets, NULL, NULL, NULL);
if(FD_ISSET(0, &rd_sets))
{
memset(msg, 0, sizeof(msg)) ;
sprintf(msg, "from %u: ", getpid());
read(0, msg + strlen(msg), 1024 - strlen(msg) );
write(fd_send, msg, strlen(msg));
}
if(FD_ISSET(fd_recv, &rd_sets))
{
memset(msg, 0, sizeof(msg)) ;
read(fd_recv, msg, 1024);
write(1, msg, strlen(msg));
}
}
}
4、轉發函數func.c
/*************************************************************************
> File Name: ./func.c
> Author: Comst
> Mail:[email protected]
> Created Time: Sun 01 Feb 2015 05:57:36 PM CST
************************************************************************/
#include"my_server.h"
void dispatch_msg(pCLIENT phead, char* msg)
{
while(phead)
{
write(phead ->m_send, msg, strlen(msg));
phead = phead ->m_next ;
}
}
注:宏定義 #define PIPE_PATH "/home/comst/pipe" 在不同的文件系統中路徑不同,讀者應根據具體情況定義。