目标:
linux系统下实现聊天室
(1)在Linux系统下,使用TCP协议套接字编程;
(2)服务器应具有处理多个客户端连接能力(设定最大连接数,如5个);
(3)具有群发和私聊的能力;
(4)过程描述
客户端:连接服务器后,应能接收服务器发来信息并处理的能力,当收到聊天信息时,显示群发或私聊、信息发送客户及发送的信息,当收到客户加入或退出时,显示客户登录或退出,并更新客户列表;每次可选择群发或私聊,群发时将键盘输入信息发送给服务器,私聊时,选择私聊客户并将输入信息发送给服务器。选择退出时,结束进程或线程,关闭程序。
服务器:为每个客户连接创建一个进程或线程,处理客户信息,当有新客户加入或有客户退出时,将客户加入或退出信息发送给每个客户端;当收到某客户的群发信息时,将信息转发给每个客户,当收到客户私聊时将信息转发给私聊客户;客户退出时关闭相应的进程或线程。
二、系统设计
1、数据结构设计
A.定义在线客户列表数据结构ClientList。
typedef struct ClientList
{
char name[NAME_LEN];
int socketFd;
}CLIENTLIST;
说明:
name为客户端名称;socketFd是服务器为客户端分配的通信 socket套接字标识符;
B.定义客户与服务器之间交互信息格式结构Message。
typedef struct Message
{
char fromUser[NAME_LEN];
int fromUserLocate;
int type;
int sendUserLocate;
char message[MSG_LEN];
CLIENTLIST clientList[MAX_CLIENT];
}MESSAGE;
说明:
fromUser为客户端名称;fromUserLocate为该客户端在在线客户列表 里的位置;type为消息的类型(包括群发、私聊、登录、退出); sendUserLocate为私聊对象在客户端列表中的位置(仅在type为私 聊类型是有效);message为发送的消息内容,clientList为在线客户
列表。
C.定义在服务器端主线程为接收线程传递的数据结构ARG。
struct ARG
{
int locate;
int fifoFd;
};
说明:
locate为与之通信的客户端在在线客户列表中的位置;
fifoFd为以只写方式打开的管道标识符;
D.客户端中的全局变量
pthread_t tid1; //接收线程的标识符
char g_name[NAME_LEN];//客户端用户的名称
int g_locate; //客户端在在线客户列表中的位置
int g_total; //在线客户的总数
2、流程设计
A. 服务器端:
B.客户端:
三、编码实现
Public.h
#ifndef PUBLIC_H_
#define PUBLIC_H_
#include"stdio.h"
#include"stdlib.h"
#include"string.h"
#include"sys/socket.h"
#include"sys/types.h"
#include"arpa/inet.h"
#include"netinet/in.h"
#include"unistd.h"
#include"pthread.h"
#define MAX_CLIENT 3
#define NAME_LEN 20
#define MSG_LEN 100
#define PORT 12345
#define LOGIN 1
#define EXIT 2
#define PUBLIC 3
#define PRIVATE 4
#define OK 5
#define ERROR -6
typedef struct ClientList
{
char name[NAME_LEN];
int socketFd;
}CLIENTLIST;
typedef struct Message
{
char fromUser[NAME_LEN];
int fromUserLocate;
int type;
int sendUserLocate;
char message[MSG_LEN];
CLIENTLIST clientList[MAX_CLIENT];
}MESSAGE;
CLIENTLIST clientList[MAX_CLIENT];
#endif
Server.c
#include"public.h"
#include"sys/stat.h"
#include"fcntl.h"
#define ADD 7
#define DEL 8
#define FIFO "FIFO"
struct ARG
{
int locate;
int fifoFd;
};
int SearchLocate()
{
int i;
for(i=0;i<MAX_CLIENT;i++)
{
if(clientList[i].socketFd==0)break;
}
if(i<MAX_CLIENT) return i; else return -1;
}
void TransmitMsg(int cmd,int locate,MESSAGE msg)
{
memcpy(&msg.clientList,&clientList,sizeof(clientList));
if(cmd==PRIVATE)
{
write(clientList[msg.sendUserLocate].socketFd,&msg,
sizeof(msg));
printf("e[31m#PRIVATE > From:%-5s To: %-5s
Msg:%se[0m ",clientList[locate].name,clientList[msg.se ndUserLocate].name,msg.message);
}
else
{
int i;
for (i=0;i<MAX_CLIENT;i++)
{
if(clientList[i].socketFd!=0 && i!=locate)
{
write(clientList[i].socketFd,&msg,sizeof(msg));
printf("e[32m#PUBLIC > From:%-5s To: %-5s
Msg:%se[0m ",clientList[locate].name,
clientList[i].name,msg.message);
}
}
if(cmd==LOGIN)
{
write(clientList[locate].socketFd,&msg,sizeof(msg));
}
}
}
void UpdateList(int cmd , char *name,int locate)
{
if(cmd==ADD)
{
strcpy(clientList[locate].name,name);
printf("e[33m*ADD USER> NAME:%-5s
e[0m ",clientList[locate].name);
}
else if(cmd==DEL)
{
printf("e[33m*DEl USER> NAME:%-5s e[0m ",clientList[locate].name);
clientList[locate].socketFd=0;
bzero(clientList[locate].name,NAME_LEN);
}
}
void *RecvMsg(void *arg_t)
{
struct ARG arg=*(struct ARG *)arg_t;
MESSAGE msg;
while(1)
{
int flag;
bzero(&msg,sizeof(msg)); msg.type=ERROR;
read(clientList[arg.locate].socketFd,&msg,sizeof(msg));
msg.fromUserLocate=arg.locate;
if(msg.type==EXIT||msg.type==ERROR)
{
if(msg.type==ERROR)
{
strcpy(msg.message,"breakdown");
printf("e[33m*CLIENT:%s HAD BREAKDOWN
e[0m ",clientList[msg.fromUserLocate].name);
msg.type=EXIT;
}
if(-1==(flag=write(arg.fifoFd,&msg,sizeof(msg))))
{
perror("write fifo error");
exit(1);
}
break;
}
if(-1==(flag=write(arg.fifoFd,&msg,sizeof(msg))))
{
perror("write fifo error");
exit(1);
}
}
return NULL;
}
void *SendMsg(void *fd)
{
int fifoFd;
if(-1==(fifoFd=open(FIFO,O_RDONLY)))
{
perror("open fifo error");
exit(1);
}
int flag;
MESSAGE msg;
while(1)
{
if(-1==(flag=read(fifoFd,&msg,sizeof(msg))))
{
perror("read fifo error");
exit(2);
}
int exit_fd;
switch(msg.type)
{
case LOGIN:
UpdateList(ADD,msg.fromUser,msg.fromUserLocate);
TransmitMsg(LOGIN,msg.fromUserLocate,msg);
break;
case PUBLIC:
TransmitMsg(PUBLIC,msg.fromUserLocate,msg);
break;
case PRIVATE:
TransmitMsg(PRIVATE,msg.fromUserLocate,msg);
break;
case EXIT:
exit_fd=clientList[msg.fromUserLocate].socketFd;
UpdateList(DEL,msg.fromUser,msg.fromUserLocate);
TransmitMsg(EXIT,msg.fromUserLocate,msg);
close(exit_fd);
break;
default:
printf("bad data %d ",msg.type);
break;
}
}
return NULL;
}
int main()
{
printf(" service is start..... ");
pthread_t tid1,tid2;
int fd,clientfd,wr_fifo;
socklen_t sock_len;
sock_len=sizeof(struct sockaddr_in);
mkfifo(FIFO,O_CREAT|O_EXCL);
pthread_create(&tid1,NULL,SendMsg,NULL);
struct sockaddr_in server,client;
server.sin_port=htons(PORT);
server.sin_family=AF_INET;
server.sin_addr.s_addr=INADDR_ANY;
if(-1== (fd=socket(AF_INET,SOCK_STREAM,0)))
{
perror("socket error ");
exit(1);
}
if(-1==bind(fd,(struct sockaddr*)&server,sock_len))
{
perror("bind error");
exit(2);
}
if(-1==(listen(fd,MAX_CLIENT+1)))
{
perror("listen error");
exit(3);
}
if(-1==(wr_fifo=open(FIFO,O_WRONLY)))
{
perror("open fifo error");
exit(1);
}
while(1)
{
if(-1==(clientfd=(accept(fd,(struct
sockaddr*)&client,&sock_len))))
{
perror("accept error");
exit(4);
}
int locate=-1;
MESSAGE msg;
if(-1==(locate=SearchLocate()))
{
printf("e[33m*RECEIVE A APPLY BUT CANNOT ALLOW
CONNECTe[0m ");
msg.type=EXIT;
write(clientfd,&msg,sizeof(msg));
}
else
{
struct ARG arg;
arg.fifoFd=wr_fifo;
arg.locate=locate;
msg.type=OK;
memcpy(&msg.clientList,&clientList,sizeof(clientList));
msg.fromUserLocate=locate;
write(clientfd,&msg,sizeof(msg));
clientList[locate].socketFd=clientfd;
pthread_create(&tid1,NULL,RecvMsg,(void *)&arg);
}
}
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
unlink("FIFO");
return 0;
}
Client.c
#include"public.h"
pthread_t tid1;
char g_name[NAME_LEN];
int g_locate;
int g_total;
void flush(){ char c; do{c=getc(stdin);}while(c!=' '&&c!=EOF);};
int CheckExist()
{
int i;
for(i=0;i<MAX_CLIENT;i++)
{
if(!strcmp(g_name,clientList[i].name))
break;
}
if(i<MAX_CLIENT)
{
printf("this name: %s is already exist!! ",g_name);
return 1;
}
else
return 0;
}
void ShowList()
{
int i;
g_total=0;
printf(" _____________________________ ");
printf(" | CLIENT LIST | ");
printf(" |_____________________________| ");
printf(" |e[4m sort | name e[24m| ");
for(i=0;i<MAX_CLIENT;i++)
{
if(clientList[i].socketFd!=0)
{
if(i==g_locate)
{
printf(" |e[4;31m *%-4d | %-10s
e[0m| ",++g_total,clientList[i].name);
}
else
{
printf(" |e[4m %-4d | %-10s
e[24m| ",++g_total,clientList[i].name);
}
}
}
printf(" |e[4m Total:%-3d e[24m| ",g_total);
}
int MakeTempList(int *tmp)
{
int i,n=0;
for(i=0;i<MAX_CLIENT;i++)
{
if(clientList[i].socketFd!=0)
{ tmp[n]=i; n++; }
}
ShowList();
int select;
printf("please select the user ");
if(1!=scanf("%d",&select))
{
flush();
printf("bad select ");
return -1;
}
if(select<=g_total)
{
if(tmp[select-1]==g_locate)
{
printf("e[33m#SYSTEM:YOU CAN NOT SELECT
YOURSELFe[0m ");
return -1;
}
else
return tmp[select-1];
}
else
{
printf("bad select ");
return -1;
}
}
void *RecvMsg(void *fd)
{
int sockfd=*(int *)fd;
MESSAGE msg;
while(1)
{
bzero(&msg,sizeof(msg)); msg.type=ERROR;
read(sockfd,&msg,sizeof(msg));
if(msg.type==ERROR)
break;
switch(msg.type)
{
case LOGIN:
if(msg.fromUserLocate==g_locate)
printf("e[34m###### > loing succeede[0m ");
else
printf("e[33m#LOGIN > From:%-10s
Msg:%se[0m ",msg.fromUser,msg.message);
break;
case EXIT:
printf("e[33m#EXIT > From:%-10s
Msg:%se[0m ",clientList[msg.fromUserLocate].name,
msg.message);
break;
case PUBLIC:
printf("e[32m#PUBLIC > From:%-10s
Msg:%se[0m ",msg.fromUser,msg.message);
break;
case PRIVATE:
printf("e[31m#PRIVATE> From:%-10s
Msg:%se[0m ",msg.fromUser,msg.message);
break;
default:break;
}
memcpy(&clientList,&msg.clientList,sizeof(clientList));
}
printf("server is breakdown ");
exit(1);
}
void SendMsg(int fd)
{
MESSAGE msg;
msg.type=LOGIN;
msg.fromUserLocate=g_locate;
strcpy(msg.fromUser,g_name);
strcpy(msg.message,g_name);
write(fd,&msg,sizeof(msg));
int tmp[MAX_CLIENT];
int key;
while(1)
{
printf(" 1 public 2 private 3 EXIT 4 client list ");
if(1!= scanf("%d",&key))
{
key=0;
flush();
}
bzero(&msg,sizeof(msg));
strcpy(msg.fromUser,g_name);
msg.fromUserLocate=g_locate;
switch(key)
{
case 1:
msg.type=PUBLIC;
printf(" public: please input content ");
flush();
fgets(msg.message,sizeof(msg.message),stdin);
msg.message[strlen(msg.message)-1]=' ';
write(fd,&msg,sizeof(msg));
break;
case 2:
bzero(tmp,sizeof(tmp));
msg.type=PRIVATE;
if(-1!=(msg.sendUserLocate=MakeTempList(tmp)))
{
printf(" private: please input content ");
flush();
fgets(msg.message,sizeof(msg.message),stdin);
msg.message[strlen(msg.message)-1]=' ';
write(fd,&msg,sizeof(msg));
}
break;
case 3:
printf("EXIT ");
msg.type=EXIT;
strcpy(msg.message,"bye-bye");
write(fd,&msg,sizeof(msg));
break;
case 4:
ShowList();
break;
default:
printf("bad select ");
msg.type=0;
break;
}
if(msg.type==EXIT)
{
break;
}
}
pthread_cancel(tid1);
}
int main()
{
int fd;
char ip[20]="127.0.0.1";
//printf("please input the ip ");scanf("%s",ip);
struct sockaddr_in addr;
addr.sin_port=htons(PORT);
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=inet_addr(ip);
if(-1==(fd=socket(AF_INET,SOCK_STREAM,0)))
{
perror("socket error");
exit(1);
}
if(-1==(connect(fd,(struct sockaddr*)&addr,sizeof(struct
sockaddr))))
{
perror("connect error");
exit(2);
}
MESSAGE msg;
read(fd,&msg,sizeof(msg));
if(msg.type==EXIT)
{
printf("service refuse connect ");
exit(1);
}
else
{
memcpy(&clientList,&msg.clientList,sizeof(clientList));
g_locate=msg.fromUserLocate;
pthread_create(&tid1,NULL,RecvMsg,(void *)&fd);
do{
printf("please input your name ");scanf("%s",g_name);
}while(CheckExist());
SendMsg(fd);
pthread_join(tid1,NULL);
}
return 0;
}
MAkeFile
tar:server client
server:server.c
gcc -g -Wall -o $@ $< -lpthread
client:client.c
gcc -g -Wall -o $@ $< -lpthread
c:
rm -rf server client FIFO
源代码:http://download.csdn.net/detail/w1143408997/9408145