编译源代码的命令
gcc client.c -o client
gcc server.c -o server
在一台linux运行server,另外一台linux运行client,2台电脑处于同一个局域网网段。
功能列表
1 注册信息写入文件 服务端将用户信息写入文件
2 查看用户所有用户信息 服务端查看所有用户信息
3 删除用户注册信息 服务端将用户信息删除
4 踢掉用户 服务端踢掉指定用户
5 注册 客户端用户可进行注册
6 登录 客户端已注册的用户可以进行登录
7 查看在线(所有)好友 客户端查看在线(所有)好友
8 私聊 客户端一对一的聊天
9 公聊 客户端对在线所有用户进行群发消息
10 查看聊天记录 客户端用户查看本用户的聊天信息
11 聊天记录写入文件 客户端将用户聊天信息写入文件
12 修改密码 客户端用户可以修改登陆密码
13 传文件 可一对一的传输文件
server.c
//===包含的头文件=========
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <fcntl.h>
#include "linklist_cwy.c"
//========================
//===定义的宏=============
#define BACKLOG 50
//=========================
//===全局变量=======================
LinkList L;//链表头指针
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//mutex快速互斥锁
int server_sockfd;
//==================================
//===函数声明======================
void pthread_server_menu(void);
void pthread_client(int *temp);
int sendto_client(int *client_sockfd_n,char *buf,int length);
int recvfrom_client(int *client_sockfd_n,char *buf,int length);
int regist_server(int *p_client_sockfd_n);
struct user_msg log_in_server(int *p_client_sockfd_n);
int chat_server(int *p_client_sockfd_n,struct user_msg user);
void speak2all(LinkList L,char *buf,int len);
void broadcast(void);
void kick_off(void);
void Getuseronline_client(LinkList L,int p_client_sockfd_n);
void Getalluser_client(LinkList L,int p_client_sockfd_n);
void delete_user(void);
static void change_passwd(int *p_client_sockfd_n,char myname[]);
void sendfile_client(int file_from_socket,int file_to_socket);
//==================================
//===主函数=======================
int main(int argc,char *argv[])
{
//参数合法性判断
if(argc<2)
{
printf("\n");
printf("---请按以下示例输入---\n");
printf("./server_cwy 3333 \n");
printf("\n");
exit(1);
}
CreateList_L(&L);//创建一个链表,传入参数:头指针的地址
file2list(&L);//将文件的内容读到链表,传入参数:头指针的地址
pthread_mutex_init(&mutex,NULL);//初始化互斥锁
int ret;
//===创建服务器-功能菜单-子线程======================
pthread_t pid_server_menu;//服务器-菜单子线程ID号
ret = pthread_create(&pid_server_menu,NULL,(void *)pthread_server_menu,NULL);
if(ret != 0)
{
perror("pthread_creat");
exit(1);
}
printf("\n");
printf("---服务器-功能菜单-子线程创建成功!---\n");
printf("\n");
//===服务器-主线程(等待客户端连接)==============
printf("\n");
printf("---服务器-主线程(等待客户端连接)启动成功!---\n");
printf("\n");
//创建主socket
server_sockfd = socket(AF_INET,SOCK_STREAM,0);
if(server_sockfd == -1)
{
perror("socket");
exit(1);
}
printf("\n");
printf("---创建主socket成功!server_sockfd=%d---\n",server_sockfd);
printf("\n");
//设置服务器地址结构体
struct sockaddr_in server_sockaddr;
unsigned short int port;//端口号
//atol()能将字符串转换成长整形数
port = (unsigned short int)atol(argv[1]);//通过main函数参数传进端口号
server_sockaddr.sin_family = AF_INET;
//htons()主机字节顺序转换为网络字节顺序(对无符号短型进行操作2bytes)
server_sockaddr.sin_port = htons(port);
//htonl()主机字节顺序转换为网络字节顺序(对无符号长型进行操作4bytes)
server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);//获取服务器当前 IP INADDR_ANY:表示本机网卡的IP
bzero(&(server_sockaddr.sin_zero),8);
//主socket绑定IP和端口
ret = bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr));
if(ret == -1)
{
perror("bind");
exit(1);
}
printf("\n");
printf("---主socket绑定IP和端口成功!---\n");
printf("\n");
//监听
ret = listen(server_sockfd,BACKLOG);
if(ret == -1)
{
perror("listen");
exit(1);
}
printf("\n");
printf("---主socket监听成功!---\n");
printf("\n");
struct sockaddr_in client_sockaddr_n;
int len;
int pthread_n = 0;
while(1)
{
pthread_t id_client;
printf("\n");
printf("---服务器-主线程正在等待新的客户端连接~~~\n");
printf("\n");
int client_sockfd = accept(server_sockfd,(struct sockaddr*)&client_sockaddr_n,(socklen_t*)&len);
printf("\n");
printf("---有一个新的客户端(client_sockfd=%d)连接成功---\n",client_sockfd);
printf("\n");
printf("\n");
printf("---获取客户端(client_sockfd=%d)IP和端口成功!---\n");
//inet_ntoa()将一个IP转换成一个互联网标准点分格式的字符串
printf("IP:%s\n",inet_ntoa(client_sockaddr_n.sin_addr));
printf("端口号:%d\n",port);
printf("\n");
int *p_client_sockfd_n = (int *)malloc(sizeof(int));
*p_client_sockfd_n = client_sockfd;
//创建新的处理客户端-子线程
pthread_create(&id_client,NULL,(void *)pthread_client,p_client_sockfd_n);
printf("\n");
printf("---新的处理客户端(client_sockfd=%d)-子线程%d创建成功---\n",client_sockfd,++pthread_n);
printf("\n");
}
return 0;
}
//=====================================================================
//===功能菜单==================================
void menu()
{
printf("======功能菜单======\n");
printf("[1]显示所有用户信息\n");
printf("[2]显示在线用户信息\n");
printf("[3]发送广播\n");
printf("[4]踢掉指定用户\n");
printf("[5]删除指定用户信息\n");
printf("[6]退出服务器\n");
printf("ctrl+\\(唤出功能菜单)\n");
printf("===============\n");
}
//======================================
//===服务器功能菜单-子线程处理函数===========
void pthread_server_menu(void)
{
signal(SIGQUIT,menu);
char buf_input[10];
char option;
while(1)
{
printf("\n\n");
menu();
memset(buf_input,0,10);
fflush(stdin);//清输入缓存
printf("请输入数字1-6进入选项:");
scanf("%s",buf_input);
printf("\n\n");
option=buf_input[0];
switch (option)
{
case '1':
{
Disp_L(L);//服务端显示所有用户
break;
}
case '2':
{
Getstate_L(L);//服务端显示在线用户
break;
}
case '3':
{
broadcast();//广播系统信息
break;
}
case '4':
{
kick_off();//踢掉指定用户
break;
}
case '5':
{
delete_user();//删除指定用户
break;
}
case '6':
{
pthread_mutex_destroy(&mutex);//退出服务器
close(server_sockfd);
//close(socketClt);
exit(0);
}
default:
{
printf("---输入无效,请重新输入数字1-6---\n");
break;
}
}
}
}
//========================================
//===系统广播=============================
void broadcast(void)
{
int len;
char buf_input[1024];
char buf_send[1024];
memset(buf_input,0,1024);
fflush(stdin);//清输入缓存
printf("请输入广播内容:");
scanf("%s",buf_input);
memset(buf_send,0,1024);
sprintf(buf_send,"系统公告:\n%s",buf_input);
len=strlen(buf_send);
speak2all(L,buf_send,len);
printf("---广播已发送---\n");
}
//=========================================
//===踢掉指定用户函数===========================
void kick_off(void)
{
char c;
char name_off[10];
char buf_send[1024];
int len;
int socketoff;
loopoff:
memset(name_off,0,10);
printf("请输入要踢掉的用户名:");
fflush(stdin);//清输入缓存
scanf("%s",name_off);
if((socketoff=LocateUsersoket_L(L,name_off))>0)
{
if(send(socketoff,"!t",2,0)==-1)
perror("send");
printf("用户:%s已被踢掉线\n",name_off);
}
else
{
printf("用户名:%s不存在,请重新输入\n",name_off);
goto loopoff;
}
//===踢掉线通知===
memset(buf_send,0,1024);
sprintf(buf_send,"用户:%s已被踢掉线!\n",name_off);
len=strlen(buf_send);
speak2all(L,buf_send,len);
}
//====================================================
//===删除指定用户信息=================================
void delete_user(void)
{
char c;
char name1[10];
loopdel:
memset(name1,0,10);
printf("请输入要删除的用户名\n");
printf(":");
scanf("%s",name1);
while((c=getchar())!='\n'&&c!=EOF);
if(ListDelete_L(&L,name1)==0)
{
if(pthread_mutex_lock(&mutex)<0)
{
perror("pthread_mutex_lock");
}
update_file(L);
if(pthread_mutex_unlock(&mutex)!=0)
{
perror("pthread_mutex_unlock");
}
printf("该用户信息已成功删除\n");
}
else
{
printf("该用户名不存在,请重新输入\n");
goto loopdel;
}
}
//=================================================
//===处理客户端-子线程函数==============
void pthread_client(int *p_client_sockfd_n)
{
//struct user_msg user;
int sendbytes,recvbytes;
char buf[10];
while(1)
{
memset(buf,0,10);//清空数据收发缓存
recvbytes = recvfrom_client(p_client_sockfd_n,buf,10); //recvbytes = recv(*p_client_sockfd_n,buf,10,0);
//===收到0字节表示客户端断开连接
if(recvbytes == 0)
{
printf("\n");
printf("---客户端client_sockfd=%d断开连接---\n",*p_client_sockfd_n);
printf("\n");
close(*p_client_sockfd_n);
pthread_exit(NULL);
}
switch (buf[0])
{
case '1'://注册
{
printf("\n");
printf("---注册新的用户中---\n");
printf("\n");
regist_server(p_client_sockfd_n);
break;
}
case '2'://登陆
{
printf("\n");
printf("---客户端client_sockfd=%d登陆中---\n",*p_client_sockfd_n);
printf("\n");
log_in_server(p_client_sockfd_n);
break;
}
case '3'://下线
{
printf("\n");
printf("---客户端client_sockfd=%d退出---\n",*p_client_sockfd_n);
printf("\n");
close(*p_client_sockfd_n);
pthread_exit(NULL);
break;
}
default:
{
printf("\n");
printf("---客户端client_sockfd=%d发来无法识别的指令信息---\n",*p_client_sockfd_n);
printf("\n");
break;
}
}
}
}
//====================================================================
//===注册=========================================
int regist_server(int *p_client_sockfd_n)
{
struct user_msg user;
int ret;
int recvbytes;
while(1)
{
char buf[10];
char name[10];
char password[10];
char flag[2];
//===接收客户端发来的要注册的用户名===
memset(buf,0,10);
recvbytes = recvfrom_client(p_client_sockfd_n,buf,10);
//===收到0字节表示客户端断开连接
if(recvbytes == 0)
{
printf("\n");
printf("---客户端client_sockfd=%d断开连接---\n",*p_client_sockfd_n);
printf("\n");
close(*p_client_sockfd_n);
pthread_exit(NULL);
}
//===确认要注册的用户名是否可用===
ret = LocateName_L(L,buf);
if( ret == 0)//要注册的用户名已存在
{
sendto_client(p_client_sockfd_n,"n",1);
printf("\n");
printf("---要注册的用户名:%s已存在---\n",buf);
printf("\n");
continue;
}
else//要注册的用户名可用
{
sendto_client(p_client_sockfd_n,"y",1);
memset(name,0,10);
strcpy(name,buf);//记录要注册的用户名
printf("\n");
printf("---用户名:%s可以使用---\n",name);
printf("\n");
}
//===接收客户端发来的密码===
memset(password,0,10);
recvbytes = recvfrom_client(p_client_sockfd_n,password,10);
//===收到0字节表示客户端断开连接
if(recvbytes == 0)
{
printf("\n");
printf("---客户端client_sockfd=%d断开连接---\n",*p_client_sockfd_n);
printf("\n");
close(*p_client_sockfd_n);
pthread_exit(NULL);
}
//===接收客户端发来的确认注册信息===
memset(flag,0,2);
recvbytes = recvfrom_client(p_client_sockfd_n,flag,2);
//===收到0字节表示客户端断开连接
if(recvbytes == 0)
{
printf("\n");
printf("---客户端client_sockfd=%d断开连接---\n",*p_client_sockfd_n);
printf("\n");
close(*p_client_sockfd_n);
pthread_exit(NULL);
}
if(flag[0] == 'y')
{
printf("客户端确认注册\n");
sendto_client(p_client_sockfd_n,"y",1);
//===保存用户注册信息===
strcpy(user.name,name);
strcpy(user.passwd,password);
user.mysockfd = *p_client_sockfd_n;
user.state = 0;
user.channel = -1;
//上快速互斥锁
if(pthread_mutex_lock(&mutex)<0)
{
perror("pthread_mutex_lock");
}
//===将用户注册信息添加到链表中===
ListInsert_L(&L,user);
printf("\n");
printf("将用户:%s注册信息添加到链表中\n",name);
printf("\n");
//===将用户注册信息保存到文件中===
uer2file(user);
printf("\n");
printf("将用户:%s注册信息保存到文件中\n",name);
printf("\n");
//解快速互斥锁
if(pthread_mutex_unlock(&mutex)!=0)
{
perror("pthread_mutex_unlock");
}
//打印注册成功的用户信息
printf("\n");
printf("---注册成功---\n");
printf("用户名:%s\n",user.name);
printf("密码:%s\n",user.passwd);
printf("\n");
}
if(flag[0] == 'n')
{
printf("---取消注册---\n");
sendto_client(p_client_sockfd_n,"n",1);
}
break;
}
return 0;
}
//=================================================
//===登录==========================================
struct user_msg log_in_server(int *p_client_sockfd_n)
{
struct user_msg user;
int recvbytes;
while(1)
{
char name[10];
char password[10];
char flag[2];
//===接收用户名===
memset(name,0,10);
recvbytes = recvfrom_client(p_client_sockfd_n,name,10);
//===收到0字节表示客户端断开连接
if(recvbytes == 0)
{
printf("\n");
printf("---客户端client_sockfd=%d断开连接---\n",*p_client_sockfd_n);
printf("\n");
close(*p_client_sockfd_n);
pthread_exit(NULL);
}
//===接收密码===
memset(password,0,10);
recvbytes = recvfrom_client(p_client_sockfd_n,password,10);
//===收到0字节表示客户端断开连接
if(recvbytes == 0)
{
printf("\n");
printf("---客户端client_sockfd=%d断开连接---\n",*p_client_sockfd_n);
printf("\n");
close(*p_client_sockfd_n);
pthread_exit(NULL);
}
//===校验用户名和密码信息===
if(LocateName_L(L,name)==0)//要登陆的用户名有注册
{
printf("---登陆用户名:%s有注册---\n",name);
//根据要登陆的用户名查链表获取用户密码
user=LocateUser_L(L,name);
if(strcmp(user.passwd,password) == 0)
{
printf("---登陆密码正确---\n");
sendto_client(p_client_sockfd_n,"y",1);
}
else
{
printf("---登陆密码不正确---\n");
sendto_client(p_client_sockfd_n,"n",1);
continue;
}
}
else//要登陆的用户名未注册
{
printf("---登陆用户名:%s未注册---\n",name);
sendto_client(p_client_sockfd_n,"n",1);
continue;
}
//===检查是否在其他地方登陆===
if(user.state == 1)
{
printf("---要登陆的用户名:%s在其他地方登陆---\n",name);
if(send(user.mysockfd,"!o",2,0)==-1)
perror("send");
}
//===更新用户信息===
user.mysockfd=*p_client_sockfd_n;
user.state=1;
user.channel=-1;
//上快速互斥锁
if(pthread_mutex_lock(&mutex)<0)
{
perror("pthread_mutex_lock");
}
update_list(&L,user);//更新要登陆的用户信息到链表中
//解快速互斥锁
if(pthread_mutex_unlock(&mutex)!=0)
{
perror("pthread_mutex_unlock");
}
chat_server(p_client_sockfd_n,user);//进入聊天模式
return user;
}
}
//=====================================================================
//===聊天==============================================================
int chat_server(int *p_client_sockfd_n,struct user_msg user)
{
#if 0
########################################################################
int i;
int len;
int running=1;
int recvsize;
char filepath[60];
int filesocket;
struct tm *pt;
time_t t;
char option;
char buf1[MAX_RECVSIZE];
char buf2[MAX_TEXTSIZE];
char id[10];
int destsocket;
//===上线通知===
memset(buf2,0,MAX_TEXTSIZE);
sprintf(buf2,"%s上线!\n",name);
len=strlen(buf2);
speak2all(L,buf2,len);
while(running)
{
memset(id,0,10);
memset(buf1,0,MAX_RECVSIZE);
memset(buf2,0,MAX_TEXTSIZE);
memset(filepath,0,60);
if((recvsize=recv(*p_client_sockfd_n,buf1,MAX_RECVSIZE,0))<=0)
{
perror("recv");
printf("用户%s掉线\n",name);
pthread_exit(NULL);
}
option=buf1[0];
switch(option)
{
case '#':
{
time(&t);
pt = localtime(&t);
sprintf(buf2,"%s对大家说:(%02d:%02d:%02d)\n%s\n",name,pt->tm_hour,pt->tm_min,pt->tm_sec,buf1+1);
len=strlen(buf2);
speak2all(L,buf2,len);
break;
}
case '$':
{
time(&t);
pt = localtime(&t);
for(i=1;((buf1[i]!=' ')&&(i<MAX_RECVSIZE));i++)
{
id[i-1]=buf1[i];
}
if((LocateName_L(L,id)<0)||(i==1)||(i>=MAX_RECVSIZE))
{
//printf("输入用户名不存在\n");
break;
}
destsocket=LocateUsersoket_L(L,id);
sprintf(buf2,"%s对你说:(%02d:%02d:%02d)\n%s\n",name,pt->tm_hour,pt->tm_min,pt->tm_sec,buf1+i);
len=strlen(buf2);
sendto_client(&destsocket,buf2,len+1);
break;
}
case ':':
{
if(buf1[1]=='0')
{
change_passwd(socketCtl2,name);
}
else if(buf1[1]=='1')
{
Getuseronline_client(L,*socketCtl2);
}
else if(buf1[1]=='2')
{
Getalluser_client(L,*socketCtl2);
}
else if(buf1[1]=='q')
{
running=0;
sendto_client(socketCtl2,"!q",3);
continue;
}
else if(buf1[1]=='y')
{
filesocket=atol(buf1+2);
sendto_client(&filesocket,"!y",3);
}
else if(buf1[1]=='n')
{
filesocket=atol(buf1+2);
sendto_client(&filesocket,"!n",3);
}
else if(buf1[1]=='s')
{
sendfile_client(*socketCtl2,destsocket);
}
else if(buf1[1]=='t')
{
//===更新被踢掉的用户信息===
user.state = 0;
//上快速互斥锁
if(pthread_mutex_lock(&mutex)<0)
{
perror("pthread_mutex_lock");
}
update_list(&L,user);//更被踢掉的用户信息到链表中
//解快速互斥锁
if(pthread_mutex_unlock(&mutex)!=0)
{
perror("pthread_mutex_unlock");
}
printf("---%s对应线程已退出---\n",name);
pthread_exit(NULL);
}
else if(buf1[1]=='g')
{
//group_chat(socketCtl2,name);
}
break;
}
default:
{
//printf("default\n");
break;
}
}
}
######################################################################################
#endif
int i;
int len;
int running=1;//运行标志
int recvbytes;
struct tm *pt;
time_t t;
char buf_recv[1024];
char buf_send[1024];
char option;
char dest_id[10];//记录目标用户名
int dest_socket;//记录聊天目标socket
char myname[10];
memset(myname,0,10);
strcpy(myname,user.name);
char file_from_path[1024];//文件来源路径
int file_to_socket;//文件接收目标socket
int file_from_socket;//文件接收目标socket
//---上线通知---------------------------------------------------------------
memset(buf_send,0,1024);
sprintf(buf_send,"%s上线!\n",myname);
len=strlen(buf_send);
speak2all(L,buf_send,len);
//--------------------------------------------------------------------------
while(running)
{
//---接收客户端发来的字符串信息(@aaa:hello)-----------------------------
memset(buf_recv,0,1024);
recvbytes = recvfrom_client(p_client_sockfd_n,buf_recv,1024);
//----------------------------------------------------------------------
//---判断客户端是否断开连接---------------------------------------------
if(recvbytes == 0) //收到0字节表示客户端断开连接
{
printf("\n");
printf("---客户端client_sockfd=%d断开连接---\n",*p_client_sockfd_n);
printf("\n");
//---更新掉线用户状态信息---
user.state=0;
if(pthread_mutex_lock(&mutex)<0)//上快速互斥锁
{
perror("pthread_mutex_lock");
}
update_list(&L,user);//更新掉线用户状态信息到链表中
if(pthread_mutex_unlock(&mutex)!=0)//解快速互斥锁
{
perror("pthread_mutex_unlock");
}
//---群发用户掉线通知---
memset(buf_send,0,1024);
sprintf(buf_send,"%s掉线!\n",myname);
len=strlen(buf_send);
speak2all(L,buf_send,len);
close(*p_client_sockfd_n);
pthread_exit(NULL);
}
//-------------------------------------------------------------------
//---从客户端发来的字符串信息中截取第一个标识字符(@aaa:hello)--------
option=buf_recv[0];
switch(option)//对标识字符进行判断
{
case '#'://#表示群聊
{
time(&t);
pt = localtime(&t);
sprintf(buf_send,"%s对大家说:(%02d:%02d:%02d)\n%s\n",myname,pt->tm_hour,pt->tm_min,pt->tm_sec,buf_recv+1);//+1去掉#号
len=strlen(buf_send);
speak2all(L,buf_send,len);
break;
}
case '@'://@表示私聊
{
time(&t);
pt = localtime(&t);
//---从客户端发来的字符串信息中截取目标用户名(@aaa:hello)---
memset(dest_id,0,10);
for(i=1;((buf_recv[i]!=':')&&(i<1024));i++)//遇到:结束
{
dest_id[i-1]=buf_recv[i];
}
//-----------------------------------------------
//---查看用户名是否存在----------------------------------
if((LocateName_L(L,dest_id)<0)||(i==1)||(i>=1204))
{
printf("输入用户名不存在\n");
break;
}
//根据目标用户确定目标socket
dest_socket=LocateUsersoket_L(L,dest_id);
//截取聊天信息加1去掉冒号(@aaa:hello) buf_recv+i+1
sprintf(buf_send,"%s对你说:(%02d:%02d:%02d)\n%s\n",myname,pt->tm_hour,pt->tm_min,pt->tm_sec,buf_recv+i+1);
len=strlen(buf_send);
sendto_client(&dest_socket,buf_send,len);
break;
}
case '$'://$表示要发送文件
{
//---从客户端发来的字符串信息中截取目标用户名($aaa:hello.txt)---
memset(dest_id,0,10);
for(i=1;((buf_recv[i]!=':')&&(i<1024));i++)//遇到:结束
{
dest_id[i-1]=buf_recv[i];
}
//-----------------------------------------------
//---查看目标用户名是否存在--------------------------
if((LocateName_L(L,dest_id)<0)||(i==1)||(i>=1204))
{
printf("输入用户名不存在\n");
break;
}
//根据目标用户名确定文件接受目标file_to_socket
file_to_socket=LocateUsersoket_L(L,dest_id);
//截取要文件来源路径加1去掉冒号($aaa:hello.txt) buf_recv+i+1
strcpy(file_from_path,buf_recv+i+1);//保存文件来源路径
sprintf(buf_send,"!%d!%s向你发送文件:%s 是否接受(y/n)?\n",*p_client_sockfd_n,myname,buf_recv+i+1);
//sprintf(buf_send,"!e!%s向你发送文件:%s 是否接受(y/n)?\n",myname,buf_recv+i+1);
len=strlen(buf_send);
sendto_client(&file_to_socket,buf_send,len);
break;
}
case ':'://:表示功能选项
{
if(buf_recv[1]=='1')//客户端要查看在线用户
{
Getuseronline_client(L,*p_client_sockfd_n);
}
else if(buf_recv[1]=='2')//客户端要查看所有用户
{
Getalluser_client(L,*p_client_sockfd_n);
}
else if(buf_recv[1]=='3')//客户端要修改密码
{
change_passwd(p_client_sockfd_n,myname);
}
else if(buf_recv[1]=='4')//客户端要查看聊天记录
{
sendto_client(p_client_sockfd_n,"!c",3);
}
else if(buf_recv[1]=='5')
{
//group_chat(socketCtl2,myname);//群组聊天
}
else if(buf_recv[1]=='6')
{
running=0;
sendto_client(p_client_sockfd_n,"!q",3);
continue;
}
else if(buf_recv[1]=='y')//文件接收者发来(:y1)
{
file_from_socket=atol(buf_recv+2);
sendto_client(&file_from_socket,"!y",3);
}
else if(buf_recv[1]=='n')
{
//filesocket=atol(buf_recv+2);
//sendto_client(&filesocket,"!n",3);
}
else if(buf_recv[1]=='s')
{
sendfile_client(*p_client_sockfd_n,file_to_socket);
}
else if(buf_recv[1]=='t')
{
//===更新被踢掉的用户信息===
user.state = 0;
//上快速互斥锁
if(pthread_mutex_lock(&mutex)<0)
{
perror("pthread_mutex_lock");
}
update_list(&L,user);//更被踢掉的用户信息到链表中
//解快速互斥锁
if(pthread_mutex_unlock(&mutex)!=0)
{
perror("pthread_mutex_unlock");
}
printf("---%s对应线程已退出---\n",myname);
pthread_exit(NULL);
}
break;
}
default:
{
printf("---用户:%s发来无法识别的聊天信息---\n",myname);
break;
}
}
//sendto_client(p_client_sockfd_n,buf_chat,recvbytes);
}
return 0;
}
//======================================================================
//===文件收发函数=========================================================
void sendfile_client(int file_from_socket,int file_to_socket)
{
printf("---服务器正在发送文件---\n");
sendto_client(&file_to_socket,"!j",3);
sleep(1);
int recvsize;
char recvbuf[1024];
while(1)
{
memset(recvbuf,0,1024);
if((recvsize=recv(file_from_socket,recvbuf,1024,0))<0)
{
perror("recv");
}
sendto_client(&file_to_socket,recvbuf,recvsize);
if(!strncmp(recvbuf,"end",3))
{
break;
}
}
printf("---文件发送完毕---\n");
}
//===========================================================
//===客户端要获取在线用户信息=================================================
void Getuseronline_client(LinkList L,int p_client_sockfd_n)
{
int len;
char buf[500];
memset(buf,0,500);
LinkList p;
p=L->next;
sprintf(buf,"%s\n","~~~~~~在线用户~~~~~~");
while(p!=NULL)
{
if(p->user.state==1)
{
sprintf(buf,"%s%s\n",buf,p->user.name);
}
p=p->next;
}
sprintf(buf,"%s%s\n",buf,"~~~~~~~~~~~~~~~~~~~~");
len=strlen(buf);
if(send(p_client_sockfd_n,buf,len,0)==-1)
perror("send");
}
//===================================================================
//===客户端要获取所有用户信息========================================
void Getalluser_client(LinkList L,int p_client_sockfd_n)
{
int len;
char buf[500];
memset(buf,0,500);
LinkList p;
p=L->next;
sprintf(buf,"%s\n","~~~~~~所有用户~~~~~~");
while(p!=NULL)
{
sprintf(buf,"%s%s\n",buf,p->user.name);
p=p->next;
}
sprintf(buf,"%s%s\n",buf,"~~~~~~~~~~~~~~~~~~~~");
len=strlen(buf);
if(send(p_client_sockfd_n,buf,len,0)==-1)
perror("send");
}
//===============================================================
//===客户端要修改密码==========================================
static void change_passwd(int *p_client_sockfd_n,char myname[])
{
sendto_client(p_client_sockfd_n,"!m",3);
struct user_msg user;
user=LocateUser_L(L,myname);
int sendbytes;
char buf_recv[1024];
char buf_send[1024];
while(1)
{
printf("---用户:%s修改密码中---\n",myname);
//---发送提示信息到客户端----------------------------
memset(buf_send,0,1024);
strcpy(buf_send,"---修改密码中---\n");
sendbytes=strlen(buf_send);
sendto_client(p_client_sockfd_n,buf_send,sendbytes);
//---------------------------------------------------
//---发送提示信息到客户端----------------------------
memset(buf_send,0,1024);
strcpy(buf_send,"---请输入旧密码---\n");
sendbytes=strlen(buf_send);
sendto_client(p_client_sockfd_n,buf_send,sendbytes);
//---------------------------------------------------
//---接收客户端发来的旧密码---------------------------
memset(buf_recv,0,1024);
recvfrom_client(p_client_sockfd_n,buf_recv,1024);
//----------------------------------------------------
//---验证旧密码-----------------------------------------
if(strcmp(user.passwd,buf_recv)==0)
{
//---发送提示信息到客户端----------------------------
memset(buf_send,0,1024);
strcpy(buf_send,"---旧密码验证正确---\n");
sendbytes=strlen(buf_send);
sendto_client(p_client_sockfd_n,buf_send,sendbytes);
//---------------------------------------------------
break;
}
else
{
//---发送提示信息到客户端----------------------------
memset(buf_send,0,1024);
strcpy(buf_send,"---旧密码错误,请重新输入旧密码---\n");
sendbytes=strlen(buf_send);
sendto_client(p_client_sockfd_n,buf_send,sendbytes);
//---------------------------------------------------
continue;
}
//--------------------------------------------------------
}
//---发送提示信息到客户端----------------------------
memset(buf_send,0,1024);
strcpy(buf_send,"---请输入新密码---\n");
sendbytes=strlen(buf_send);
sendto_client(p_client_sockfd_n,buf_send,sendbytes);
//---------------------------------------------------
//---接收客户端发来的新密码---------------------------
memset(buf_recv,0,1024);
recvfrom_client(p_client_sockfd_n,buf_recv,1024);
//----------------------------------------------------
memset(user.passwd,0,10);
strcpy(user.passwd,buf_recv);
//-------------------更新用户信息------------------------
if(pthread_mutex_lock(&mutex)<0)
{
perror("pthread_mutex_lock");
}
update_list(&L,user);//更新用户信息到链表中
update_file(L);//更新用户信息文件
if(pthread_mutex_unlock(&mutex)!=0)
{
perror("pthread_mutex_unlock");
}
//--------------------------------------------------------------
//---发送提示信息到客户端----------------------------
memset(buf_send,0,1024);
strcpy(buf_send,"---修改密码成功---");
sendbytes=strlen(buf_send);
sendto_client(p_client_sockfd_n,buf_send,sendbytes);
//---------------------------------------------------
}
//======================================================================
//===群聊函数==========================================================
void speak2all(LinkList L,char *buf,int len)
{
LinkList p;
p=L->next;
while (p!=NULL)
{
if(p->user.state==1)
{
if(send((p->user.mysockfd),buf,len,0)==-1)
perror("send");
}
p=p->next;
}
}
//====================================================================
//===从指定客户端接收一条信息==========================================
int recvfrom_client(int *p_client_sockfd_n,char *buf,int length)
{
int recvbytes;
recvbytes = recv(*p_client_sockfd_n,buf,length,0);
if(recvbytes == -1)
{
perror("recv");
exit(1);
}
printf("\n");
printf("---收到一条[%d]字节的信息(信息来源:client_sockfd=%d的客户端)---\n",recvbytes,*p_client_sockfd_n);
printf("信息内容:%s\n",buf);
printf("\n");
return recvbytes;
}
//====================================================================
//===发送一条信息给指定客户端===================================
int sendto_client(int *p_client_sockfd_n,char *buf,int length)
{
int sendbytes;
sendbytes = send(*p_client_sockfd_n,buf,length,0);
if(sendbytes == -1)
{
perror("recv");
exit(1);
}
printf("\n");
printf("---发送一条[%d]字节的信息(信息目的:client_sockfd=%d客户端)---\n",sendbytes,*p_client_sockfd_n);
printf("信息内容:%s\n",buf);
printf("\n");
return sendbytes;
}
//====================================================================
client.c
//===包含的头文件=========
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
//=======================
//===定义的宏============
//#define MSG_MAXSIZE 1024//消息队列正文最大字节数
//#define REC_PATH "/home/"
//=======================
//===定义的全局变量=========
struct message
{
long msg_type;
char msg_text[1024];
};
int client_sockfd;
int qid_msg;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//mutex互斥锁
char default_path[50];//用户默认路径
char chatdata_path[50];//聊天记录二进制文件存放路径
char chatdata_txt_path[50];//聊天记录存文本文件放存放路径
char file_from_socket[6];//保存文件来源file_from_socket
volatile int confirm,yes,no,enable,inputdone,changeflag,chating;//标志位
//==================================
//===函数声明=======================
void primary_menu(void);
void paint_primary_menu(void);
void pthread_client(void);
int regist_client(void);
int sendto_server(char *buf,int length);
int recvfrom_server(char *buf,int length);
int log_in_client(void);
int chat_client(void);
static void log_out(void);
void callmenu(int signo);
void view_chat(void);
//==================================
//===主函数===============
int main(int argc,char *argv[])
{
//---参数合法性判断----------------------------
if(argc<4)
{
printf("\n");
printf("---请按以下示例输入---\n");
printf("./client_cwy 127.0.0.1 3333 11\n");
printf("\n");
exit(1);
}
//---------------------------------------------
pthread_mutex_init(&mutex,NULL);//初始化互斥锁
//---创建消息队列------------------------------------------
key_t key;
key = (key_t)atol(argv[3]);//通过main函数参数传进消息队列ID
qid_msg = msgget(key,IPC_CREAT|0666);
if(qid_msg == -1)
{
perror("msgget");
exit(1);
}
printf("\n");
printf("---创建消息队列成功qid_msg=%d---\n",qid_msg);
printf("\n");
//-------------------------------------------------------------
//---创建socket------------------------------------------------
client_sockfd = socket(AF_INET,SOCK_STREAM,0);
if(client_sockfd == -1)
{
perror("socket");
exit(1);
}
printf("\n");
printf("---创建socket成功!client_sockfd=%d---\n",client_sockfd);
printf("\n");
//-------------------------------------------------------------------------
//---配置服务器地址结构体--------------------------------------------------
struct sockaddr_in server_sockaddr;
unsigned short int port;
port = (unsigned short int)atol(argv[2]);//通过main函数参数传进服务器端口号//atol()能将字符串转换成长整形数
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(port);//htons()主机字节顺序转换为网络字节顺序(对无符号短型进行操作2bytes)
//inet_addr()能将一个用数字和点表示IP 地址的字符串转换成一个无符号长整型。
//htonl()主机字节顺序转换为网络字节顺序(对无符号长型进行操作4bytes)
server_sockaddr.sin_addr.s_addr = inet_addr(argv[1]);//通过main函数参数传进服务器IP
bzero(&(server_sockaddr.sin_zero),8);//字节清零补齐保证sockaddr_in和sockaddr大小一样
//------------------------------------------------------------------------------------
//---连接服务器---------------------------------------------------------------------------
int ret;
ret = connect(client_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr));
if(ret == -1)
{
perror("connect");
exit(1);
}
printf("\n");
printf("---连接服务器成功!---\n");
printf("\n");
//-----------------------------------------------------------------------------------------
primary_menu();//进入主菜单
//exit(0);
}
//======================================================
//===画主菜单函数========================================
void paint_primary_menu(void)
{
printf("\n");
printf("======主菜单======\n");
printf("[1]注册 \n");
printf("[2]登录 \n");
printf("[3]下线 \n");
printf("==================\n");
printf("\n");
}
//========================================================
//===主菜单===============================================
void primary_menu(void)
{
char buf_option[10];
char option;
while(1)
{
paint_primary_menu();//画主菜单
//---接收键盘输入------------------
memset(buf_option,0,10);
fflush(stdin);//清缓存
printf("请输入数字1-3进行选择:");
scanf("%s",buf_option);
//--------------------------------
option = buf_option[0];
switch (option)
{
case '1':
{
printf("\n");
printf("---注册中---\n");
printf("\n");
sendto_server("1",1);
regist_client();//注册新的用户
break;
}
case '2':
{
printf("\n");
printf("---登陆中---\n");
printf("\n");
sendto_server("2",1);
log_in_client();//用户登陆
break;
}
case '3':
{
printf("\n");
printf("---退出---\n");
printf("\n");
sendto_server("3",1);
log_out();//退出客户端
break;
}
default:
{
printf("\n");
printf("---输入无效,请重新输入数字1-3!---\n");
printf("\n");
break;
}
}
}
}
//======================================================
//===客户端退出函数================================================OK
static void log_out(void)
{
close(client_sockfd);
pthread_mutex_destroy(&mutex);
if(msgctl(qid_msg, IPC_RMID, 0) == -1)//删除消息队列
{
fprintf(stderr, "msgctl failed\n");
exit(EXIT_FAILURE);
}
exit(0);
}
//==========================================================
//===注册新的用户=======================================
int regist_client(void)
{
char name[10];
char password[10];
char buf1[10];
char buf2[10];
char flag[2];
int len;
while(1)
{
//---输入要注册的用户名--------
memset(name,0,10);
fflush(stdin);//清输入缓存
printf("请输入要注册的用户名:");
scanf("%s",name);
//-----------------------------
//---发送要注册的用户名给服务器---
len=strlen(name);
sendto_server(name,len);
//-------------------------------
//---等待服务器确认要注册的用户名是否可用----
memset(flag,0,2);
recvfrom_server(flag,1);
if(flag[0] == 'n')//要注册的用户名不可用返回重新输入
{
printf("\n");
printf("---要注册的用户名:%s已被注册,请重新输入---\n",name);
printf("\n");
continue;
}
if(flag[0] == 'y')//要注册的用户名可以使用继续往下走
{
printf("\n");
printf("---要注册的用户名:%s可以使用---\n",name);
printf("\n");
}
//-------------------------------------------------------
//---输入密码--------------------------------------------
loop_passsword1:
//---第一次输入密码---
memset(buf1,0,10);
fflush(stdin);//清缓存
printf("请输入密码:");
scanf("%s",buf1);
//--------------------
//---第二次输入密码---
memset(buf2,0,10);
fflush(stdin);//清缓存
printf("请再次输入密码:");
scanf("%s",buf2);
//--------------------
//---比较两次输入的密码是否一致---
if(strcmp(buf1,buf2) != 0)
{
printf("\n");
printf("---两次输入密码不一致请重新输入---\n");
printf("\n");
goto loop_passsword1;//两次输入密码不一致返回重新输入
}
//
//---发送密码到服务器-------
strcpy(password,buf1);
len=strlen(password);
sendto_server(password,len);
//--------------------------
//---输入确认注册信息-------
memset(flag,0,2);
fflush(stdin);//清输入缓存
printf("确认注册?y or n:");
scanf("%s",flag);
//---------------------------
//---将确认注册信息发送给服务器---
if(flag[0] == 'n')
{
sendto_server("n",1);
}
if(flag[0] == 'y')
{
sendto_server("y",1);
}
//-------------------------------
//---等待服务器确认注册成功---
memset(flag,0,2);
recvfrom_server(flag,1);
if(flag[0] == 'n')
{
printf("\n");
printf("---取消注册---\n");
printf("\n");
}
if(flag[0] == 'y')
{
printf("\n");
printf("---注册成功---\n");
printf("\n");
printf("请记住您的用户名和密码\n");
printf("\n");
printf("用户名:%s\n",name);
printf("密码:%s\n",password);
printf("\n");
}
//---------------------------------------------
break;
}
return 0;//返回主菜单
}
//======================================================
//===登陆===============================================
int log_in_client(void)
{
int len;
int i;
char name[10];
char password[10];
char flag[2];
while(1)
{
//---将要登陆的用户名发送给服务器---
memset(name,0,10);
fflush(stdin);//清输入缓存
printf("请输入要登陆的用户名:");
scanf("%s",name);
len=strlen(name);
if(len == 0)
{
printf("---未输入要登陆的用户名---\n");
continue;
}
else
{
sendto_server(name,len);
}
//-------------------------------
//---将要登陆的密码发送给服务器----
loop_passsword2:
memset(password,0,10);
fflush(stdin);//清输入缓存
printf("请输入要登陆的密码:");
scanf("%s",password);
len=strlen(password);
if(len == 0)
{
printf("---未输入要登陆的密码---\n");
goto loop_passsword2;
}
else
{
sendto_server(password,len);
}
//----------------------------------------------------------
//---等待服务器响应--------------------------------
memset(flag,0,2);
recvfrom_server(flag,1);
if(flag[0] == 'n')
{
printf("---密码或用户名错误,请重新输入---\n");
continue;
}
if(flag[0] == 'y')
{
printf("---登录成功---\n");
}
//--------------------------------------------------
//---登录成功创建用户目录及聊天记录文档---
//---创建用户目录-------------------------
sprintf(default_path,"%s",name);
//sprintf(default_path,"%s%s",REC_PATH,name);
if(mkdir(default_path,0777)<0)
{
perror("mkdir");
}
//--------------------------------------
//---创建聊天记录二进制文件-------------
int chat_fd;
sprintf(chatdata_path,"%s%s",default_path,"/chat_data");
if((chat_fd=open(chatdata_path,O_CREAT|O_APPEND|O_RDWR,0666))<0)
{
perror("open");
}
//--------------------------------------
//---创建聊天记录文本文件----------------
sprintf(chatdata_txt_path,"%s%s",default_path,"/chat_data.txt");
FILE *chat_fp;
//追加打开一个文本文件,并在文件末尾写数据,若不存在则创建
if((chat_fp=fopen(chatdata_txt_path,"at+")) == NULL)
{
printf("cannot open file\n");
exit(1);
}
if(fputs("-----------新登录---------------",chat_fp) == EOF)//测试
{
printf("file write error\n");
}
if(fputs("\n",chat_fp) == EOF)
{
printf("file write error\n");
}
fclose(chat_fp);//记得关掉文件否则写入的信息不保存
//-----------------------------------------
printf("---当前用户:%s聊天信息保存在%s目录下---\n",name,default_path);
//-----------------------------------------------------------------------------
chat_client();//进入聊天模式
return 0;
}
}
//=====================================================================================
//===聊天===============================================================================
int chat_client(void)
{
view_chat();//进入聊天模式后先查看以前的聊天信息
//---创建子进程接受服务器的信息转发给显示端-------------------
pthread_t pid;
if(pthread_create(&pid,NULL,(void *)pthread_client,NULL) < 0)
{
perror("pthread_create");
exit(1);
}
printf("---创建子进程接受服务器的信息转发给显示端---\n");
//-----------------------------------------------------------
//---主线程-接收键盘输入并发送给服务器-----------------------
int sendbytes;
char buf_chat[1024];
//char option;
//int i;
chating=1;
signal(SIGQUIT,callmenu);//ctrl+\ 呼唤菜单
while(chating)
{
//---从键盘接收输入聊天信息---
memset(buf_chat,0,1024);
fflush(stdin);//清输入缓存
printf("请输入聊天信息:");
scanf("%s",buf_chat);
sendbytes=strlen(buf_chat);
//-----------------------------
//---发送聊天信息到服务器----------
sendto_server(buf_chat,sendbytes);
//---------------------------------
#if 0
*******************************************************************
//===文件发送========================
if(confirm==1)
{
option=buf[0];
switch(option)
{
case 'y':
{
yes=1;
continue;
}
case 'n':
{
no=1;
continue;
}
case '/':
{
memset(dest_path,0,50);
strcpy(dest_path,buf);
for(i=0;i<50;i++)
{
if(dest_path[i]=='\n')
dest_path[i]=0;
}
inputdone=1;
continue;
}
default:
{
printf("输入选项有误,请重新输入\n");
continue;
}
}
}
if(buf_chat[0] == '@')
{
for(i=1;((buf_chat[i]!=' ')&&(i<50));i++)
{
}
if((i==1)||(i>=50)||(buf_chat[i+1]==' '))
{
printf("输入格式有误,请重新输入\n");
continue;
}
strcpy(source_path,buf_chat+i+1);
for(i=0;i<50;i++)
{
if(source_path[i]=='\n')
source_path[i]=0;
}
}
//=================================================
************************************************************************
#endif
}
return 0;
}
//===================================================================
//===创建子进程接受服务器的信息转发给显示端====================================OK
void pthread_client(void)
{
int sendbytes,recvbytes;
int i;
int len;
char flag;
char buf_recv[1024];
struct message msg;
msg.msg_type = 1;//消息类型 1; 或者getpid();都可以
//char filename[50];
//---打开聊天记录文件----------------------------------------
int chat_fd;
if((chat_fd=open(chatdata_path,O_APPEND|O_WRONLY,0666))<0)//二进制文件
{
perror("open");
}
FILE *chat_fp;//文本文件
//-----------------------------------------------------------
chating=1;//表示聊天模式
while(chating)
{
//---接收服务器发来的信息-----------------------------
memset(buf_recv,0,1024);//清空数据收发缓存
recvbytes = recvfrom_server(buf_recv,1024);
//-----------------------------------------------------
//---确认服务器是否还在运行----------------------------
if(recvbytes == 0) //收到0字节表示与服务器断线
{
printf("\n");
printf("---与服务器断线---\n");
printf("\n");
log_out();
}
//-----------------------------------------------------
//---截取服务器发来的字符串信息中的特殊标识位(!t)-----------
flag=buf_recv[0];
if(flag == '!')
{
if(buf_recv[1]=='c')//查看聊天记录
{
printf("---查看聊天记录---\n");
view_chat();
}
if(buf_recv[1]=='m')//修改密码
{
printf("---修改密码中---\n");
printf("请输入旧密码:");
}
if(buf_recv[1]=='o')//在其他地方登陆
{
printf("---您已在其他地方登陆---\n");
log_out();
}
else if(buf_recv[1]=='t')//服务器要踢掉本用户
{
printf("---您已被服务器踢下线---\n");
sendto_server(":t",2);//告诉服务器退出对应处理客户端的子线程
log_out();//退出客户端
}
else if(buf_recv[1]=='q')//注销登录
{
chating = 0;
log_out();//退出客户端
}
if(buf_recv[1]=='j')//修改密码
{
printf("---接收文件中---\n");
//recv_file();
}
//---保存文件来源file_from_socket-------------------------------------
else if((buf_recv[1]>='0')&&(buf_recv[1]<='9'))
{
memset(file_from_socket,0,6);
for(i=1;i<1024;i++)
{
if(buf_recv[i]!='!')
{
file_from_socket[i-1]=buf_recv[i];
}
else if(buf_recv[i]=='!')
{
printf("---文件来源file_from_socket=%s---\n",file_from_socket+i+1);
//---将服务器发来的信息转发给显示窗口---------------------
memset(msg.msg_text,0,1024);
strcpy(msg.msg_text,buf_recv);
sendbytes = strlen(msg.msg_text);
if(msgsnd(qid_msg, (void *)&msg, sendbytes, 0) == -1)
{
perror("msgsnd");
exit(1);
}
//------------------------------------------------------
break;
}
}
continue;
}
//-------------------------------------------------------------
else if(buf_recv[1]=='y')
{
printf("对方已同意接收,正在发送...\n");
//send_file(1);
}
else if(buf_recv[1]=='n')
{
printf("---对方拒绝接收,已取消---\n");
//memset(source_path,0,50);
}
/*
else if(buf_recv[1]=='f')
{
recv_file();
}
else if(buf_recv[1]=='g')
{
memset(filename,0,50);
strcpy(filename,buf_recv+2);
sprintf(dest_path,"%s/%s",default_path,filename);//默认接收到的文件目录
recv_file();
}
else if(buf_recv[1]=='c')
{
while(changeflag);
}
*/
continue;
}
//--------------------------------------------------------
//---将服务器发来的信息转发给显示窗口---------------------
memset(msg.msg_text,0,1024);
strcpy(msg.msg_text,buf_recv);
sendbytes = strlen(msg.msg_text);
if(msgsnd(qid_msg, (void *)&msg, sendbytes, 0) == -1)
{
perror("msgsnd");
exit(1);
}
//------------------------------------------------------
//---将聊天信息写入文件中--------------------------------
if(pthread_mutex_lock(&mutex)<0)//上快速互斥锁
{
perror("pthread_mutex_lock");
}
len=strlen(buf_recv);
if(write(chat_fd,buf_recv,len)<0)//写入二进制文件
{
perror("write");
}
//---追加打开一个文本文件,并在文件末尾写数据,若不存在则创建----------
if((chat_fp=fopen(chatdata_txt_path,"at+")) == NULL)
{
printf("cannot open file\n");
exit(1);
}
if(fputs(buf_recv,chat_fp) == EOF)//写入文本文件
{
printf("file write error\n");
}
if(fputs("\n",chat_fp) == EOF)//加'\n'才会写进去
{
printf("file write error\n");
}
fclose(chat_fp);//写完要记得关闭文件否则文件为空
//-----------------------------------------------------------------
if(pthread_mutex_unlock(&mutex)!=0)//解快速互斥锁
{
perror("pthread_mutex_unlock");
}
//-------------------------------------------------------
//printf("---转发一条信息到显示端---\n");
}
}
//======================================================
//===查看聊天记录===========================================
void view_chat(void)
{
int readsize;
int chat_fd;
char buf[1024];
memset(buf,0,1024);;
struct message msg1;
msg1.msg_type=1;
memset(msg1.msg_text,0,1024);
if((chat_fd=open(chatdata_path,O_RDONLY,0666))<0)
{
perror("open");
}
system("clear");//清屏
strcpy(msg1.msg_text,"————————————————聊天记录起始————————————————\n");
if(msgsnd(qid_msg, (void *)&msg1, 1024, 0) == -1)
{
perror("msgsnd");
exit(-1);
}
while(1)
{
memset(msg1.msg_text,0,1024);
if(pthread_mutex_lock(&mutex)<0)
{
perror("pthread_mutex_lock");
}
if((readsize=read(chat_fd,msg1.msg_text,1024))<0)
{
perror("read");
}
if(pthread_mutex_unlock(&mutex)!=0)
{
perror("pthread_mutex_unlock");
}
if(readsize==0)
{
break;
}
if(msgsnd(qid_msg, (void *)&msg1, 1024, 0) == -1)
{
perror("msgsnd");
exit(-1);
}
}
memset(msg1.msg_text,0,1024);
strcpy(msg1.msg_text,"————————————————聊天记录结束————————————————\n");
if(msgsnd(qid_msg, (void *)&msg1, 1024, 0) == -1)
{
perror("msgsnd");
exit(-1);
}
}
//====================================================================================
//===功能菜单===============================================
void callmenu(int signo)
{
printf("---AAAAAAAAAAAAA---\n");
//char option;
//char c;
//int menuflag=1;
struct message msg;
msg.msg_type = 3;
printf("---进入功能菜单模式---\n");
printf("---请输入(:1-7)选择功能记得数字前加冒号哦!---\n");
//fflush(stdout);//上面那句如果没有\n 需要清缓存,才能把上面那句打印出来
strcpy(msg.msg_text,"fun_menu");
if(msgsnd(qid_msg, (void *)&msg, 1024, 0) == -1)
{
perror("msgsnd");
}
#if 0
********************************************************************
setbuf(stdin,NULL);
while(menuflag)
{
printf("Notes:进入功能菜单模式\n");
strcpy(msg.msg_text,"menu");
if(msgsnd(qid_msg, (void *)&msg, 1024, 0) == -1)
{
perror("msgsnd");
}
printf(":");
option=getchar();
setbuf(stdin,NULL);
switch (option)
{
case '1'://查询在线好友
{
c=getchar();//清理输入缓存中的数据
sendto_server(":1",2);
break;
}
case '2'://查询所有好友
{
c=getchar();//清理输入缓存中的数据
sendto_server(":2",2);
break;
}
case '3'://修改密码
{
c=getchar();//清理输入缓存中的数据
sendto_server(":3",2);
//change_passwd();
break;
}
case '4'://查看当前聊天记录
{
c=getchar();//清理输入缓存中的数据
view_chat();
break;
}
case '5'://群组聊天
{
c=getchar();//清理输入缓存中的数据
//group();
menuflag=0;//退出菜单
printf("---进入群组聊天模式\n");
continue;
}
case '6'://退出菜单
{
menuflag=0;//退出菜单进入聊天模式
printf("---退出菜单进入聊天模式---\n");
continue;
}
case '7'://注销用户
{
menuflag=0;
chating=0;//退出聊天模式
sendto_server(":q",2);//通知服务器本注销用户
printf("---注销用户---\n");
continue;
}
default:
{
printf("输入选项有误,请重新输入\n");
break;
}
}
}
*************************************************************************
#endif
}
//================================================================
//===发送信息给服务器=========================OK
int sendto_server(char *buf,int length)
{
int sendbytes;
sendbytes = send(client_sockfd,buf,length,0);
if(sendbytes == -1)
{
perror("send");
exit(1);
}
//printf("\n");
//printf("---发送一条[%d]字节的信息到服务器---\n",sendbytes);
//printf("信息内容:%s\n",buf);
//printf("\n");
return sendbytes;
}
//=========================================
//===从服务器接收信息=======================OK
int recvfrom_server(char *buf,int length)
{
int recvbytes;
recvbytes = recv(client_sockfd,buf,length,0);
if(recvbytes == -1)
{
perror("recv");
exit(1);
}
//printf("\n");
//printf("---收到一条[%d]字节的信息自服务器---\n",recvbytes);
//printf("信息内容:%s\n",buf);
//printf("\n");
return recvbytes;
}
//=========================================
display.c
//===包含的头文件=========
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>
#include <sys/types.h>
#include <string.h>
#include <pthread.h>
//=======================
//===定义的宏============
//#define MSG_MAXSIZE 1024//消息最大字节数
//=======================
//===定义的全局变量=========
struct message
{
long msg_type;
char msg_text[1024];
};
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//mutex互斥锁
//==================================
//===函数声明================
void tip_menu();
void fun_menu(void);
void group_menu(void);
void pthread_display_menu(void);
//===============================
//===主函数===========================
int main(int argc,char *argv[])
{
//参数合法性判断
if(argc<2)
{
printf("---请按以下示例输入---\n");
printf("./display 11\n");
exit(1);
}
//signal(SIGQUIT,tip_menu);//测试不OK改成子线程处理
//---创建显示端-显示菜单-子线程---------------------
int ret;
pthread_t pid_display_menu;//显示端-显示菜单-子线程ID号
ret = pthread_create(&pid_display_menu,NULL,(void *)pthread_display_menu,NULL);
if(ret != 0)
{
perror("pthread_creat");
exit(1);
}
printf("\n");
printf("---显示端-显示菜单-子线程创建成功!---\n");
printf("\n");
//------------------------------------------------------
//---创建消息队列--------------------------------------
struct message msg;
key_t key;
int qid_msg;
key = (key_t)atol(argv[1]);
qid_msg = msgget(key,IPC_CREAT|0666);
if(qid_msg == -1)
{
perror("msgget");
exit(1);
}
printf("---创建消息队列成功qid_msg=%d---\n",qid_msg);
//----------------------------------------------------------
while(1)
{
//循环读取消息
//printf("---准备从客户端接收消息---\n");
memset(msg.msg_text,0,1024);//清空消息正文缓存
//---读取消息队列中的第一个消息(阻塞)-------------
if(msgrcv(qid_msg, (void *)&msg, 1024, 0, 0) == -1)
{
printf("---客户端已退出---\n");
exit(1);
}
//-------------------------------------------------
if(!strncmp(msg.msg_text, "fun_menu",4))
{
fun_menu();
continue;
}
if(!strncmp(msg.msg_text, "group",5))
{
group_menu();
continue;
}
printf("%s\n", msg.msg_text);//将读取到的消息显示在显示窗口
}
}
//====================================
//===显示端-显示菜单-子线程处理函数======================
void pthread_display_menu(void)
{
char buf_option[10];
while(1)
{
printf("\n\n");
tip_menu();//显示提示菜单
memset(buf_option,0,10);
fflush(stdin);//清缓存
scanf("%s",buf_option);
printf("\n\n");
}
}
//=============================================
//===提示菜单================================
void tip_menu()
{
system("clear");//清屏
printf("======操作提示======\n");
printf("#msg(公聊)\n");
printf("@ID:msg(私聊)\n");
printf("$ID:file(传文件)\n");
printf("ctrl+\\(唤出功能菜单)\n");
printf("====================\n");
}
//========================================
//===功能菜单==================================
void fun_menu(void)
{
system("clear");//清屏
printf("======功能菜单======\n");
printf("[1]查询在线好友\n");
printf("[2]查询所有好友\n");
printf("[3]修改密码\n");
printf("[4]查看当前聊天记录\n");
printf("[5]群组聊天\n");
printf("[6]注销\n");
printf("===================\n");
}
//========================================
//===群组菜单=============================
void group_menu(void)
{
system("clear");//清屏
printf("======群组======\n");
printf("[1]ETC\n");
printf("[2]嵌入式\n");
printf("[3]返回\n");;
printf("================\n");
}
//========================================
linklist.c
//===包含的头文件==========
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//=========================
//===定义的全局变量========
typedef int ElemType,Status;
struct user_msg
{
char name[10];
char passwd[10];
int state;
int channel;
unsigned short mysockfd;
};
typedef struct LNode{
struct user_msg user;
struct LNode *next;
}LNode,*LinkList;
//=========================================
//===创建一个带头结点的链表=========================测试OK
void CreateList_L(LinkList *L)//头指针的地址传进来
{
*L=(LinkList)malloc(sizeof(LNode));
(*L)->next=NULL;
}
//=========================================
//===将用户信息插入到链表的结尾===========================测试OK
Status ListInsert_L (LinkList *L,struct user_msg user)//头指针的地址传进来
{
LinkList p = *L,q;
if(!p) return -1;
while(p!=NULL)
{
q=p;
p=p->next;
}
p=(LinkList)malloc(sizeof(LNode));
p->user=user;
p->next=q->next;
q->next=p;
return 0;
}
//====================================================
//===查询是否存在该用户名==================================测试OK
int LocateName_L(LinkList L, char name[])
{
LinkList p;
p=L->next;//指向第一个节点
while (p!=NULL)
{
if(!(strcmp(p->user.name,name)))
{
return 0;//存在该用户名
}
p=p->next;
}
return -1;//不存在该用户名
}
//==============================================================
//===将新用户信息添加到文件末尾========================测试OK
void uer2file(struct user_msg user)
{
//===写入user_data二进制文件中===
FILE *fp1;
//追加打开一个二进制文件,并在文件末尾写数据,若不存在则创建
if((fp1=fopen("user_data","ab+"))==NULL)
{
printf("cannot open file\n");
exit(1);
}
if(fwrite(&user,sizeof(struct user_msg),1,fp1)!=1)
{
printf("file write error\n");
}
fclose(fp1);
//===========================================
//===写到user_view.txt文本中=================
FILE *fp2;
//追加打开一个文本文件,并在文件末尾写数据,若不存在则创建
if((fp2=fopen("user_view.txt","at+")) == NULL)
{
printf("cannot open file\n");
exit(1);
}
//写入用户名
if(fputs("用户名:",fp2) == EOF)
{
printf("file write error\n");
}
if(fputs(user.name,fp2) == EOF)
{
printf("file write error\n");
}
if(fputs("\t",fp2) == EOF)
{
printf("file write error\n");
}
//写入密码
if(fputs("密码:",fp2) == EOF)
{
printf("file write error\n");
}
if(fputs(user.passwd,fp2) == EOF)
{
printf("file write error\n");
}
if(fputs("\n",fp2) == EOF)
{
printf("file write error\n");
}
fclose(fp2);
//=============================
}
//====================================================
//===读取文件的内容到链表===================================
void file2list(LinkList *L)//头指针的地址传进来
{
int i;
FILE *fp2;
struct user_msg user;
//读写打开一个二进制文件,允许读,或在文件末追加数据,若不存在则创建
fp2=fopen("user_data","ab+");
for(i=0;i<50;i++)
{
if(fread(&user,sizeof(struct user_msg),1,fp2)!=1)
break;
ListInsert_L(L,user);
}
fclose(fp2);
}
//==========================================================
//===根据用户名查链表返回user结构体==================================//
struct user_msg LocateUser_L(LinkList L, char name[])
{
LinkList p;
p=L->next;
while (p!=NULL)
{
if(!(strcmp(p->user.name,name)))
break;
p=p->next;
}
if(p!=NULL)
{
return(p->user);
}
exit(-1);
}
//========================================================//
//===更新user_data==========================================
void update_file(LinkList L)
{
FILE *fp1;
LinkList p;
p=L->next;
if((fp1=fopen("user_data","wb"))==NULL)
{
printf("cannot open file\n");
return;
}
while (p!=NULL)
{
if(p->user.state==1)
{
p->user.state=0;
uer2file(p->user);
p->user.state=1;
}
else
{
uer2file(p->user);
}
p=p->next;
}
}
//==========================================================
//===更新用户到链表==========================================
void update_list(LinkList *L,struct user_msg user1)
{
LinkList p;
p=(*L)->next;
while (p!=NULL)
{
if(!(strcmp(p->user.name,user1.name)))
break;
p=p->next;
}
if(p!=NULL)
{
p->user=user1;
}
}
//===========================================================
//===根据socket确定用户名====================================
struct user_msg Locatesocket_L(LinkList L, int socket)
{
LinkList p;
p=L->next;
while (p!=NULL)
{
if(p->user.mysockfd==socket)
break;
p=p->next;
}
if(p!=NULL)
{
return(p->user);
}
exit(-1);
}
//==========================================
//===根据用户名返回socket==================================
int LocateUsersoket_L(LinkList L, char name[])
{
LinkList p;
p=L->next;
while (p!=NULL)
{
if(!(strcmp(p->user.name,name)))
break;
p=p->next;
}
if(p!=NULL)
{
return(p->user.mysockfd);
}
else
{
return -1;
}
}
//==========================================================
//===求取用户个数===========================
int Length_L(LinkList L)
{
int i=0;
LinkList p;
if(!L)
return -1;
p=L->next;
while (p!=NULL)
{
p=p->next;
i++;
}
return i;
}
//=========================================================
//===从链表中删除指定用户==================================
Status ListDelete_L(LinkList *L,char name[])
{
LinkList p,q;
p=(*L)->next;
q=*L;
while(p!=NULL)
{
if(!(strcmp(p->user.name,name)))
break;
q=p;
p=p->next;
}
if(p==NULL)
{
return -1;
}
else//用户名在链表中
{
q->next=p->next;
free(p);
return 0;
}
}
//=========================================================
//===显示全部用户信息=================================
int Disp_L(LinkList L)
{
LinkList p;
if(!L)
return -1;
p=L->next;
printf("===============全部用户信息================\n");
printf("name\tpasswd\tstate\tsocket\tchannel\n");
while(p!=NULL)
{
printf("%s\t",p->user.name);
printf("%s\t",p->user.passwd);
printf("%d\t",p->user.state);
printf("%d\t",p->user.mysockfd);
printf("%d\n",p->user.channel);
p=p->next;
}
printf("===========================================\n");
return 0;
}
//==========================================================
//===获取当前在线用户并打印=================================
void Getstate_L(LinkList L)
{
LinkList p;
p=L->next;
printf("=============在线用户信息==================\n");
printf("name\tpasswd\tstate\tsocket\tchannel\n");
while(p!=NULL)
{
if(p->user.state==1)
{
printf("%s\t",p->user.name);
printf("%s\t",p->user.passwd);
printf("%d\t",p->user.state);
printf("%d\t",p->user.mysockfd);
printf("%d\n",p->user.channel);
}
p=p->next;
}
printf("===========================================\n");
}
//==========================================================