七年前写的练手程序,好不容易翻出来,回过头来看当年的代码,感慨良多。源码在文末,分享一下,谢谢。
============================================================================================
实现目标
一个在Linux下可以使用的聊天软件,要求至少实现如下功能:
1. 采用Client/Server架构
2. Client A 登陆聊天服务器前,需要注册自己的ID和密码
3. 注册成功后,Client A 就可以通过自己的ID和密码登陆聊天服务器
4. 多个Client X 可以同时登陆聊天服务器之后,与其他用户进行通讯聊天
5. Client A成功登陆后可以查看当前聊天室内其他在线用户Client x
6. Client A可以选择发消息给某个特定的Client X,即”悄悄话”功能
7. Client A 可以选择发消息全部的在线用户,即”群发消息”功能
8. Client A 在退出时需要保存聊天记录
9. Server端维护一个所有登陆用户的聊天会的记录文件,以便备查
可以选择实现的附加功能:
1. Server可以内建一个特殊权限的账号admin,用于管理聊天室
2. Admin可以将某个Client X “提出聊天室”
3. Admin可以将某个Client X ”设为只能旁听,不能发言”
4. Client 端发言增加表情符号,可以设置某些自定义的特殊组合来表达感情.如输入:),则会自动发送”XXX向大家做了个笑脸”
5. Client段增加某些常用话语,可以对其中某些部分进行”姓名替换”,例如,输入/ClientA/welcome,则会自动发送”ClientA 大侠,欢迎你来到咱们的聊天室”
附加功能:
文件传输
部分主要代码:
客户端:
client_main.h
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#define PORT 6666
#define MAXLEN 1000
#define OK 1
#define FAULT 0
struct message /*消息结构体*/
{
char flag[15]; /*标志位*/
char name[20]; /*用户名*/
char msg[MAXLEN]; /*消息内容*/
char addressee[20]; /*传输文件目的用户*/
int size; /*传输内容字节数*/
};
int qid = -1,fd = -1;
int sockfd = -1; /*套接字描述符*/
int savefilefd = -1; /*保存文件描述符*/
char filefromname[20]; /*文件来源名*/
char chat_log[100]; /*聊天记录名*/
pthread_mutex_t lock ; /*线程锁*/
client_handle.h
#ifndef CLIENT_HANDLE_H
#define CLIENT_HANDLE_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#define PORT 8888
#define MAXLEN 1000
#define OK 1
#define FAULT 0
struct message /*消息结构体*/
{
char flag[15]; /*标志位*/
char name[20]; /*用户名*/
char msg[MAXLEN]; /*消息内容*/
char addressee[20]; /*传输文件目的用户*/
int size; /*传输内容字节数*/
};
extern int qid,fd;
extern int sockfd; /*套接字描述符*/
extern int savefilefd; /*保存文件描述符*/
extern char filefromname[20]; /*文件来源名*/
extern char chat_log[100]; /*聊天记录名*/
extern pthread_mutex_t lock ; /*线程锁*/
char filefromuser[20]; /*文件发送者用户名*/
char locname[20]; /*本客户端用户名*/
int Interface();
void cutStr(char str[],char left[], int n, char right[],int m, char c);
int help(char str[]);
void expression(char name[],char msg[]);
void common_use_words(char msg[]);
void handlesendfile();
void handlerecvfile(struct message *msg);
void handlerecvmsg(int *sockfd);
int admin_kick(int sockfd,struct message *a);
int admin_screen(int sockfd,struct message *a);
int login_admin(struct message *a);
int login_success(struct message *a);
int Register(struct message *a);
void log_user(struct message *a);
#endif //CLIENT_HANDLE_H
my_system_call.h
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
client_main.c
/***************************************************
File name: client.c
Author:Chenchunjian
Data:2012
Description:聊天软件客户端main函数
***************************************************/
#include "../../include/client_main.h"
int main(int argc, char *argv[])
{
int ret;
int do_number;
char str[MAXLEN];
char buf[MAXLEN];
char buf_new[100];
struct message msg;
struct message a;
struct sockaddr_in my_addr;
time_t timep;
enum action{log=1,reg,ex};
struct hostent *host;
if(argc!=2)
{
fprintf(stderr,"Usage:%s hostname \a\n",argv[0]);
exit(1);
}
if((host=gethostbyname(argv[1]))==NULL)
{
fprintf(stderr,"Gethostname error\n");
exit(1);
}
while(1)
{
if((sockfd=my_socket(PF_INET,SOCK_STREAM,0)) < 0) /*创建套接字,使用TCP协议*/
{
exit(-1);
}
bzero(&my_addr,sizeof(struct sockaddr_in)); /*清空地址结构*/
my_addr.sin_family = AF_INET; /*使用IPV4通信域*/
my_addr.sin_port = htons(PORT); /*端口号转换为网络字节序*/
my_addr.sin_addr = *((struct in_addr *)host->h_addr); /*可接受任意地址*/
if(my_connect(sockfd,(struct sockaddr *)(&my_addr),sizeof(my_addr)) == -1) /*主动连接服务器*/
{
printf("正在连接,请稍等……\n");
exit(1);
}
do_number = Interface(); /*登录界面*/
switch(do_number)
{
case log: /*登录*/
{
int n = 3;
while(n)
{
log_user(&a);
if(my_strcmp(a.msg,"hello,admin!") == 0) /*管理员登录成功*/
{
if(login_admin(&a) == 0)
{
return FAULT;
}
}
if(my_strcmp(a.msg,"login,success!") == 0) /*用户登录成功*/
{
if(login_success(&a) == FAULT)
{
return FAULT;
}
}
else /*登录未成功*/
{
n--;
printf("你还有 %d次机会!\n",n);
}
}
my_close(sockfd);
exit(3);
break;
}
case reg: /*注册*/
{
Register(&a);
break;
}
case ex: /*退出*/
{
my_close(sockfd);
printf("离开聊天室!\n");
break;
}
default:
{
break;
}
}
}
my_close(sockfd);
return FAULT;
}
admin.c
#include "../../include/client_handle.h"
/***************************************************
函数名:admin_chat
功能:管理员聊天功能
传入参数:int sockfd,struct message *a
返回值:退出返回FAULT
***************************************************/
int admin_chat(int sockfd,struct message *a)
{
char str[MAXLEN];
char buf[MAXLEN];
time_t timep;
sprintf(chat_log,"./chat_log/%s.txt",(*a).name);
if((fd=my_open(chat_log,O_RDWR|O_CREAT|O_APPEND,0777)) < 0)
{
printf("打开聊天记录失败!");
exit(1);
}
setbuf(stdin,NULL);
my_strcpy((*a).flag,"all");
printf("尊敬的%s你好,如需帮助请输入:.help\n",locname);
while(1)
{
memset((*a).msg,0,strlen((*a).msg));
memset(str,0,strlen(str));
usleep(100000);
printf("TO %s:\n",(*a).flag);
setbuf(stdin,NULL);
gets(str);
if(OK == help(str)) //提示信息
{
continue;
}
my_strcpy((*a).name,locname);
my_strcpy(buf,(*a).flag);
cutStr(str,(*a).flag,15,(*a).msg,MAXLEN,'$'); //调用字符切割函数
expression((*a).name,(*a).msg); //表情替换函数
common_use_words((*a).msg); //常用语使用函数
if(my_strcmp((*a).flag,"exit") == 0)
{
return FAULT;
}
if(my_strcmp((*a).flag,"view") == 0)
{
my_send(sockfd,a,sizeof((*a)),0); //请求查看在线用户
my_strcpy((*a).flag,buf);
continue;
}
if(my_strcmp((*a).flag,"all") == 0)
{
my_send(sockfd,a,sizeof(*a),0);
continue;
}
if ((my_strcmp((*a).flag,"trans") == 0) && (savefilefd <=0))
{
if ((my_strcmp((*a).msg,"agree") == 0) && (savefilefd == 0))
{
char savefilename[MAXLEN];
my_strcpy((*a).addressee,filefromuser);
printf("请输入你想保存的文件名:\n");
do
{
setbuf(stdin,NULL);
gets(savefilename);
savefilefd = open(savefilename,O_RDWR|O_CREAT|O_EXCL,0777);
if(savefilefd == -1)
{
printf("文件已存在,你重新输入:\n");
}
}while(savefilefd == -1);
if(savefilefd < 0)
{
printf("接收文件失败!\n");
savefilefd = -1;
}
else
{
my_strcpy((*a).msg,"agree");
my_send(sockfd,a,sizeof(*a),0);
printf("文件接收中……\n");
}
}
else
{
memset(str,0,strlen(str));
cutStr((*a).msg,(*a).addressee,20,str,MAXLEN,'$');
if (str[0] != '\0' && (*a).addressee[0] != '\0')
{
char transfileallname[22];
sprintf(transfileallname,"./%s",str);
savefilefd = open(str,O_RDWR,0666);
if(savefilefd < 0)
{
printf("打开文件失败!\n");
savefilefd = -1;
}
else
{
memset((*a).msg,0,strlen((*a).msg));
my_strcpy((*a).msg,str);
my_send(sockfd,a,sizeof(*a),0);
}
}
else
{
my_strcpy((*a).msg,"disagree");
my_strcpy((*a).name,locname);
my_strcpy((*a).addressee,filefromuser);
my_send(sockfd,a,sizeof(*a),0);
}
}
my_strcpy((*a).flag,buf);
continue;
}
if (my_strcmp((*a).flag,"trans") == 0)
{
my_strcpy((*a).flag,buf);
}
else
{
my_strcpy(buf,(*a).flag);
my_strcpy((*a).addressee,(*a).flag);
my_strcpy((*a).flag,"personal");
my_send(sockfd,a,sizeof(*a),0); //发送私信
my_strcpy((*a).flag,buf);
time (&timep);
memset(str,0,strlen(str));
sprintf(str,"%s你对 %s 说: %s\n",ctime(&timep),(*a).flag,(*a).msg);
printf("%s",str);
my_write(fd,str,strlen(str)); //写入聊天记录文件中
}
}
}
/***************************************************
函数名:admin_k