Linux下 基于TCP协议下的C/S架构的网络聊天室 C程序

一、 实现目标

 

 

一个在 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 大侠,欢迎你来到咱们的聊天室”





#ifndef TCP_NET_SOCKET_H_

#define TCP_NET_SOCKET_H_


#include <stdio.h>
#include <sqlite3.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <semaphore.h>
#include <termios.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>


#define SERV_PORT 9000


/*********************注册登录**********************/
#define reg    1         //注册
#define log 2  //登录
#define forget          3  //忘记密码
#define exit            4  //退出
#define existing_acc    5  //账号已存在
#define logged_acc      6         //账号已登录
#define error           7    //账号或密码错误
#define log_success     8  //登录成功
#define reg_success     9  //注册成功
#define Exit 10  //退出
/**************************************************/


/******************聊天室功能**********************/
#define private_chat    11  //私聊
#define group_chat      12         //群聊
#define group_result    13  //群聊接受
#define file_transfer   14  //文件传输
#define online_member   15  //查看在线人数
#define expression      16  //表情
#define phrases         17  //常用语
#define motto 18  //个性签名
#define motto_change    19  //更改个性签名
#define like            20        //点赞
#define Vip             21        //开会员
#define Shutup          22        //禁言
#define lifted          23  //解禁
#define kick            24        //踢人
/**************************************************/




/****************服务器返回结果*******************/
#define vip_success     25  //开会员成功
#define Shutup_success  26  //禁言成功
#define Send_success    27         //发送信息成功
#define Send_error      28         //发送信息失败
#define kick_fail       29        //踢人失败
#define kick_success    30        //踢人成功
#define like_success    31        //点赞成功
#define change_success  32  //更改个性签名成功
/************************************************/


extern int tcp_init();
extern int tcp_accept(int sfd);
extern int tcp_connet();
extern void signalhandler(void);
extern int mygetch();
extern int gettime();
extern char* nowtime();


#endif
/*********************************************************************
File Name:               tcp_net_socket.c
Author:                             date:
Description:            
Fuction List: int tcp_init() //用于初始化操作
int tcp_accept(int sfd) //用于服务器的接收
int tcp_connect(const char* ip) //用于客户端的连接
void signalhandler(void) //用于信号处理,让服务器在按下Ctrl+c或Ctrl+\时不会退出

********************************************************************/


//服务器端

#include "tcp_net_socket.h"
//用于初始化操作
int tcp_init()  
{
int sfd = socket(AF_INET, SOCK_STREAM, 0);     //创建套接字
if(sfd == -1)
{
perror("socket");
return -1;
}

int ret;
struct sockaddr_in serveraddr;


memset(&serveraddr,0,sizeof(struct sockaddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERV_PORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);

ret = bind(sfd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr));           //绑定
if(ret == -1)
{
perror("bind");
return -1;
}

ret = listen(sfd,10);           //监听它,并设置允许最大的连接数为10个
if(ret == -1)
{
perror("listen");
close(sfd);
return -1;
}

return sfd;
}


//用于服务器的接收
int tcp_accept(int sfd)
{
struct sockaddr_in clientaddr;
memset(&clientaddr, 0, sizeof(struct sockaddr));
int addrlen = sizeof(struct sockaddr);

//sfd接受客户端的连接,并创建新的socket为new_fd,将请求连接的客户端的ip、port保存在结构体clientaddr中
int new_fd = accept(sfd, (struct sockaddr*)&clientaddr, &addrlen);       
if(new_fd == -1)
{
perror("accept");
close(sfd);
return -1;
}
printf("%s %d success connet...\n", 
inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));

return new_fd;
}


//用于客户端的连接
int tcp_connect(const char* ip)
{
int ret;
int sfd = socket(AF_INET, SOCK_STREAM, 0);     //申请新的socket
if(sfd == -1)
{
perror("socket");
return -1;
}

struct sockaddr_in serveraddr;

memset(&serveraddr, 0,sizeof(struct sockaddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERV_PORT);
serveraddr.sin_addr.s_addr = inet_addr(ip);    

ret = connect(sfd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr));       //将sfd连接至指定的服务器网络地址 serveraddr
if(ret == -1)
{
perror("connect");
close(sfd);
return -1;
}

return sfd;
}


//用于信号处理,让服务器在按下Ctrl+c或Ctrl+\时不会退出
void signalhandler(void)
{
sigset_t sigSet;
sigemptyset(&sigSet);
sigaddset(&sigSet,SIGINT);
sigaddset(&sigSet,SIGQUIT);
sigprocmask(SIG_BLOCK,&sigSet,NULL);
}


//用于将密码数字转换为*
int mygetch( )
{
struct termios oldt,
newt;
int ch;
tcgetattr( STDIN_FILENO, &oldt );
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr( STDIN_FILENO, TCSANOW, &newt );
ch = getchar();
tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
return ch;
}


//获取当前时间 
int gettime()
{
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
printf ( "%s",asctime (timeinfo) );
}


//获取当前时间   指针 用于消息记录
char* nowtime()
{
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );

return asctime (timeinfo);

}



struct send
{
char name[20]; //名字
char toname[20]; //接收人
char account[20]; //账号
char passward[20]; //密码
int  likes; //点赞数
int  vip; //是否是会员
char moto[300]; //个性签名
int  cmd; //提取操作符
char msg[200]; //发送、接收消息
char file_name[20]; //文件名
char file[2048]; //发送文件存的数据
char question[50]; //密保问题
char answer[50]; //密保答案
char e_s; //确认发送的表情
char p_s; //确认发送的常用语
};


struct recv
{
char from_name[20]; //发信人
char to_name[20]; //名字
int  result; //返回操作结果
int  online_num; //在线人数
char num[20][20]; //在线人名
char msg[200]; //发送、接收消息
char file_name[20]; //文件名
char file[2048]; //发送文件存的数据
char question[50]; //密保问题
char answer[50]; //密保答案
char passward[20]; //密码
char moto[300]; //个性签名
int  likes; //点赞数
int  vip; //是否是会员
char e_s; //确认发送的表情
char p_s; //确认发送的常用语
};


typedef struct node
{
int  socket;
char name[20];
struct node* next;
}UMge;
typedef struct node* PUMge;


struct send userInfo;            //客户端用户信息结构体
struct recv userBack;            //服务器用户信息结构体
sqlite3 * db = NULL; //基础信息存放数据库
int i;
int ret;
PUMge head;                   //在线用户表


//保存用户
void save_user()
{

char *errmsg = NULL;
char auff[200] = {0}; 
char cuff[200] = {0};
sprintf(auff, "insert into save_user values('%s','%s','%s','%s',%d,%d)",userInfo.account, userInfo.passward, userInfo.name, userInfo.moto, userInfo.likes, userInfo.vip);

ret = sqlite3_exec(db, auff, NULL, NULL, &errmsg);
if(ret != SQLITE_OK)
{
printf("insert fail:%d(%s)\n", ret, errmsg);
userBack.result = existing_acc;   //账号已存在
printf("%s is insert error...\n",userInfo.name);
return;
}
printf("sqlite save_user insert success...\n");

sprintf(cuff,"insert into question values('%s','%s','%s','%s')", userInfo.account, userInfo.passward, userInfo.question, userInfo.answer);
ret = sqlite3_exec(db, cuff, NULL, NULL, &errmsg);
if(ret != SQLITE_OK)
{
printf("insert fail:%d(%s)\n", ret, errmsg);
return;
}

userBack.result = reg_success;                 //注册成功
}


//登录检查表和客户端发来的数据对比
void deal_log(int cfd)
{
char **resultp = NULL;          //用二重指针存储数据
int nrow;                       //查到满足条件的总行数
int ncolumn;                     //存储列
int ret;
int i;
char *errmsg = NULL;
char cuff[200];

sprintf(cuff, "select account,passward,name,moto,likes,vip from save_user where account = '%s' and passward = '%s'", userInfo.account, userInfo.passward);

ret = sqlite3_get_table(db, cuff, &resultp, &nrow, &ncolumn, &errmsg);
if (ret != SQLITE_OK)
{
printf ("log error : %d(%s)!\n", ret, errmsg);
return;
}

printf("%s is logging...\n", resultp[8]);


if(nrow == 1)
{
PUMge temp = head->next;
while(temp != head)
{
if(strcmp(temp->name, resultp[8]) == 0)
{
userBack.result = logged_acc;
printf("%s logged error because him has logged...\n", userInfo.name);
return;
}
temp = temp->next;
}

userBack.result = log_success;    //登录成功
strcpy(userBack.from_name, resultp[8]);
strcpy(userBack.moto, resultp[9]);

userBack.likes = *(resultp[10]) - 48;
userBack.vip = *(resultp[11]) - 48;

printf("%s logged success...\n", resultp[8]);

PUMge p = (PUMge)malloc(sizeof(UMge)/sizeof(char));
if(p == NULL)
{
perror("malloc");
return;
}
p->socket = cfd;
strcpy(p->name, resultp[8]);
printf("%s 's socket is %d...\n", p->name, p->socket);
p->next = head->next;
head->next = p;
}
else
{
userBack.result = error;          //账号密码错误
printf("%s 's passward is error...\n", resultp[8]);
}

ret = write(cfd, &userBack, sizeof(userBack));
if(ret == -1)
{
perror("write");
return;
}
}


//忘记密码
void deal_forget(int cfd)
{
char auff[100];
char **resultp = NULL;
int nrow;
int ncolumn;
int ret;
char *errmsg = NULL;

printf("i am select from question...\n");

sprintf(auff, "select passward, question, answer from question where account = '%s'", userInfo.account);
ret = sqlite3_get_table(db, auff,  &resultp, &nrow, &ncolumn, &errmsg);
if(ret != SQLITE_OK)
{
printf("select error fail:%d(%s)\n", ret, errmsg);
}

strcpy(userBack.passward, resultp[3]);
strcpy(userBack.question, resultp[4]);
strcpy(userBack.answer, resultp[5]);

write(cfd, &userBack, sizeof(userBack));

}


//处理私聊请求
int deal_pchar(int cfd)
{
int flag = 0;

PUMge temp = head->next; 
while(temp != head)
{
if(strcmp(temp->name, userInfo.toname) == 0 && temp->socket != cfd)
{
flag = 1;
strcpy(userBack.msg, userInfo.msg);
strcpy(userBack.from_name, userInfo.name);
userBack.result = private_chat;
printf("%s(%d) send %s(%d) ...\n", userInfo.name, cfd,  userInfo.toname, temp->socket);
write(temp->socket, &userBack, sizeof(userBack));

break;
}
temp = temp->next;
}

if(flag)
{
userBack.result = Send_success;
write(cfd,&userBack, sizeof(userBack));

printf("%s send a message to %s...\n", userInfo.name, userInfo.toname);
}
else
{
userBack.result = Send_error;
write(cfd,&userBack, sizeof(userBack));

printf("%s send error...\n", userInfo.name);
}
}


//处理群聊请求
int deal_groupchat(int cfd)
{
int flag = 0;

PUMge temp = head->next;
while(temp != head)
{
if(temp->socket != cfd)
{
flag = 1;
strcpy(userBack.from_name, userInfo.name);
strcpy(userBack.msg, userInfo.msg);
userBack.result = group_chat;

write(temp->socket, &userBack, sizeof(userBack));
}
temp = temp->next;
}
printf("%s send a msg to everyone...\n",userInfo.name);

if(flag)
{
userBack.result = Send_success;
write(cfd,&userBack, sizeof(userBack));
}
else
{
userBack.result = Send_error;
write(cfd,&userBack, sizeof(userBack));

printf("%s send grep error...\n", userInfo.name);
}
}


//处理查看当前人数
int deal_member(int cfd)
{
userBack.online_num = 0;
int i = 0;
PUMge temp = head->next;
while(temp != head)
{
userBack.online_num++;
strcpy(userBack.num[i], temp->name);
i++;
temp = temp->next;
}
userBack.result = online_member;

write(cfd,&userBack,sizeof(userBack));

printf("show online_member success...\n");
}


//文件传输
int deal_file_transfer(int cfd)
{
PUMge temp = head->next;
int flag = 0;
while(temp != head)
{
if(strcmp(temp->name, userInfo.toname) == 0 && temp->socket != cfd)
{
flag = 1;
userBack.result = file_transfer;
strcpy(userBack.from_name, userInfo.name);
strcpy(userBack.file, userInfo.file);
strcpy(userBack.file_name, userInfo.file_name);

write(temp->socket, &userBack, sizeof(userBack));

break;
}

temp = temp->next;
}

if(flag)
{
userBack.result = Send_success;
ret = write(cfd,&userBack, sizeof(userBack));
if(ret == -1)
{
perror("write");
return -1;
}
printf("%s send a file to %s...\n", userInfo.name, userInfo.toname);
}
else
{
userBack.result = Send_error;
write(cfd,&userBack, sizeof(userBack));

printf("%s send file error...\n", userInfo.name);
}
}


//发送表情
int deal_expression(int cfd)
{
PUMge temp = head->next;
int flag = 0;
while(temp != head)
{
if(strcmp(temp->name, userInfo.toname) == 0 && temp->socket != cfd)
{
flag = 1;
userBack.result = expression;
strcpy(userBack.from_name, userInfo.name);
userBack.e_s = userInfo.e_s;

write(temp->socket, &userBack, sizeof(userBack));

break;
}

temp = temp->next;
}

if(flag)
{
userBack.result = Send_success;
write(cfd,&userBack, sizeof(userBack));

printf("%s send a expression to %s...\n", userInfo.name, userInfo.toname);
}
else
{
userBack.result = Send_error;
write(cfd,&userBack, sizeof(userBack));

printf("%s send expression error...\n", userInfo.name);
}
}


//发送常用语
int deal_phrases(int cfd)
{
PUMge temp = head->next;
int flag = 0;

while(temp != head)
{
if(strcmp(temp->name, userInfo.toname) == 0 && temp->socket != cfd)
{
flag = 1;
userBack.result = phrases;
strcpy(userBack.from_name, userInfo.name);
userBack.p_s = userInfo.p_s;

write(temp->socket, &userBack, sizeof(userBack));

break;
}

temp = temp->next;
}

if(flag)
{
userBack.result = Send_success;
write(cfd,&userBack, sizeof(userBack));

printf("%s send a phrases to %s...\n", userInfo.name, userInfo.toname);
}
else
{
userBack.result = Send_error;
write(cfd,&userBack, sizeof(userBack));

printf("%s send phrases error...\n", userInfo.name);
}
}


//处理点赞请求
int deal_like(int cfd)
{
int ret;
int i;
char *errmsg = NULL;
char **resultp = NULL;
int nrow;
int ncolumn;
char cuff[200];

sprintf(cuff, "select likes from save_user where name = '%s'", userInfo.toname);

ret = sqlite3_get_table(db, cuff, &resultp, &nrow, &ncolumn, &errmsg);
if(ret != SQLITE_OK)
{
printf("select fail:%d(%s)\n", ret, errmsg);
return -1;
}

if(nrow == 1)
{
sprintf(cuff, "update save_user set likes = %d where name = '%s'",*(resultp[1]) - 47, userInfo.toname);
ret = sqlite3_exec(db, cuff, NULL, NULL, &errmsg);
if(ret != SQLITE_OK)
{
printf("update fail:%d(%s)\n", ret, errmsg);
return -1;
}

PUMge temp = head->next;

while(temp != head)
{
if(strcmp(temp->name, userInfo.toname) == 0)
{
userBack.likes = *(resultp[1]) - 47;
userBack.result = like;
strcpy(userBack.from_name,userInfo.name);
write(temp->socket, &userBack, sizeof(userBack));

break;
}
temp = temp->next;
}
userBack.result = like_success;
write(cfd, &userBack, sizeof(userBack));

}
else
{
userBack.result = Send_error;
write(cfd, &userBack, sizeof(userBack));
}
}


//处理更改个签
int deal_motochange(int cfd)
{
char xcf[200];
char *errmsg = NULL;


sprintf(xcf, "update save_user set moto = '%s' where name = '%s'",userInfo.moto,userInfo.name);

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值