目标:
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 { if(clientList[i].socketFd==0)break; } if(i } 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:%s\e[0m\n",clientList[locate].name,clientList[msg.se ndUserLocate].name,msg.message); } else { int i; for (i=0;i { if(clientList[i].socketFd!=0 && i!=locate) { write(clientList[i].socketFd,&msg,sizeof(msg)); printf("\e[32m#PUBLIC > From:%-5s To: %-5s Msg:%s\e[0m\n",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\n",clientList[locate].name); } else if(cmd==DEL) { printf("\e[33m*DEl USER> NAME:%-5s \e[0m\n",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\n",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 \n",msg.type); break; } } return NULL; } int main() { printf("\n\tservice is start.....\n"); 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 CONNECT\e[0m \n"); 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!='\n'&&c!=EOF);}; int CheckExist() { int i; for(i=0;i { if(!strcmp(g_name,clientList[i].name)) break; } if(i { printf("this name: %s is already exist!!\n",g_name); return 1; } else return 0; } void ShowList() { int i; g_total=0; printf("\t _____________________________ \n"); printf("\t| CLIENT LIST |\n"); printf("\t|_____________________________|\n"); printf("\t|\e[4m sort | name \e[24m|\n "); for(i=0;i { if(clientList[i].socketFd!=0) { if(i==g_locate) { printf("\t|\e[4;31m *%-4d | %-10s \e[0m|\n",++g_total,clientList[i].name); } else { printf("\t|\e[4m %-4d | %-10s \e[24m|\n",++g_total,clientList[i].name); } } } printf("\t|\e[4m Total:%-3d \e[24m|\n",g_total); } int MakeTempList(int *tmp) { int i,n=0; for(i=0;i { if(clientList[i].socketFd!=0) { tmp[n]=i; n++; } } ShowList(); int select; printf("please select the user \n"); if(1!=scanf("%d",&select)) { flush(); printf("bad select \n"); return -1; } if(select<=g_total) { if(tmp[select-1]==g_locate) { printf("\e[33m#SYSTEM:YOU CAN NOT SELECT YOURSELF\e[0m\n"); return -1; } else return tmp[select-1]; } else { printf("bad select \n"); 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 succeed\e[0m\n"); else printf("\e[33m#LOGIN > From:%-10s Msg:%s\e[0m\n",msg.fromUser,msg.message); break; case EXIT: printf("\e[33m#EXIT > From:%-10s Msg:%s\e[0m\n",clientList[msg.fromUserLocate].name, msg.message); break; case PUBLIC: printf("\e[32m#PUBLIC > From:%-10s Msg:%s\e[0m\n",msg.fromUser,msg.message); break; case PRIVATE: printf("\e[31m#PRIVATE> From:%-10s Msg:%s\e[0m\n",msg.fromUser,msg.message); break; default:break; } memcpy(&clientList,&msg.clientList,sizeof(clientList)); } printf("server is breakdown \n"); 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\n"); 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("\npublic: please input content \n"); flush(); fgets(msg.message,sizeof(msg.message),stdin); msg.message[strlen(msg.message)-1]='\0'; write(fd,&msg,sizeof(msg)); break; case 2: bzero(tmp,sizeof(tmp)); msg.type=PRIVATE; if(-1!=(msg.sendUserLocate=MakeTempList(tmp))) { printf("\nprivate: please input content \n"); flush(); fgets(msg.message,sizeof(msg.message),stdin); msg.message[strlen(msg.message)-1]='\0'; write(fd,&msg,sizeof(msg)); } break; case 3: printf("EXIT \n"); msg.type=EXIT; strcpy(msg.message,"bye-bye"); write(fd,&msg,sizeof(msg)); break; case 4: ShowList(); break; default: printf("bad select \n"); 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 \n");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 \n"); 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\n");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