网络编程项目要求
一、 实现目标
一个在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 大侠,欢迎你来到咱
们的聊天室”
6 文件传输
二、 考核内容
网络编程:
设计Client和Server的通讯协议,并实现Server向登陆客户的消息发送
文件I/O编程:
设计聊天记录的文件格式
设计注册用户和密码及配置参数的”数据库”文件
多线程或进程编程:
Server端需要至少创建2个线程或进程,一个用于监听Client端的连接请求,
一个用于给登陆的Client用户列表发送某个client的消息内容.
项目文档的编写:
系统概要设计文档
系统详细设计文档
用户使用手册
本程序通过多进程,非阻塞socket采用轮询方式实现。
package.h
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/wait.h>
#include <sqlite3.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <time.h>
typedef struct mesg
{
char name[15];
char word[50];
int fd;
}mesg;
typedef struct usrmesg
{
char name[10];
char pwd[10];
int flag;
}usrmesg;
typedef struct usrdata
{
char name[10];
int num;
}usrdata;
extern int Socket();
extern void Bind( int, struct sockaddr_in, int);
extern void Listen( int, int);
extern int Accept( int, struct sockaddr *, socklen_t *);
extern void Connect( int, struct sockaddr_in, int);
extern pid_t Fork();
extern int Read( int, void *, size_t);
extern void clientlogin( int sockfd, usrmesg *usr);
extern void usrfunction( int sockfd, usrmesg usr);
extern void rootfunction( int sockfd);
extern int Shmget();
extern sqlite3 * initdb();
extern void login( int , sqlite3 *);
extern void searchfunction( int, mesg *, sqlite3 *);
extern void transit( mesg *, sqlite3 *);
package.c
#include"../../include/package.h"
typedef struct sockaddr SA;
int Socket()
{
int listenfd;
if( ( listenfd = socket( PF_INET, SOCK_STREAM, 0 ) ) < 0)
{
fprintf( stderr, "fail to socket :%s\n", strerror(errno) );
exit(-1);
}
return listenfd;
}
void Bind( int sockfd, struct sockaddr_in my_addr, int addrlen )
{
if ( bind( sockfd, (SA *)&my_addr, addrlen ) < 0)
{
perror("fail to bind");
exit(-1);
}
}
void Listen( int listenfd,int backlog)
{
if( listen( listenfd, backlog) == -1 )
{
perror("fail to listen");
exit(-1);
}
}
int Accept( int listenfd, struct sockaddr *addr, socklen_t *addrlen)
{
if( ( listenfd = accept( listenfd, NULL, NULL) ) == -1)
{
perror("fail to accept");
exit(-1);
}
return listenfd;
}
void Connect( int sockfd, struct sockaddr_in my_addr, int addrlen )
{
if ( connect( sockfd, (SA *)&my_addr, addrlen ) < 0)
{
perror("fail to connect to server");
exit(-1);
}
}
pid_t Fork()
{
pid_t pid;
if( ( pid = fork() ) < 0 )
{
perror("fail to create process");
exit(-1);
}
return pid;
}
int Shmget ()
{
int shmid;
if( ( shmid = shmget( IPC_PRIVATE, sizeof(mesg), 0777 ) ) < 0 )
{
perror("fail to shmget");
exit(-1);
}
return shmid;
}
int Read( int fd, void * ptr, size_t size)
{
int n = 0;
if( ( n = read( fd, ptr, size) ) < 0)
{
printf("fail to read\n");
exit(-1);
}
return n;
}
client.c
#include"../../include/package.h"
usrmesg usr;
int main(int argc, char *argv[])
{
int sockfd;
int ret;
if( argc < 2 ) //出错判断
{
printf("请输入IP地址!\n");
exit(0);
}
struct sockaddr_in client_addr;
sockfd = Socket();
memset(&client_addr, 0, sizeof(client_addr));
client_addr.sin_family = PF_INET;
client_addr.sin_port = htons(8888); //链接服务器
client_addr.sin_addr.s_addr = inet_addr(argv[1]);
Connect( sockfd, client_addr, sizeof(client_addr));
usr.flag = 0;
system("clear");
printf("\n\n\n\n\n\n\n\n\n\n\t\t\t欢迎使用易水萧风聊天室\n\n\n\n\n\n\n\n\n\n\n\n");
sleep(2);
clientlogin(sockfd,&usr);
system("clear");
if( strcmp( usr.name,"root") == 0) //用户甄别
{
goto management;
}
usrfunction( sockfd,usr); //用户功能选择
management:
rootfunction( sockfd); //管理员功能选择
return 0;
}
clientlogin.c
#include"../../include/package.h" //客户端登录处理函数
void clientlogin( int sockfd, usrmesg *usr)
{
int chose; //flag识别服务器返回消息类型
enum flag { success = 3, wrong = -3, reregistration = -4, resuccess = 4
, dberror = -2, relogin = -5 , login = 1, registration = 2};
while(1)
{
system("clear");
if(usr->flag == success)
{
printf("登录成功\n");
sleep(1);
break;
}
if( usr->flag == wrong)
{
printf("用户名或密码错误,请重新登录\n");
sleep(1);
}
if( usr->flag == reregistration)
{
printf("该用户名已被注册\n");
sleep(1);
}
if( usr->flag == resuccess)
{
printf("注册成功,你现在可以用该帐号登录了\n");
sleep(1);
}
if( usr->flag == dberror)
{
printf("数据库故障,注册失败\n");
sleep(1);
}
if( usr->flag == relogin)
{
printf("该用户已登录,请不要重复登录\n");
sleep(1);
}
printf("\n\n\n\n\n\n\n\t\t\t\t聊天室登录 \n\n");
printf(" \t\t1.登录 \n\n");
printf(" \t\t2.注册 \n\n");
printf(" \t\t3.退出 \n\n");
printf("\n\n\n\n\n\n请选择:");
scanf("%d",&chose);
setbuf( stdin, 0);
switch(chose) //登录输入的出错处理没做。。。。
{
case 1:
{
system("clear");
printf("****************登录********************\n");
printf("用户名(不含空格):");
scanf("%s",usr->name);
setbuf(stdin,0);
printf("密码(不含空格):");
scanf("%s",usr->pwd);
setbuf(stdin,0);
usr->flag = login;
write( sockfd, usr, sizeof(usrmesg) );
Read( sockfd, usr, sizeof(usrmesg) );
break;
}
case 2:
{
system("clear");
printf("*****************注册********************\n");
printf("请输入字母数字开头(不含空格)10位内用户名:");
scanf("%s", usr->name);
setbuf(stdin,0);
printf("请输入10位密码:");
scanf("%s", usr->pwd);
setbuf(stdin,0);
usr->flag = registration;
write( sockfd, usr, sizeof(usrmesg) );
Read( sockfd, usr, sizeof(usrmesg) );
break;
}
case 3:
{
printf("欢迎使用!\n");
exit(0);
}
default:
{
printf("输入有误,请重新输入\n");
break;
}
}
}
}
urrfunction.c
#include"../../include/package.h" //用户功能选择函数
void usrfunction( int sockfd, usrmesg usr)
{
int choose;
mesg out;
mesg get;
time_t tm;
usrdata data[10];
pid_t pid = Fork();
enum flag { all = -1, file = -5, busy = -6, quit = -2, kicking = -4
,useless = -3 };
if( pid > 0)
{
while(1)
{
printf("请选择: 1 群聊 2 私聊 3 传送 4 退出\n");
scanf("%d",&choose);
switch( choose )
{
case 1:
{
strcpy( out.name,"all"); //群发将发送给所有人
printf("请输入:");
setbuf( stdin,0);
fgets( out.word, sizeof(out.word),stdin);
out.fd = 0;
write( sockfd, &out,sizeof(out));
break;
}
case 2:
{
int i;
int choose;
strcpy( out.word, "getusrmesg"); //请求在线用户
write( sockfd, &out,sizeof(out));
sleep(1);
Read(sockfd, data, sizeof(data));
for( i = 0; i <= data[0].num ;i++)
{
printf("%d.%s ",i+1,data[i].name);
}
printf("\n");
printf("请选择私聊对象:");
scanf("%d",&choose);
if( strcmp ( data[choose-1].name, "root") == 0 )
{
printf("对不起你不能给管理员发消息\n");
sleep(1);
break; //简单出错判断
}
strcpy( out.name, data[choose-1].name);
out.fd = 0;
printf("你想对他说:");
setbuf(stdin,0);
fgets( out.word,sizeof(out.word), stdin);
write( sockfd, &out,sizeof(out));
break;
}
case 3:
{
int i;
int choose;
strcpy( out.word, "getusrmesg"); //请求在线用户
write( sockfd, &out,sizeof(out));
sleep(1);
read(sockfd, data, sizeof(data));
for( i = 0; i <= data[0].num ;i++)
{
printf("%d.%s ",i+1,data[i].name);
}
printf("\n");
printf("你想把你的文件传给谁:( 0 取消):\n");
scanf("%d",&choose);
if( strcmp ( data[choose-1].name, "root") == 0 )
{
printf("对不起你没有权限传给管理员东西\n");
sleep(1);
break; //简单判断
}
if( strcmp ( data[choose-1].name,usr.name) == 0 )
{
printf("不是吧,自己的东西还要传。。。\n");
sleep(1);
break;
}
if( choose == 0)
{
break;
}
strcpy( out.name, data[choose-1].name);
strcpy( out.word, "transport");
write( sockfd, &out, sizeof(out));
sleep(1);
read( sockfd, &out, sizeof(out));
if( strcmp( out.word, "wait") == 0 )
{
printf("服务器繁忙,请稍后再试...\n");
break;
}
int fd =open("send",O_RDWR|O_CREAT|O_APPEND,0666);
if( fd < 0)
{
printf("打开测试文件失败\n");
break;
}
lseek( fd, 0, SEEK_SET); // 坑爹的3小时,注意!!!
memset( out.word, 0, sizeof(out.word));
int n = 0;
sleep(1);
while( ( n = read(fd,out.word,sizeof(out.word))>0))
{
write( sockfd,out.word,sizeof(out.word));
memset( out.word, 0, sizeof(out.word));
}
strcpy(out.word,"finish");
write( sockfd,out.word,sizeof(out.word));
sleep(3);
printf("发送完成\n");
close(fd); //“finish”解除服务器
break; //读阻塞
}
case 4:
{
strcpy( out.name,"sever");
strcpy( out.word,"quit");
out.fd = 0;
write( sockfd, &out,sizeof(out));
printf("欢迎使用\n");
sleep(1); //等待子进程退出
exit(0);
}
default:
{
printf("输入有误\n");
break;
}
}
}
}
if( pid == 0)
{
while(1)
{
Read( sockfd, &get,sizeof(get));
if( get.fd == kicking)
{
printf("你被管理员踢下线了..........\n");
printf("请猛按4退出或强制退出............\n");
printf("你什么都干不了了,认命吧騒年\n");
close(sockfd);
exit(0);
}
if(get.fd == file) //传送文件
{
int n;
printf("%s给你发送了个文件\n",get.word);
int fd = open( "recv",O_RDWR|O_CREAT|O_APPEND,0666);
if( fd < 0)
{
printf("fail to open the file\n");
exit(-1);
}
memset( get.word,0,sizeof(get.word));
while( ( n = Read( sockfd,get.word,sizeof(get.word) ) ) > 0 )
{
if( strcmp( get.word,"finish") == 0)
{
break;
}
write( fd,get.word,sizeof(get.word));
memset( get.word, 0, sizeof(get.word));
}
printf("接收完成\n");
close(fd);
}
if( get.fd == busy )
{
printf("服务器繁忙,请稍后再试...\n");
}
if( get.fd == useless )
{
sleep(1); //为了防止和主进程,抢夺在线用户列表而定义的消息
}
if( get.fd == all)
{
tm = time(NULL);
printf("%s",ctime(&tm));
printf("群消息:%s说:%s",get.name,get.word);
}
if( get.fd > 0 )
{
tm = time(NULL);
printf("%s",ctime(&tm));
printf("私聊:%s对你说:%s",get.name,get.word);
}
if( get.fd == quit )
{
exit(0); //主进程叫你退出啦
}
}
}
}
rootfunction.c
#include "../../include/package.h" //管理员功能选择函数
//管理员不具有聊天功能
void rootfunction( int sockfd)
{
mesg out;
mesg get;
usrdata data[10];
while(1)
{
int choose;
system("clear");
printf("请选择功能:1.踢人 2.禁言 3退出\n");
scanf("%d",&choose);
switch(choose)
{
case 1:
{
int i;
strcpy( out.word, "getusrmesg"); //请求在线用户
write( sockfd, &out,sizeof(out));
Read(sockfd, &get, sizeof(get));
Read(sockfd, data, sizeof(data));
for( i = 0; i <= data[0].num ;i++)
{
printf("%d.%s ",i+1,data[i].name); //显示
}
printf("\n");
printf("你要踢除谁:(0 放弃):");
setbuf(stdin,0);
scanf("%d",&choose);
if( choose != 0) //简单判断
{
if( strcmp (data[choose-1].name, "root") == 0)
{
system("clear");
printf("你不能剔除自己哦。。亲\n");
sleep(1);
}
else
{
strcpy( out.name, data[choose-1].name);
strcpy( out.word,"delete"); //发送请求
write(sockfd, &out,sizeof(out));
}
}
break;
}
case 2:
{
int i;
strcpy( out.word, "getusrmesg"); //请求在线用户
write( sockfd, &out,sizeof(out));
Read(sockfd, &get, sizeof(get));
Read(sockfd, data, sizeof(data));
for( i = 0; i <= data[0].num ;i++)
{
printf("%d.%s ",i+1,data[i].name);
}
printf("\n");
printf("你要禁言谁:(0 放弃):");
setbuf(stdin,0);
scanf("%d",&choose);
if( choose != 0)
{
strcpy( out.name, data[choose-1].name);
strcpy( out.word,"Gay");
write(sockfd, &out,sizeof(out));
}
break;
}
case 3:
{
strcpy( out.word, "quit");
write( sockfd, &out, sizeof(out));
printf("\n\n\n\n\n\n\n\n\n\n\t\t\t\t感谢您的管理\n\n\n\n\n\n\n\n\n\n\n");
sleep(1);
system("clear");
exit(0);
}
default:
{
printf("输入有误,请重新输入\n");
break;
}
}
}
}
服务器
server.c
/*********************************************
Author: zxf Date:2012.07 Version: 1.0
Description: 服务器的main函数
Functionlist:
1 initdb() 初始化数据库
2 seachfunction() 客户端功能选择函数
3 transit() 功能选择转发函数
**********************************************/
#include"../../include/package.h"
mesg get; //消息结构体
time_t tm;
char *sql;
char *zErrMsg; // 存放数据库错误信息
usrdata data[10]; //存放在线用户姓名
int main()
{
int listenfd, connfd;
int column,row;
int i;
int opt = 1;
int shmid = Shmget(); //定义共享内存,让主进程来转发子进程消息
mesg *shmaddr;
sqlite3 *db = initdb();
shmaddr = (mesg *)shmat( shmid,NULL,0);
struct sockaddr_in server_addr;
listenfd = Socket();
int flags = fcntl( listenfd, F_GETFL,0); //非阻塞
fcntl ( listenfd,F_SETFL,flags | O_NONBLOCK);
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = PF_INET;
server_addr.sin_port = htons(8888);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
setsockopt( listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
Bind( listenfd, server_addr, sizeof(server_addr) );
Listen( listenfd, 10 );
while(1)
{
int ret;
connfd = -1;
while(connfd < 0) //轮询
{
connfd = accept( listenfd, NULL, NULL);
transit( shmaddr,db); //主进程转发子进程消息
}
printf("有用户请求,正在连接数据库\n");
pid_t pid = Fork();
if( pid == 0)
{
int configure = 1;
while(1)
{
while( configure)
{
login( connfd, db); //登录
configure = 0;
}
searchfunction( connfd,shmaddr,db); //监听客户端请求
}
}
}
return 0;
}
severlogin.c
#include "../../include/package.h" //客户端登录请求,响应函数,处理完后
//返回给客户端一个消息类型
void login( int connfd ,sqlite3 *db)
{
int configure = 1;
usrmesg usr;
int column, row, i, j;
char *sql;
char **result;
char *zErrMsg;
enum signal { login = 1, registration = 2, relogin = -5, success = 3,
wrong = -3, reregistration = -4, dberror = -2, resuccess
= 4};
while(configure)
{
int ret = Read( connfd, &usr, sizeof(usr));
if(ret == 0)
{
printf("客户端已退出\n");
exit(0);
}
if( usr.flag == login )
{
printf("正在验证用户名和密码\n");
sql = sqlite3_mprintf("select *from online where ID = '%s';",usr.name);
sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
if( row != 0)
{
printf("该用户已登录,请不要重复登录\n");
usr.flag = relogin;
write( connfd, &usr, sizeof(usr) );
}
else
{
sql = "select *from usrdata;";
sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
for(i = column; i < ( (row+1)*column ); i = i+column )
{
if ( strcmp( usr.name, result[i] ) == 0 )
{
if( strcmp( usr.pwd,result[i+1] ) == 0)
{
usr.flag = success;
configure = 0;
sql = sqlite3_mprintf("insert into online values('%s',%d,0);",usr.name,connfd);
sqlite3_exec( db, sql, NULL, NULL,&zErrMsg);
printf("验证成功\n");
write( connfd, &usr, sizeof(usr) );
}
}
}
}
if( usr.flag == login )
{
printf("验证失败\n");
usr.flag = wrong;
write( connfd, &usr, sizeof(usr) );
}
}
else
{
printf("正在注册用户\n");
sql = "select *from usrdata;";
sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
for(i = column; i < ( (row+1)*column ); i = i+column )
{
if ( strcmp( usr.name, result[i] ) == 0 )
{
usr.flag = reregistration;
write(connfd, &usr, sizeof(usr));
printf("注册失败\n");
break;
}
}
if( usr.flag == registration)
{
sql = sqlite3_mprintf("insert into usrdata values('%s','%s');",usr.name,usr.pwd);
int result = sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
if(result)
{
usr.flag = dberror;
printf("数据库故障注册失败\n");
printf(" 插入失败,原因:%s\n",zErrMsg);
}
else
{
usr.flag = resuccess;
printf("注册成功\n");
}
write(connfd,&usr,sizeof(usr));
}
}
}
}
initlib.c
#include "../../include/package.h" //打开数据库,创建密码表,在线用户表 //聊天记录表
sqlite3 * initdb()
{
int rc;
sqlite3 *db = NULL;
char *zErrMsg = NULL;
rc = sqlite3_open("usr.db",&db);
if(rc)
{
fprintf(stderr,"Can't open database:%s",sqlite3_errmsg(db));
sqlite3_close(db);
}
char *sql = " create table usrdata( ID text PRIMARY KEY,PWD text ); " ;
sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
sql = " create table online( ID text PRIMARY KEY,fd INTEGER , gay INTEGER); " ;
sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
sql = " create table chatrecord( time text,send text , get text, word text ); " ;
sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
sql = "insert into usrdata values('root','123456');";
sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
return db;
}
seachfunction.c
#include "../../include/package.h" //检索客户端的请求类型
void searchfunction( int connfd, mesg *shmaddr, sqlite3 *db)
{
int column,row;
char *sql = NULL;
char *zErrMsg = NULL;
time_t tm;
int i;
int ret;
mesg get;
usrdata data[10];
while(1)
{
start:
ret = Read( connfd, &get, sizeof(get) );
if( ret == 0) //判断客户端是否强制退出
{
char **result;
sql = sqlite3_mprintf( "select ID from online where fd = %d;",connfd);
sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
printf("%s强制退出了客户端\n",result[1]);
sql = sqlite3_mprintf( "delete from online where fd = %d;",connfd);
sqlite3_exec(db,sql,NULL,NULL,&zErrMsg);
exit(-1);
}
if( strncmp( get.word, "quit", 4) == 0) //客户端主动退出
{
char **result;
sql = sqlite3_mprintf( "select ID from online where fd = %d;",connfd);
sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
printf("%s正常退出了客户端\n",result[1]);
get.fd = -2;
write( connfd,&get, sizeof(get));
sql = sqlite3_mprintf( "delete from online where fd = %d;",connfd);
sqlite3_exec(db,sql,NULL,NULL,&zErrMsg);
exit(0);
}
if( strcmp( get.word,"getusrmesg") == 0)//客户端请求在线用户列表
{
get.fd = -3;
write(connfd,&get,sizeof(get));
char **result;
int j = 0;
sql = "select ID from online;";
sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
for( i = column; i < (row+1)*column; i = i+column)
{
strcpy( data[j].name, result[i]);
j++;
}
data[0].num = j-1;
write( connfd, data,sizeof(data) );
continue;
}
if( strcmp ( get.word,"transport") == 0) //客户端传送文件
{
get.fd = -4;
char **result;
int n = 0;
int i = 0;
sql = "select * from online;";
sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
for( i = column+2; i < (row+1)*column ; i = i+column )
{
if( atoi (result[i]) == 2)
{
get.fd = -3;
write( connfd, &get, sizeof(get) );
strcpy( get.word, "wait");
write( connfd, &get, sizeof(get) );
goto start;
}
}
get.fd = -3;
write( connfd, &get, sizeof(get) );
write( connfd, &get, sizeof(get) );
get.fd = -4;
sql = sqlite3_mprintf("select ID from online where fd = %d;",connfd);
sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
strcpy( get.word, result[1]);
memcpy(shmaddr,&get,sizeof(get)); //通知接收端建立文件
memset( get.word, 0, sizeof(get.word));
printf("通知接收完成\n");
while((n = Read(connfd, get.word,sizeof(get.word)))>0)
{
get.fd = -5;
memcpy(shmaddr,&get,sizeof(get));
sleep(1); //拷贝太快了,保护共享内存中的内容
if( strcmp( get.word,"finish") == 0)
{
break;
}
memset( get.word, 0, sizeof(get.word));
}
printf("拷贝完成\n");
sql = sqlite3_mprintf("update online set gay = 0 where ID = '%s';",get.name);
sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
continue;
}
if( strcmp( get.word,"delete") == 0) //强制用户下线
{
get.fd = -2;
sql = sqlite3_mprintf("update online set gay = 1 where ID = '%s';",get.name);
sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
memcpy(shmaddr,&get,sizeof(get));
continue;
}
if( strcmp( get.word,"Gay") == 0) //禁言
{
sql = sqlite3_mprintf("update online set gay = 1 where ID = '%s';",get.name);
sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
continue;
}
char **result; //把在线用户表中,相应位置1
int j;
char *temp = (char *)malloc(sizeof(get.name));
strcpy( temp, get.name);
sql = "select * from online;";
sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
for( i = column; i < (row+1)*column ; i = i+column )
{
if( strcmp( get.name, result[i]) == 0 )
{
if( atoi (result[i+2]) == 2)
{
get.fd = -6;
write( connfd, &get, sizeof(get));
break;
}
for( j=column+1; j< (row+1)*column; j = j+column)
{
if( atoi( result[j] ) == connfd)
{
if( atoi( result[j+1]) == 1)
{
get.fd = -3; //私聊,并把聊天记录写在表中
}
strcpy( get.name, result[j-1]);
break;
}
}
tm = time(NULL);
sql = sqlite3_mprintf("insert into chatrecord values('%s','%s','%s','%s');",ctime(&tm),get.name,temp,get.word);
sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
if( get.fd != -3)
{
get.fd = atoi(result[i+1]);
}
memcpy(shmaddr,&get,sizeof(get));
free( temp );
temp = NULL;
break;
}
}
int flag = 1;
if( strcmp ( get.name, "all") == 0) //群聊
{
for( j = column+2; j < (row+1)*column; j = j+column )
{
if( atoi( result[j]) == 2 ) //有用户传输文件
{
get.fd = -6;
write( connfd, &get, sizeof(get)); //通知等待
flag = 0;
break;
}
}
if( flag == 0)
{
continue;
}
for( j = column+1; j < (row+1)*column ; j = j+column )
{
if( atoi( result[j] ) == connfd)
{
if( atoi(result[j+1]) == 1)
{
get.fd = -3;
}
strcpy(get.name,result[j-1]);
break;
}
}
tm = time(NULL);
sql = sqlite3_mprintf("insert into chatrecord values('%s','%s','%s','%s');",ctime(&tm),get.name,temp,get.word);
sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
if(get.fd != -3)
{
get.fd = -1;
}
memcpy(shmaddr,&get,sizeof(get));
}
}
}
transit.c
#include "../../include/package.h" //中转客户端消息
void transit( mesg *shmaddr, sqlite3 *db)
{
int row;
int column;
char *sql;
int i;
char *zErrMsg;
enum choosefunction { all = -1, kicking = -2, gay = -3, file = -4,
transport = -5 };
if( shmaddr->fd == all) //群发
{
char **result;
sql = "select * from online;";
sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
if( strncmp (shmaddr->word,"smile",5) == 0)
{
strcpy( shmaddr->word, "向大家发送了笑脸\n");
}
for( i = column+1; i < (row+1)*column; i = i+column)
{
if( strcmp( result[i-1],"root") != 0)
{
write(atoi(result[i]),shmaddr,sizeof(mesg));
}
}
shmaddr->fd = 0;
}
if( shmaddr->fd == kicking ) //踢人
{
char **result;
sql = "select * from online;";
sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
for( i = column; i < (row+1)*column; i = i+column)
{
if( strcmp( shmaddr->name,result[i] ) == 0)
{
shmaddr->fd = -4;
write(atoi(result[i+1]),shmaddr,sizeof(mesg));
break;
}
}
shmaddr->fd = 0;
}
if( shmaddr->fd == gay ) //禁言
{
printf("该用户被禁言...................\n");
shmaddr->fd = 0;
}
if( shmaddr->fd == file ) //通知客户端,接受文件
{
char **result;
int temp;
sql = sqlite3_mprintf( "select fd from online where ID = '%s';",shmaddr->name);
sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
sql = sqlite3_mprintf( "update online set gay = 2 where ID ='%s';",shmaddr->name);
sqlite3_exec( db, sql, NULL, NULL, &zErrMsg);
shmaddr->fd = -5;
write( atoi( result[1]),shmaddr,sizeof(mesg));
shmaddr->fd = 0;
}
if( shmaddr->fd == transport ) //客户1->服务器->客户2 读一次写一次
{
char **result;
sql = sqlite3_mprintf( "select fd from online where ID = '%s';",shmaddr->name);
sqlite3_get_table(db,sql,&result,&row,&column,&zErrMsg);
write(atoi( result[1]),shmaddr->word,sizeof(shmaddr->word));
shmaddr->fd = 0;
}
if( shmaddr->fd != 0) //私聊
{
write(shmaddr->fd,shmaddr,sizeof(mesg));
shmaddr->fd = 0;
}
}
尚未解决的问题:
客户端在接收文件时,接收文件的客户端发送消息会出错
原因:服务器返回的消息被客户端接收文件的read所接收
后面做了所有的出错处理和注释,概要设计什么的,发上来太麻烦了,就不发了啊